Skip to content

Commit b510246

Browse files
committed
Merge branch 'issue-169' into devel
2 parents c019f39 + fda16c1 commit b510246

File tree

10 files changed

+191
-21
lines changed

10 files changed

+191
-21
lines changed

docs/linux-specific-properties.md

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,23 @@
22

33
```xml
44
<linuxConfig>
5-
<pngFile>path/to/icon.png</pngFile>
6-
<generateDeb>true|false</generateDeb>
7-
<generateRpm>true|false</generateRpm>
5+
<pngFile>path/to/icon.png</pngFile>
6+
<generateAppImage>true|false</generateAppImage>
7+
<generateDeb>true|false</generateDeb>
8+
<generateRpm>true|false</generateRpm>
89
<wrapJar>true|false</wrapJar>
10+
<categories>
11+
<category>Utility</category>
12+
...
13+
</categories>
914
</linuxConfig>
1015
```
1116

12-
13-
14-
| Property | Mandatory | Default value | Description |
15-
| ------------- | --------- | ------------- | ----------------------------------------------- |
16-
| `pngFile` | :x: | `null` | Icon file. |
17-
| `generateDeb` | :x: | `true` | DEB package will be generated. |
18-
| `generateRpm` | :x: | `true` | RPM package will be generated. |
19-
| `wrapJar` | :x: | `true` | Wraps JAR file inside the executable if `true`. |
17+
| Property | Mandatory | Default value | Description |
18+
| ------------------ | --------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------- |
19+
| `pngFile` | :x: | `null` | Icon file. |
20+
| `generateAppImage` | :x: | `true` | [AppImage](https://appimage.org/) package will be generated. |
21+
| `generateDeb` | :x: | `true` | DEB package will be generated. |
22+
| `generateRpm` | :x: | `true` | RPM package will be generated. |
23+
| `wrapJar` | :x: | `true` | Wraps JAR file inside the executable if `true`. |
24+
| `categories` | :x: | `[ "Utility"]` | [Main categories](https://specifications.freedesktop.org/menu-spec/latest/apa.html) in the application's desktop entry file. |

src/main/java/io/github/fvarrui/javapackager/model/LinuxConfig.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.io.File;
44
import java.io.Serializable;
5+
import java.util.Arrays;
6+
import java.util.List;
57

68
import io.github.fvarrui.javapackager.packagers.Packager;
79

@@ -10,11 +12,21 @@
1012
*/
1113
public class LinuxConfig implements Serializable {
1214
private static final long serialVersionUID = -1238166997019141904L;
13-
15+
16+
private List<String> categories;
1417
private boolean generateDeb = true;
1518
private boolean generateRpm = true;
19+
private boolean generateAppImage = true;
1620
private File pngFile;
1721
private boolean wrapJar = true;
22+
23+
public void setCategories(List<String> categories) {
24+
this.categories = categories;
25+
}
26+
27+
public List<String> getCategories() {
28+
return categories;
29+
}
1830

1931
public boolean isGenerateDeb() {
2032
return generateDeb;
@@ -32,26 +44,34 @@ public void setGenerateRpm(boolean generateRpm) {
3244
this.generateRpm = generateRpm;
3345
}
3446

47+
public boolean isGenerateAppImage() {
48+
return generateAppImage;
49+
}
50+
51+
public void setGenerateAppImage(boolean generateAppImage) {
52+
this.generateAppImage = generateAppImage;
53+
}
54+
3555
public File getPngFile() {
36-
return pngFile;
56+
return pngFile;
3757
}
3858

3959
public void setPngFile(File pngFile) {
4060
this.pngFile = pngFile;
4161
}
42-
62+
4363
public boolean isWrapJar() {
4464
return wrapJar;
4565
}
46-
66+
4767
public void setWrapJar(boolean wrapJar) {
4868
this.wrapJar = wrapJar;
4969
}
5070

5171
@Override
5272
public String toString() {
53-
return "LinuxConfig [generateDeb=" + generateDeb + ", generateRpm=" + generateRpm + ", pngFile=" + pngFile
54-
+ ", wrapJar=" + wrapJar + "]";
73+
return "LinuxConfig [categories=" + categories + ", generateDeb=" + generateDeb + ", generateRpm=" + generateRpm
74+
+ ", generateAppImage=" + generateAppImage + ", pngFile=" + pngFile + ", wrapJar=" + wrapJar + "]";
5575
}
5676

5777
/**
@@ -60,7 +80,7 @@ public String toString() {
6080
* @param packager Packager
6181
*/
6282
public void setDefaults(Packager packager) {
63-
// nothing
83+
this.setCategories((categories == null || categories.isEmpty()) ? Arrays.asList("Utility") : categories);
6484
}
6585

6686
}

src/main/java/io/github/fvarrui/javapackager/packagers/Context.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public Context() {
2222
// building tool independent generators
2323
getInstallerGenerators(Platform.linux).add(new GenerateDeb());
2424
getInstallerGenerators(Platform.linux).add(new GenerateRpm());
25+
getInstallerGenerators(Platform.linux).add(new GenerateAppImage());
2526
getInstallerGenerators(Platform.mac).add(new GenerateDmg());
2627
getInstallerGenerators(Platform.mac).add(new GeneratePkg());
2728
getInstallerGenerators(Platform.windows).add(new GenerateSetup());
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package io.github.fvarrui.javapackager.packagers;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
6+
import org.apache.commons.lang3.SystemUtils;
7+
8+
import io.github.fvarrui.javapackager.model.Platform;
9+
import io.github.fvarrui.javapackager.utils.CommandUtils;
10+
import io.github.fvarrui.javapackager.utils.FileUtils;
11+
import io.github.fvarrui.javapackager.utils.Logger;
12+
import io.github.fvarrui.javapackager.utils.VelocityUtils;
13+
14+
public class GenerateAppImage extends ArtifactGenerator<LinuxPackager> {
15+
16+
private static final int IMAGETOOL_VERSION = 13;
17+
private static final String IMAGETOOL_URL = "https://github.com/AppImage/AppImageKit/releases/download/" + IMAGETOOL_VERSION + "/appimagetool-%s.AppImage";
18+
19+
public GenerateAppImage() {
20+
super("AppImage");
21+
}
22+
23+
@Override
24+
public boolean skip(LinuxPackager packager) {
25+
26+
if (!packager.getLinuxConfig().isGenerateAppImage()) {
27+
return true;
28+
}
29+
30+
if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) {
31+
Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!");
32+
return true;
33+
}
34+
35+
return false;
36+
37+
}
38+
39+
@Override
40+
protected File doApply(LinuxPackager packager) throws Exception {
41+
42+
File appFolder = packager.getAppFolder();
43+
File outputFolder = packager.getOutputDirectory();
44+
String name = packager.getName();
45+
File executable = packager.getExecutable();
46+
File assetsFolder = packager.getAssetsFolder();
47+
File iconFile = packager.getIconFile();
48+
49+
// output AppImage file
50+
File appImage = new File(outputFolder, name + ".AppImage");
51+
52+
// AppDir folder
53+
File appDir = new File(assetsFolder, "AppDir");
54+
55+
// gets/downloads AppImage tool
56+
Logger.info("Getting appimagetool...");
57+
File appImageTool = getAppImageTool(packager);
58+
Logger.info("App image tool found! " + appImageTool);
59+
60+
// copies app folder to AppDir/usr/bin
61+
FileUtils.copyFolderContentToFolder(appFolder, new File(appDir, "usr/bin"));
62+
63+
// generates AppImage desktop file from velocity template
64+
File desktopFile = new File(appDir, name + ".desktop");
65+
VelocityUtils.render("linux/desktop-appimage.vtl", desktopFile, packager);
66+
Logger.info("Desktop file rendered in " + desktopFile.getAbsolutePath());
67+
68+
// creates AppRun symlink to startup script
69+
Logger.info("Creating AppRun symlink to startup script...");
70+
File appRun = new File(appDir, "AppRun");
71+
FileUtils.createSymlink(appRun, new File("usr/bin", executable.getName()));
72+
73+
// creates AppRun symlink to startup script
74+
Logger.info("Copying icon to AppDir ...");
75+
FileUtils.copyFileToFolder(iconFile, appDir);
76+
77+
// runs appimagetool on appFolder
78+
Logger.info("Running appimagetool on " + appFolder);
79+
CommandUtils.execute(
80+
appImageTool,
81+
"--appimage-extract-and-run",
82+
appDir,
83+
appImage
84+
);
85+
86+
Logger.info("Setting execution permissions to " + appImage);
87+
appImage.setExecutable(true);
88+
89+
return appImage;
90+
}
91+
92+
private File getAppImageTool(LinuxPackager packager) throws Exception {
93+
File assetsFolder = packager.getAssetsFolder();
94+
File appImageTool = new File(assetsFolder, "appimagetool");
95+
if (!appImageTool.exists()) {
96+
String imageToolUrl = String.format(IMAGETOOL_URL, getOSArch());
97+
try {
98+
FileUtils.downloadFromUrl(imageToolUrl, appImageTool);
99+
} catch (IOException e) {
100+
throw new Exception(imageToolUrl + "not found! ... Unsupported OS architecture " + getOSArch() + "?");
101+
}
102+
appImageTool.setExecutable(true);
103+
}
104+
return appImageTool;
105+
}
106+
107+
private String getOSArch() {
108+
switch (SystemUtils.OS_ARCH) {
109+
case "amd64":
110+
return "x86_64";
111+
case "x86":
112+
case "i386":
113+
return "i686";
114+
}
115+
return SystemUtils.OS_ARCH;
116+
}
117+
118+
}

src/main/java/io/github/fvarrui/javapackager/utils/CommandUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*/
1414
public class CommandUtils {
1515

16-
public static String execute(File workingDirectory, String executable, Object... arguments) throws IOException, CommandLineException {
16+
public static String executeOnDirectory(File workingDirectory, String executable, Object... arguments) throws IOException, CommandLineException {
1717
ExecutionResult result = executeWithResult(workingDirectory, executable, arguments);
1818
if (result.getExitCode() != 0) {
1919
throw new CommandLineException("Command execution failed: " + executable + " " + StringUtils.join(arguments, " "));
@@ -26,7 +26,7 @@ public static String execute(File executable, Object... arguments) throws IOExce
2626
}
2727

2828
public static String execute(String executable, Object... arguments) throws IOException, CommandLineException {
29-
return execute(new File("."), executable, arguments);
29+
return executeOnDirectory(new File("."), executable, arguments);
3030
}
3131

3232
public static ExecutionResult executeWithResult(File workingDirectory, String executable, Object... arguments) throws IOException, CommandLineException {

src/main/java/io/github/fvarrui/javapackager/utils/FileUtils.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,16 @@ public static void downloadFromUrl(URL url, File file) throws IOException {
298298
Logger.info("File downloaded from [" + url + "] to [" + file.getAbsolutePath() + "]");
299299
}
300300

301+
/**
302+
* Download a resource from an URL to a file
303+
* @param url URL to download
304+
* @param file File to copy the downloaded resource
305+
* @throws IOException Resource cannot be copied/downloaded
306+
*/
307+
public static void downloadFromUrl(String url, File file) throws IOException {
308+
downloadFromUrl(new URL(url), file);
309+
}
310+
301311
/**
302312
* Checks if a file exists or is not null
303313
* @param file File

src/main/java/io/github/fvarrui/javapackager/utils/JarUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class JarUtils {
99

1010
public static void addFileToJar(File jarFile, File newFile) throws IOException, CommandLineException {
1111
File jar = new File(System.getProperty("java.home"), "/bin/jar");
12-
CommandUtils.execute(newFile.getParentFile(), jar.getAbsolutePath(), "uf", jarFile, newFile.getName());
12+
CommandUtils.executeOnDirectory(newFile.getParentFile(), jar.getAbsolutePath(), "uf", jarFile, newFile.getName());
1313
}
1414

1515
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#set ($categories = $StringUtils.join($info.linuxConfig.categories, ";"))
2+
[Desktop Entry]
3+
Name=${info.displayName}
4+
GenericName=${info.displayName}
5+
Comment=${info.description}
6+
Exec=${info.name} %U
7+
Icon=${info.name}
8+
Terminal=false
9+
Type=Application
10+
StartupNotify=true
11+
#if ($info.isThereFileAssociations())
12+
MimeType=${info.getMimeTypesListAsString(";")}
13+
#end
14+
Categories=${categories}

src/main/resources/linux/desktop.vtl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#set ($categories = $StringUtils.join($info.linuxConfig.categories, ";"))
12
[Desktop Entry]
23
Name=${info.displayName}
34
GenericName=${info.displayName}
@@ -10,3 +11,4 @@ StartupNotify=true
1011
#if ($info.isThereFileAssociations())
1112
MimeType=${info.getMimeTypesListAsString(";")}
1213
#end
14+
Categories=${categories}
0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)