--- old/make/CompileDemos.gmk 2019-11-18 20:24:57.366828600 -0500 +++ new/make/CompileDemos.gmk 2019-11-18 20:24:55.879211100 -0500 @@ -219,6 +219,11 @@ MAIN_CLASS := transparentruler.Ruler, \ )) +$(eval $(call SetupBuildDemo, JNLPConverter, \ + DEMO_SUBDIR := jpackage, \ + MAIN_CLASS := jnlp.converter.Main, \ +)) + ################################################################################ # Copy html and README files. --- old/make/CompileJavaModules.gmk 2019-11-18 20:25:13.277801100 -0500 +++ new/make/CompileJavaModules.gmk 2019-11-18 20:25:11.772055800 -0500 @@ -380,12 +380,10 @@ ################################################################################ -jdk.jpackage_ADD_JAVAC_FLAGS += -parameters -XDstringConcat=inline +jdk.incubator.jpackage_COPY += .gif .png .txt .spec .script .prerm .preinst .postrm .postinst .list .sh \ + .desktop .copyright .control .plist .template .icns .scpt .entitlements .wxs .wxl .wxi .ico .bmp -jdk.jpackage_COPY += .gif .png .txt .spec .script .prerm .preinst .postrm .postinst .list \ - .desktop .copyright .control .plist .template .icns .scpt .entitlements .wxs .iss .ico .bmp - -jdk.jpackage_CLEAN += .properties +jdk.incubator.jpackage_CLEAN += .properties ################################################################################ --- old/make/common/Modules.gmk 2019-11-18 20:25:27.995718700 -0500 +++ new/make/common/Modules.gmk 2019-11-18 20:25:26.567488300 -0500 @@ -128,8 +128,8 @@ JRE_TOOL_MODULES += \ jdk.jdwp.agent \ + jdk.incubator.jpackage \ jdk.pack \ - jdk.jpackage \ jdk.scripting.nashorn.shell \ # @@ -150,6 +150,7 @@ jdk.editpad \ jdk.hotspot.agent \ jdk.httpserver \ + jdk.incubator.jpackage \ jdk.jartool \ jdk.javadoc \ jdk.jcmd \ @@ -170,7 +171,6 @@ jdk.naming.rmi \ jdk.net \ jdk.pack \ - jdk.jpackage \ jdk.rmic \ jdk.scripting.nashorn \ jdk.sctp \ @@ -247,8 +247,8 @@ ################################################################################ # jpackage is only on windows, macosx, and linux -ifeq ($(filter $(OPENJDK_TARGET_OS), windows macosx linux), ) - MODULES_FILTER += jdk.jpackage +ifeq ($(call isTargetOs, windows macosx linux), false) + MODULES_FILTER += jdk.incubator.jpackage endif ################################################################################ --- old/src/java.base/share/classes/module-info.java 2019-11-18 20:25:42.530037200 -0500 +++ new/src/java.base/share/classes/module-info.java 2019-11-18 20:25:41.126904500 -0500 @@ -203,7 +203,8 @@ java.management.rmi, jdk.jartool, jdk.jfr, - jdk.jlink; + jdk.jlink, + jdk.incubator.jpackage; exports jdk.internal.perf to java.management, jdk.management.agent, --- old/test/jdk/tools/jpackage/apps/image/Hello.java 2019-11-18 20:25:57.454746600 -0500 +++ new/test/jdk/tools/jpackage/apps/image/Hello.java 2019-11-18 20:25:56.042070500 -0500 @@ -21,60 +21,157 @@ * questions. */ -import java.io.BufferedWriter; +import java.awt.AWTError; +import java.awt.Desktop; +import java.awt.GraphicsEnvironment; +import java.awt.desktop.OpenFilesEvent; +import java.awt.desktop.OpenFilesHandler; import java.io.File; -import java.io.FileWriter; +import java.io.IOException; import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Path; +import java.nio.file.Files; +import java.util.stream.Collectors; +import java.util.List; +import java.util.ArrayList; +import java.util.stream.Stream; +import java.util.Collections; + +public class Hello implements OpenFilesHandler { + + public static void main(String[] args) throws IOException, InterruptedException { + var faFiles = getFaFiles(); + if (faFiles != null) { + // Some files got opened through fa mechanizm. + // They are the arguments then. + args = faFiles.toArray(String[]::new); + } -public class Hello { + var lines = printArgs(args); - private static final String MSG = "jpackage test application"; - private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1 + lines.forEach(System.out::println); - public static void main(String[] args) { - printToStdout(args); - printToFile(args); + var outputFile = getOutputFile(args); + trace(String.format("Output file: [%s]", outputFile)); + Files.write(outputFile, lines); } - private static void printToStdout(String[] args) { - System.out.println(MSG); + private static List printArgs(String[] args) { + List lines = new ArrayList<>(); + lines.add(MSG); - System.out.println("args.length: " + args.length); + lines.add("args.length: " + args.length); - for (String arg : args) { - System.out.println(arg); - } + lines.addAll(List.of(args)); for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) { String value = System.getProperty("param" + index); if (value != null) { - System.out.println("-Dparam" + index + "=" + value); + lines.add("-Dparam" + index + "=" + value); } } + + return lines; } - private static void printToFile(String[] args) { - String outputFile = "appOutput.txt"; - File file = new File(outputFile); - - try (PrintWriter out - = new PrintWriter(new BufferedWriter(new FileWriter(file)))) { - out.println(MSG); + private static Path getOutputFile(String[] args) { + Path outputFilePath = Path.of("appOutput.txt"); + + // If first arg is a file (most likely from fa), then put output in the same folder as + // the file from fa. + if (args.length >= 1) { + Path faPath = Path.of(args[0]); + if (Files.exists(faPath)) { + return faPath.toAbsolutePath().getParent().resolve(outputFilePath); + } + } + + try { + // Try writing in the default output file. + Files.write(outputFilePath, Collections.emptyList()); + return outputFilePath; + } catch (IOException ex) { + // Log reason of a failure. + StringWriter errors = new StringWriter(); + ex.printStackTrace(new PrintWriter(errors)); + Stream.of(errors.toString().split("\\R")).forEachOrdered(Hello::trace); + } + + return Path.of(System.getProperty("user.home")).resolve(outputFilePath); + } + + @Override + public void openFiles(OpenFilesEvent e) { + synchronized(lock) { + trace("openFiles"); + files = e.getFiles().stream() + .map(File::toString) + .collect(Collectors.toList()); + + lock.notifyAll(); + } + } - out.println("args.length: " + args.length); + private static List getFaFiles() throws InterruptedException { + if (openFilesHandler == null) { + return null; + } - for (String arg : args) { - out.println(arg); + synchronized(openFilesHandler.lock) { + trace("getFaFiles: wait"); + openFilesHandler.lock.wait(1000); + if (openFilesHandler.files == null) { + trace(String.format("getFaFiles: no files")); + return null; } + // Return copy of `files` to keep access to `files` field synchronized. + trace(String.format("getFaFiles: file count %d", + openFilesHandler.files.size())); + return new ArrayList<>(openFilesHandler.files); + } + } - for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) { - String value = System.getProperty("param" + index); - if (value != null) { - out.println("-Dparam" + index + "=" + value); - } + private List files; + private final Object lock = new Object(); + private final static Hello openFilesHandler = createInstance(); + + private static Hello createInstance() { + if (GraphicsEnvironment.isHeadless()) { + return null; + } + + trace("Environment supports a display"); + + try { + // Disable JAB. + // Needed to suppress error: + // Exception in thread "main" java.awt.AWTError: Assistive Technology not found: com.sun.java.accessibility.AccessBridge + System.setProperty("javax.accessibility.assistive_technologies", ""); + } catch (SecurityException ex) { + ex.printStackTrace(); + } + + try { + var desktop = Desktop.getDesktop(); + if (desktop.isSupported(Desktop.Action.APP_OPEN_FILE)) { + trace("Set file handler"); + Hello instance = new Hello(); + desktop.setOpenFileHandler(instance); + return instance; } - } catch (Exception ex) { - System.err.println(ex.getMessage()); + } catch (AWTError ex) { + trace("Set file handler failed"); + ex.printStackTrace(); } + + return null; + } + + private static final String MSG = "jpackage test application"; + private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1 + + private static void trace(String msg) { + System.out.println("hello: " + msg); } } --- old/test/jdk/tools/jpackage/helpers/JPackageHelper.java 2019-11-18 20:26:11.763123800 -0500 +++ new/test/jdk/tools/jpackage/helpers/JPackageHelper.java 2019-11-18 20:26:10.344132700 -0500 @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.io.BufferedWriter; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -33,8 +34,11 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.spi.ToolProvider; +import jdk.incubator.jpackage.ToolProviderFactory; public class JPackageHelper { @@ -49,6 +53,24 @@ private static final Path JAR; private static final Path JLINK; + public static class ModuleArgs { + private final String version; + private final String mainClass; + + ModuleArgs(String version, String mainClass) { + this.version = version; + this.mainClass = mainClass; + } + + public String getVersion() { + return version; + } + + public String getMainClass() { + return mainClass; + } + } + static { if (OS.startsWith("win")) { JPACKAGE = BIN_DIR.resolve("jpackage.exe"); @@ -63,34 +85,36 @@ } // Figure out test src based on where we called - File testSrc = new File(System.getProperty("test.src") + File.separator + ".." - + File.separator + "apps"); - if (testSrc.exists()) { - TEST_SRC_ROOT = System.getProperty("test.src") + File.separator + ".."; + TEST_SRC = System.getProperty("test.src"); + Path root = Path.of(TEST_SRC); + Path apps = Path.of(TEST_SRC, "apps"); + if (apps.toFile().exists()) { + // fine - test is at root } else { - testSrc = new File(System.getProperty("test.src") + File.separator - + ".." + File.separator + ".." + File.separator + "apps"); - if (testSrc.exists()) { - TEST_SRC_ROOT = System.getProperty("test.src") + File.separator + ".." - + File.separator + ".."; - } else { - testSrc = new File(System.getProperty("test.src") + File.separator - + ".." + File.separator + ".." + File.separator + ".." - + File.separator + "apps"); - if (testSrc.exists()) { - TEST_SRC_ROOT = System.getProperty("test.src") + File.separator + ".." - + File.separator + ".." + File.separator + ".."; - } else { - TEST_SRC_ROOT = System.getProperty("test.src"); - } + apps = Path.of(TEST_SRC, "..", "apps"); + if (apps.toFile().exists()) { + root = apps.getParent().normalize(); // test is 1 level down + } else { + apps = Path.of(TEST_SRC, "..", "..", "apps"); + if (apps.toFile().exists()) { + root = apps.getParent().normalize(); // 2 levels down + } else { + apps = Path.of(TEST_SRC, "..", "..", "..", "apps"); + if (apps.toFile().exists()) { + root = apps.getParent().normalize(); // 3 levels down + } else { + // if we ever have tests more than three levels + // down we need to add code here + throw new RuntimeException("we should never get here"); + } + } } } - - TEST_SRC = System.getProperty("test.src"); + TEST_SRC_ROOT = root.toString(); } static final ToolProvider JPACKAGE_TOOL = - ToolProvider.findFirst("jpackage").orElseThrow( + ToolProviderFactory.findFirst("jpackage").orElseThrow( () -> new RuntimeException("jpackage tool not found")); public static int execute(File out, String... command) throws Exception { @@ -163,8 +187,15 @@ @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException { + file.toFile().setWritable(true); if (OS.startsWith("win")) { - Files.setAttribute(file, "dos:readonly", false); + try { + Files.setAttribute(file, "dos:readonly", false); + } catch (Exception ioe) { + // just report and try to contune + System.err.println("IOException: " + ioe); + ioe.printStackTrace(System.err); + } } Files.delete(file); return FileVisitResult.CONTINUE; @@ -190,12 +221,12 @@ public static void deleteOutputFolder(String output) throws IOException { File outputFolder = new File(output); - System.out.println("AMDEBUG output: " + outputFolder.getAbsolutePath()); + System.out.println("deleteOutputFolder: " + outputFolder.getAbsolutePath()); try { deleteRecursive(outputFolder); } catch (IOException ioe) { - System.out.println("IOException: " + ioe); - ioe.printStackTrace(); + System.err.println("IOException: " + ioe); + ioe.printStackTrace(System.err); deleteRecursive(outputFolder); } } @@ -203,8 +234,8 @@ public static String executeCLI(boolean retValZero, String... args) throws Exception { int retVal; File outfile = new File("output.log"); + String[] command = getCommand(args); try { - String[] command = getCommand(args); retVal = execute(outfile, command); } catch (Exception ex) { if (outfile.exists()) { @@ -216,6 +247,9 @@ String output = Files.readString(outfile.toPath()); if (retValZero) { if (retVal != 0) { + System.err.println("command run:"); + for (String s : command) { System.err.println(s); } + System.err.println("command output:"); System.err.println(output); throw new AssertionError("jpackage exited with error: " + retVal); } @@ -272,33 +306,44 @@ return ((OS.contains("nix") || OS.contains("nux"))); } + public static void createHelloImageJar(String inputDir) throws Exception { + createJar(false, "Hello", "image", inputDir); + } + public static void createHelloImageJar() throws Exception { - createJar(false, "Hello", "image"); + createJar(false, "Hello", "image", "input"); } public static void createHelloImageJarWithMainClass() throws Exception { - createJar(true, "Hello", "image"); + createJar(true, "Hello", "image", "input"); } public static void createHelloInstallerJar() throws Exception { - createJar(false, "Hello", "installer"); + createJar(false, "Hello", "installer", "input"); } public static void createHelloInstallerJarWithMainClass() throws Exception { - createJar(true, "Hello", "installer"); + createJar(true, "Hello", "installer", "input"); } private static void createJar(boolean mainClassAttribute, String name, - String testType) throws Exception { + String testType, String inputDir) throws Exception { int retVal; - File input = new File("input"); + File input = new File(inputDir); if (!input.exists()) { - input.mkdir(); + input.mkdirs(); } - Files.copy(Path.of(TEST_SRC_ROOT + File.separator + "apps" + File.separator - + testType + File.separator + name + ".java"), Path.of(name + ".java")); + Path src = Path.of(TEST_SRC_ROOT + File.separator + "apps" + + File.separator + testType + File.separator + name + ".java"); + Path dst = Path.of(name + ".java"); + + if (dst.toFile().exists()) { + Files.delete(dst); + } + Files.copy(src, dst); + File javacLog = new File("javac.log"); try { @@ -324,7 +369,7 @@ args.add("-c"); args.add("-v"); args.add("-f"); - args.add("input" + File.separator + name.toLowerCase() + ".jar"); + args.add(inputDir + File.separator + name.toLowerCase() + ".jar"); if (mainClassAttribute) { args.add("-e"); args.add(name); @@ -347,15 +392,19 @@ } public static void createHelloModule() throws Exception { - createModule("Hello.java", "input", "hello"); + createModule("Hello.java", "input", "hello", null, true); + } + + public static void createHelloModule(ModuleArgs moduleArgs) throws Exception { + createModule("Hello.java", "input", "hello", moduleArgs, true); } public static void createOtherModule() throws Exception { - createModule("Other.java", "input-other", "other"); + createModule("Other.java", "input-other", "other", null, false); } - private static void createModule(String javaFile, String inputDir, - String aName) throws Exception { + private static void createModule(String javaFile, String inputDir, String aName, + ModuleArgs moduleArgs, boolean createModularJar) throws Exception { int retVal; File input = new File(inputDir); @@ -394,34 +443,52 @@ throw new AssertionError("javac exited with error: " + retVal); } - File jarLog = new File("jar.log"); - try { - List args = new ArrayList<>(); - args.add(JAR.toString()); - args.add("--create"); - args.add("--file"); - args.add(inputDir + File.separator + "com." + aName + ".jar"); - args.add("-C"); - args.add("module" + File.separator + "com." + aName); - args.add("."); - - retVal = execute(jarLog, args.stream().toArray(String[]::new)); - } catch (Exception ex) { - if (jarLog.exists()) { - System.err.println(Files.readString(jarLog.toPath())); + if (createModularJar) { + File jarLog = new File("jar.log"); + try { + List args = new ArrayList<>(); + args.add(JAR.toString()); + args.add("--create"); + args.add("--file"); + args.add(inputDir + File.separator + "com." + aName + ".jar"); + if (moduleArgs != null) { + if (moduleArgs.getVersion() != null) { + args.add("--module-version"); + args.add(moduleArgs.getVersion()); + } + + if (moduleArgs.getMainClass()!= null) { + args.add("--main-class"); + args.add(moduleArgs.getMainClass()); + } + } + args.add("-C"); + args.add("module" + File.separator + "com." + aName); + args.add("."); + + retVal = execute(jarLog, args.stream().toArray(String[]::new)); + } catch (Exception ex) { + if (jarLog.exists()) { + System.err.println(Files.readString(jarLog.toPath())); + } + throw ex; } - throw ex; - } - if (retVal != 0) { - if (jarLog.exists()) { - System.err.println(Files.readString(jarLog.toPath())); + if (retVal != 0) { + if (jarLog.exists()) { + System.err.println(Files.readString(jarLog.toPath())); + } + throw new AssertionError("jar exited with error: " + retVal); } - throw new AssertionError("jar exited with error: " + retVal); } } public static void createRuntime() throws Exception { + List moreArgs = new ArrayList<>(); + createRuntime(moreArgs); + } + + public static void createRuntime(List moreArgs) throws Exception { int retVal; File jlinkLog = new File("jlink.log"); @@ -432,6 +499,8 @@ args.add("runtime"); args.add("--add-modules"); args.add("java.base"); + args.addAll(moreArgs); + retVal = execute(jlinkLog, args.stream().toArray(String[]::new)); } catch (Exception ex) { if (jlinkLog.exists()) { @@ -473,6 +542,42 @@ return argsStr; } + public static String[] cmdWithAtFilename(String [] cmd, int ndx, int len) + throws IOException { + ArrayList newAList = new ArrayList<>(); + String fileString = null; + for (int i=0; i ndx && i < ndx + len) { + fileString += " " + cmd[i]; + } else { + newAList.add(cmd[i]); + } + } + if (fileString != null) { + Path path = new File("argfile.cmds").toPath(); + try (BufferedWriter bw = Files.newBufferedWriter(path); + PrintWriter out = new PrintWriter(bw)) { + out.println(fileString); + } + } + return newAList.toArray(new String[0]); + } + + public static String [] splitAndFilter(String output) { + if (output == null) { + return null; + } + + return Stream.of(output.split("\\R")) + .filter(str -> !str.startsWith("Picked up")) + .filter(str -> !str.startsWith("WARNING: Using incubator")) + .filter(str -> !str.startsWith("hello: ")) + .collect(Collectors.toList()).toArray(String[]::new); + } + private static String quote(String in, boolean toolProvider) { if (in == null) { return null; @@ -576,5 +681,4 @@ return in; } - } --- old/test/jdk/tools/jpackage/helpers/JPackageInstallerHelper.java 2019-11-18 20:26:26.139773800 -0500 +++ new/test/jdk/tools/jpackage/helpers/JPackageInstallerHelper.java 2019-11-18 20:26:24.736554100 -0500 @@ -144,100 +144,4 @@ } } } - - public static void validateStartMenu(String menuGroup, String menu, boolean exist) - throws Exception { - String startMenuLink = JPackagePath.getWinStartMenu() + - File.separator + menuGroup + File.separator + - menu + ".lnk"; - - File link = new File(startMenuLink); - if (exist) { - if (!link.exists()) { - throw new AssertionError("Cannot find " + link.getAbsolutePath()); - } - } else { - if (link.exists()) { - throw new AssertionError("Error: " + link.getAbsolutePath() + " exist"); - } - } - } - - public static void validateDesktopShortcut(String name, boolean exist) - throws Exception { - String shortcutLink = JPackagePath.getWinPublicDesktop() + - File.separator + name + ".lnk"; - - File link = new File(shortcutLink); - if (exist) { - if (!link.exists()) { - throw new AssertionError("Cannot find " + link.getAbsolutePath()); - } - } else { - if (link.exists()) { - throw new AssertionError("Error: " + link.getAbsolutePath() + " exist"); - } - } - } - - public static void validateUserLocalStartMenu(String menuGroup, String menu, boolean exist) - throws Exception { - String startMenuLink = JPackagePath.getWinUserLocalStartMenu() + - File.separator + menuGroup + File.separator + - menu + ".lnk"; - - File link = new File(startMenuLink); - if (exist) { - if (!link.exists()) { - throw new AssertionError("Cannot find " + link.getAbsolutePath()); - } - } else { - if (link.exists()) { - throw new AssertionError("Error: " + link.getAbsolutePath() + " exist"); - } - } - } - - public static void validateWinRegistry(String key, String [] values, boolean retValZero) - throws Exception { - File outFile = new File("regOutput.txt"); - if (outFile.exists()) { - outFile.delete(); - } - - int retVal = JPackageHelper.execute(outFile, "reg.exe", "query", key); - if (retValZero) { - if (retVal != 0) { - System.out.println("validateWinRegistry() key=" + key); - if (outFile.exists()) { - System.err.println(Files.readString(outFile.toPath())); - } - throw new AssertionError( - "Reg.exe application exited with error: " + retVal); - } - } else { - if (retVal == 0) { - System.out.println("validateWinRegistry() key=" + key); - if (outFile.exists()) { - System.err.println(Files.readString(outFile.toPath())); - } - throw new AssertionError( - "Reg.exe application exited without error: " + retVal); - } else { - return; // Done - } - } - - if (!outFile.exists()) { - throw new AssertionError(outFile.getAbsolutePath() + " was not created"); - } - - String output = Files.readString(outFile.toPath()); - for (String value : values) { - if (!output.contains(value)) { - System.err.println(output); - throw new AssertionError("Cannot find in registry: " + value); - } - } - } } --- old/test/jdk/tools/jpackage/helpers/JPackagePath.java 2019-11-18 20:26:40.243319700 -0500 +++ new/test/jdk/tools/jpackage/helpers/JPackagePath.java 2019-11-18 20:26:38.814795100 -0500 @@ -22,22 +22,14 @@ */ import java.io.File; +import java.nio.file.Path; /** - * Helper class which contains functions to get different system dependent paths used by tests + * Helper class which contains functions to get different system + * dependent paths used by tests */ public class JPackagePath { - // Path to Windows "Program Files" folder - // Probably better to figure this out programattically - private static final String WIN_PROGRAM_FILES = "C:\\Program Files"; - - // Path to Windows Start menu items - private static final String WIN_START_MENU = "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs"; - - // Path to Windows public desktop location - private static final String WIN_PUBLIC_DESKTOP = "C:\\Users\\Public\\Desktop"; - // Return path to test src adjusted to location of caller public static String getTestSrcRoot() { return JPackageHelper.TEST_SRC_ROOT; @@ -50,70 +42,44 @@ // Returns path to generate test application public static String getApp() { - if (JPackageHelper.isWindows()) { - return "output" + File.separator + "test" + File.separator + "test.exe"; - } else if (JPackageHelper.isOSX()) { - return "output" + File.separator + "test.app" + File.separator + "Contents" - + File.separator + "MacOS" + File.separator + "test"; - } else if (JPackageHelper.isLinux()) { - return "output" + File.separator + "test" + File.separator + "test"; - } else { - throw new AssertionError("Cannot detect platform"); - } + return getApp("test"); + } + + public static String getApp(String name) { + return getAppSL(name, name); } // Returns path to generate test application icon public static String getAppIcon() { - if (JPackageHelper.isWindows()) { - return "output" + File.separator + "test" + File.separator + "test.ico"; - } else if (JPackageHelper.isOSX()) { - return "output" + File.separator + "test.app" + File.separator + "Contents" - + File.separator + "Resources" + File.separator + "test.icns"; - } else if (JPackageHelper.isLinux()) { - return "output" + File.separator + "test" + File.separator - + File.separator + "resources"+ File.separator + "test.png"; - } else { - throw new AssertionError("Cannot detect platform"); - } + return getAppIcon("test"); } - // Returns path to generate test application without --name argument - public static String getAppNoName() { + public static String getAppIcon(String name) { if (JPackageHelper.isWindows()) { - return "output" + File.separator + "Hello" + File.separator + "Hello.exe"; + return Path.of("output", name, name + ".ico").toString(); } else if (JPackageHelper.isOSX()) { - return "output" + File.separator + "Hello.app" + File.separator + "Contents" - + File.separator + "MacOS" + File.separator + "Hello"; + return Path.of("output", name + ".app", + "Contents", "Resources", name + ".icns").toString(); } else if (JPackageHelper.isLinux()) { - return "output" + File.separator + "Hello" + File.separator + "Hello"; + return Path.of("output", name, "lib", name + ".png").toString(); } else { throw new AssertionError("Cannot detect platform"); } } - // Returns path to generate secondary launcher of test application - public static String getAppSL() { - if (JPackageHelper.isWindows()) { - return "output" + File.separator + "test" + File.separator + "test2.exe"; - } else if (JPackageHelper.isOSX()) { - return "output" + File.separator + "test.app" + File.separator + "Contents" - + File.separator + "MacOS" + File.separator + "test2"; - } else if (JPackageHelper.isLinux()) { - return "output" + File.separator + "test" + File.separator + "test2"; - } else { - throw new AssertionError("Cannot detect platform"); - } + // Returns path to generate secondary launcher of given application + public static String getAppSL(String sl) { + return getAppSL("test", sl); } - // Returns path to app working directory (where test application generates its output) - public static String getAppWorkingDir() { - if (JPackageHelper.isWindows()) { - return "output" + File.separator + "test" + File.separator + "app"; + public static String getAppSL(String app, String sl) { + if (JPackageHelper.isWindows()) { + return Path.of("output", app, sl + ".exe").toString(); } else if (JPackageHelper.isOSX()) { - return "output" + File.separator + "test.app" + File.separator + "Contents" - + File.separator + "Java"; + return Path.of("output", app + ".app", + "Contents", "MacOS", sl).toString(); } else if (JPackageHelper.isLinux()) { - return "output" + File.separator + "test" + File.separator + "app"; + return Path.of("output", app, "bin", sl).toString(); } else { throw new AssertionError("Cannot detect platform"); } @@ -121,29 +87,17 @@ // Returns path to test application cfg file public static String getAppCfg() { - if (JPackageHelper.isWindows()) { - return "output" + File.separator + "test" + File.separator + "app" + File.separator - + "test.cfg"; - } else if (JPackageHelper.isOSX()) { - return "output" + File.separator + "test.app" + File.separator + "Contents" - + File.separator + "Java" + File.separator + "test.cfg"; - } else if (JPackageHelper.isLinux()) { - return "output" + File.separator + "test" + File.separator + "app" + File.separator - + "test.cfg"; - } else { - throw new AssertionError("Cannot detect platform"); - } + return getAppCfg("test"); } - // Returns path to app working directory without --name (where test application generates its output) - public static String getAppWorkingDirNoName() { + public static String getAppCfg(String name) { if (JPackageHelper.isWindows()) { - return "output" + File.separator + "Hello" + File.separator + "app"; + return Path.of("output", name, "app", name + ".cfg").toString(); } else if (JPackageHelper.isOSX()) { - return "output" + File.separator + "Hello.app" + File.separator + "Contents" - + File.separator + "Java"; + return Path.of("output", name + ".app", + "Contents", "app", name + ".cfg").toString(); } else if (JPackageHelper.isLinux()) { - return "output" + File.separator + "Hello" + File.separator + "app"; + return Path.of("output", name, "lib", "app", name + ".cfg").toString(); } else { throw new AssertionError("Cannot detect platform"); } @@ -151,22 +105,14 @@ // Returns path including executable to java in image runtime folder public static String getRuntimeJava() { + return getRuntimeJava("test"); + } + + public static String getRuntimeJava(String name) { if (JPackageHelper.isWindows()) { - return "output" + File.separator + "test" - + File.separator + "runtime" + File.separator - + "bin" + File.separator + "java.exe"; - } else if (JPackageHelper.isOSX()) { - return "output" + File.separator + "test.app" + File.separator - + "Contents" + File.separator - + "runtime" + File.separator + "Contents" + File.separator - + "Home" + File.separator + "bin" + File.separator + "java"; - } else if (JPackageHelper.isLinux()) { - return "output" + File.separator + "test" - + File.separator + "runtime" + File.separator - + "bin" + File.separator + "java"; - } else { - throw new AssertionError("Cannot detect platform"); + return Path.of(getRuntimeBin(name), "java.exe").toString(); } + return Path.of(getRuntimeBin(name), "java").toString(); } // Returns output file name generate by test application @@ -176,117 +122,46 @@ // Returns path to bin folder in image runtime public static String getRuntimeBin() { + return getRuntimeBin("test"); + } + + public static String getRuntimeBin(String name) { if (JPackageHelper.isWindows()) { - return "output" + File.separator + "test" - + File.separator + "runtime" + File.separator + "bin"; + return Path.of("output", name, "runtime", "bin").toString(); } else if (JPackageHelper.isOSX()) { - return "output" + File.separator + "test.app" - + File.separator + "Contents" - + File.separator + "runtime" - + File.separator + "Contents" - + File.separator + "Home" + File.separator + "bin"; + return Path.of("output", name + ".app", + "Contents", "runtime", + "Contents", "Home", "bin").toString(); } else if (JPackageHelper.isLinux()) { - return "output" + File.separator + "test" - + File.separator + "runtime" + File.separator + "bin"; + return Path.of("output", name, "lib", "runtime", "bin").toString(); } else { throw new AssertionError("Cannot detect platform"); } } - public static String getWinProgramFiles() { - return WIN_PROGRAM_FILES; - } - - public static String getWinUserLocal() { - return System.getProperty("user.home") + File.separator + "AppData" - + File.separator + "Local"; - } - - public static String getWinStartMenu() { - return WIN_START_MENU; - } - - public static String getWinPublicDesktop() { - return WIN_PUBLIC_DESKTOP; - } - - public static String getWinUserLocalStartMenu() { - return System.getProperty("user.home") + File.separator + "AppData" - + File.separator + "Roaming" + File.separator + "Microsoft" - + File.separator + "Windows" + File.separator + "Start Menu" - + File.separator + "Programs"; - - } - - public static String getWinInstalledApp(String testName) { - return getWinProgramFiles() + File.separator + testName + File.separator - + testName + ".exe"; - } - - public static String getWinInstalledApp(String installDir, String testName) { - return getWinProgramFiles() + File.separator + installDir + File.separator - + testName + ".exe"; - } - public static String getOSXInstalledApp(String testName) { - return File.separator + "Applications" + File.separator + testName - + ".app" + File.separator + "Contents" + File.separator - + "MacOS" + File.separator + testName; - } - - public static String getLinuxInstalledApp(String testName) { - return File.separator + "opt" + File.separator + testName + - File.separator + testName; + return File.separator + "Applications" + + File.separator + testName + ".app" + + File.separator + "Contents" + + File.separator + "MacOS" + + File.separator + testName; } public static String getOSXInstalledApp(String subDir, String testName) { - return File.separator + "Applications" + File.separator + subDir - + File.separator + testName + ".app" + File.separator - + "Contents" + File.separator + "MacOS" + File.separator - + testName; - } - - public static String getLinuxInstalledApp(String subDir, String testName) { - return File.separator + "opt" + File.separator + subDir + File.separator - + testName + File.separator + testName; - } - - public static String getWinInstallFolder(String testName) { - return getWinProgramFiles() + File.separator + testName; - } - - public static String getLinuxInstallFolder(String testName) { - return File.separator + "opt" + File.separator + testName; - } - - public static String getLinuxInstallFolder(String subDir, String testName) { - if (testName == null) { - return File.separator + "opt" + File.separator + subDir; - } else { - return File.separator + "opt" + File.separator + subDir - + File.separator + testName; - } - } - - public static String getWinUserLocalInstalledApp(String testName) { - return getWinUserLocal() + File.separator + testName + File.separator + testName + ".exe"; - } - - public static String getWinUserLocalInstallFolder(String testName) { - return getWinUserLocal() + File.separator + testName; + return File.separator + "Applications" + + File.separator + subDir + + File.separator + testName + ".app" + + File.separator + "Contents" + + File.separator + "MacOS" + + File.separator + testName; } // Returs path to test license file public static String getLicenseFilePath() { - String path = JPackagePath.getTestSrcRoot() + File.separator + "resources" + String path = JPackagePath.getTestSrcRoot() + + File.separator + "resources" + File.separator + "license.txt"; return path; } - - // Returns path to app folder of installed application - public static String getWinInstalledAppFolder(String testName) { - return getWinProgramFiles() + File.separator + testName + File.separator - + "app"; - } } --- old/test/jdk/tools/launcher/HelpFlagsTest.java 2019-11-18 20:26:55.151714800 -0500 +++ new/test/jdk/tools/launcher/HelpFlagsTest.java 2019-11-18 20:26:53.701770000 -0500 @@ -161,7 +161,7 @@ new ToolHelpSpec("rmiregistry", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways. new ToolHelpSpec("serialver", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways. new ToolHelpSpec("unpack200", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented. - new ToolHelpSpec("jpackage", 0, 1, 1, 0, 0, 1, 255), // -h, --help, + new ToolHelpSpec("jpackage", 0, 1, 1, 0, 0, 1, 1), // -h, --help, }; // Returns true if the file is not a tool. --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinLauncher.template 2019-11-18 20:27:10.246819200 -0500 +++ /dev/null 2019-11-18 20:27:11.000000000 -0500 @@ -1,34 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -CompanyName=COMPANY_NAME -FileDescription=FILE_DESCRIPTION -FileVersion=FILE_VERSION -InternalName=INTERNAL_NAME -LegalCopyright=LEGAL_COPYRIGHT -OriginalFilename=ORIGINAL_FILENAME -ProductName=PRODUCT_NAME -ProductVersion=PRODUCT_VERSION --- /dev/null 2019-11-18 20:27:11.000000000 -0500 +++ new/make/launcher/Launcher-jdk.incubator.jpackage.gmk 2019-11-18 20:27:07.478158000 -0500 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +include LauncherCommon.gmk + +$(eval $(call SetupBuildLauncher, jpackage, \ + MAIN_CLASS := jdk.incubator.jpackage.main.Main, \ +)) --- old/make/lib/Lib-jdk.jpackage.gmk 2019-11-18 20:27:25.428671900 -0500 +++ /dev/null 2019-11-18 20:27:26.000000000 -0500 @@ -1,87 +0,0 @@ -# -# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -include LibCommon.gmk - -################################################################################ - -# Output app launcher library in resources dir, and symbols in the object dir -$(eval $(call SetupJdkLibrary, BUILD_LIB_APPLAUNCHER, \ - NAME := applauncher, \ - OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \ - SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libapplauncher, \ - TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ - OPTIMIZATION := LOW, \ - CFLAGS := $(CXXFLAGS_JDKLIB), \ - CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \ - LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \ - $(call SET_SHARED_LIBRARY_ORIGIN), \ - LIBS := $(LIBCXX), \ - LIBS_windows := user32.lib shell32.lib advapi32.lib ole32.lib, \ - LIBS_linux := -ldl -lpthread, \ - LIBS_macosx := -ldl -framework Cocoa, \ -)) - -$(BUILD_LIB_APPLAUNCHER): $(call FindLib, java.base, java) - -TARGETS += $(BUILD_LIB_APPLAUNCHER) - -################################################################################ - -ifeq ($(OPENJDK_TARGET_OS), windows) - - $(eval $(call SetupJdkLibrary, BUILD_LIB_JPACKAGE, \ - NAME := jpackage, \ - OPTIMIZATION := LOW, \ - CFLAGS := $(CXXFLAGS_JDKLIB), \ - CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \ - LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \ - $(call SET_SHARED_LIBRARY_ORIGIN), \ - LIBS := $(LIBCXX), \ - LIBS_windows := user32.lib shell32.lib advapi32.lib ole32.lib, \ - )) - - TARGETS += $(BUILD_LIB_JPACKAGE) - -endif - -# Build Wix custom action helper -# Output library in resources dir, and symbols in the object dir -ifeq ($(OPENJDK_TARGET_OS), windows) - - $(eval $(call SetupJdkLibrary, BUILD_LIB_WIXHELPER, \ - NAME := wixhelper, \ - OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \ - SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libwixhelper, \ - OPTIMIZATION := LOW, \ - CFLAGS := $(CXXFLAGS_JDKLIB), \ - CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \ - LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK), \ - LIBS := $(LIBCXX), \ - LIBS_windows := msi.lib Shlwapi.lib User32.lib, \ - )) - - TARGETS += $(BUILD_LIB_WIXHELPER) -endif --- /dev/null 2019-11-18 20:27:26.000000000 -0500 +++ new/make/lib/Lib-jdk.incubator.jpackage.gmk 2019-11-18 20:27:22.829299100 -0500 @@ -0,0 +1,140 @@ +# +# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +include LibCommon.gmk + +################################################################################ + +# Output app launcher library in resources dir, and symbols in the object dir +$(eval $(call SetupJdkLibrary, BUILD_LIB_APPLAUNCHER, \ + NAME := applauncher, \ + OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/incubator/jpackage/internal/resources, \ + SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libapplauncher, \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CXXFLAGS_JDKLIB), \ + CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \ + LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \ + $(call SET_SHARED_LIBRARY_ORIGIN), \ + LIBS := $(LIBCXX), \ + LIBS_windows := user32.lib shell32.lib advapi32.lib ole32.lib, \ + LIBS_linux := -ldl -lpthread, \ + LIBS_macosx := -ldl -framework Cocoa, \ +)) + +$(BUILD_LIB_APPLAUNCHER): $(call FindLib, java.base, java) + +TARGETS += $(BUILD_LIB_APPLAUNCHER) + +JPACKAGE_APPLAUNCHER_SRC := \ + $(TOPDIR)/src/jdk.incubator.jpackage/$(OPENJDK_TARGET_OS)/native/jpackageapplauncher + +# Output app launcher executable in resources dir, and symbols in the object dir +$(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_APPLAUNCHEREXE, \ + NAME := jpackageapplauncher, \ + OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/incubator/jpackage/internal/resources, \ + SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jpackageapplauncher, \ + SRC := $(JPACKAGE_APPLAUNCHER_SRC), \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CXXFLAGS_JDKEXE), \ + CFLAGS_windows := -EHsc -DLAUNCHERC -DUNICODE -D_UNICODE, \ + LDFLAGS := $(LDFLAGS_JDKEXE), \ + LIBS_macosx := -framework Cocoa, \ + LIBS := $(LIBCXX), \ + LIBS_linux := -ldl, \ + LIBS_windows := user32.lib shell32.lib advapi32.lib, \ +)) + +TARGETS += $(BUILD_JPACKAGE_APPLAUNCHEREXE) + +################################################################################ + +ifeq ($(call isTargetOs, windows), true) + + $(eval $(call SetupJdkLibrary, BUILD_LIB_JPACKAGE, \ + NAME := jpackage, \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CXXFLAGS_JDKLIB), \ + CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \ + LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \ + $(call SET_SHARED_LIBRARY_ORIGIN), \ + LIBS := $(LIBCXX), \ + LIBS_windows := user32.lib shell32.lib advapi32.lib ole32.lib, \ + )) + + TARGETS += $(BUILD_LIB_JPACKAGE) + + # Build Wix custom action helper + # Output library in resources dir, and symbols in the object dir + $(eval $(call SetupJdkLibrary, BUILD_LIB_WIXHELPER, \ + NAME := wixhelper, \ + OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/incubator/jpackage/internal/resources, \ + SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libwixhelper, \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CXXFLAGS_JDKLIB), \ + CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE -MT, \ + LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK), \ + LIBS := $(LIBCXX), \ + LIBS_windows := msi.lib Shlwapi.lib User32.lib, \ + )) + + TARGETS += $(BUILD_LIB_WIXHELPER) + + # Build exe installer wrapper for msi installer + $(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_MSIWRAPPER, \ + NAME := msiwrapper, \ + OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/incubator/jpackage/internal/resources, \ + SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/msiwrapper, \ + SRC := $(TOPDIR)/src/jdk.incubator.jpackage/$(OPENJDK_TARGET_OS)/native/msiwrapper, \ + EXTRA_FILES := $(addprefix $(TOPDIR)/src/jdk.incubator.jpackage/$(OPENJDK_TARGET_OS)/native/libjpackage/, \ + FileUtils.cpp Log.cpp WinSysInfo.cpp tstrings.cpp WinErrorHandling.cpp ErrorHandling.cpp), \ + CFLAGS := $(CXXFLAGS_JDKEXE) -MT \ + $(addprefix -I$(TOPDIR)/src/jdk.incubator.jpackage/$(OPENJDK_TARGET_OS)/native/, msiwrapper libjpackage), \ + CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \ + LDFLAGS := $(LDFLAGS_JDKEXE), \ + LIBS := $(LIBCXX), \ + )) + + TARGETS += $(BUILD_JPACKAGE_MSIWRAPPER) + + # Build non-console version of launcher + $(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_APPLAUNCHERWEXE, \ + NAME := jpackageapplauncherw, \ + OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/incubator/jpackage/internal/resources, \ + SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jpackageapplauncherw, \ + SRC := $(JPACKAGE_APPLAUNCHER_SRC), \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CXXFLAGS_JDKEXE), \ + CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \ + LDFLAGS := $(LDFLAGS_JDKEXE), \ + LIBS := $(LIBCXX), \ + LIBS_windows := user32.lib shell32.lib advapi32.lib, \ + )) + + TARGETS += $(BUILD_JPACKAGE_APPLAUNCHERWEXE) + +endif --- /dev/null 2019-11-18 20:27:44.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/README.txt 2019-11-18 20:27:39.216812300 -0500 @@ -0,0 +1,23 @@ +About JNLPConverter +=================== + +JNLPConverter is a standalone tool which uses jpackage to create bundles from +Java Web Start(TM) Applications and helps to migrate from JNLP to jpackage. +JNLPConverter will locate and use the jpackage tool from the same JDK as used +to run JNLPConverter. JNLPConverter supports HTTP/HTTPS and FILE protocol. + +Running JNLPConverter +===================== + +To run the JNLPConverter: + + java -jar JNLPConverter.jar + +To get help on JNLPConverter options: + + java -jar JNLPConverter.jar --help + +These instructions assume that this installation's version of the java command +is in your path. If it isn't, then you should either specify the complete path +to the java command or update your PATH environment variable as described +in the installation instructions for the Java(TM) SE Development Kit. --- /dev/null 2019-11-18 20:27:57.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/HTTPHelper.java 2019-11-18 20:27:54.402811200 -0500 @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import jnlp.converter.parser.GeneralUtil; + +public class HTTPHelper { + + public static final int BUFFER_SIZE = 4096; + + public static String downloadFile(String url, String destFolder, String destFileName) throws MalformedURLException, IOException { + HttpURLConnection connection = null; + String destFile = null; + + try { + if (url.contains(" ")) { + url = url.replace(" ", "%20"); + } + if (url.contains("\\")) { + url = url.replace("\\", "/"); + } + + URL resource = new URL(url); + connection = (HttpURLConnection) resource.openConnection(); + + int responseCode = connection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + destFile = destFolder + File.separator + destFileName; + Log.verbose("Downloading " + url + " to " + destFile); + + try (InputStream inputStream = connection.getInputStream(); + OutputStream outputStream = new FileOutputStream(destFile)) { + byte[] buffer = new byte[BUFFER_SIZE]; + + int length; + do { + length = inputStream.read(buffer); + if (length > 0) { + outputStream.write(buffer, 0, length); + } + } while (length > 0); + } + } else { + HTTPHelperException e = new HTTPHelperException("Error: Cannot download " + url + ". Server response code: " + responseCode); + e.setResponseCode(responseCode); + throw e; + } + } catch (IOException e) { + if (e instanceof HTTPHelperException) { + throw e; + } else { + throw new HTTPHelperException("Error: Cannot download " + url + ". " + e.getClass().getSimpleName() + ": " + e.getMessage()); + } + } finally { + if (connection != null) { + connection.disconnect(); + } + } + + return destFile; + } + + public static String copyFile(String url, String destFolder, String destFileName) throws Exception { + if (url.contains(" ")) { + url = url.replace(" ", "%20"); + } + + URI sourceURI = new URI(url); + + String sourceFile = sourceURI.getPath(); + File file = new File(sourceFile); + if (!file.exists()) { + throw new FileNotFoundException("Error: " + sourceFile + " does not exist."); + } + + String destFile = destFolder + File.separator + destFileName; + file = new File(destFile); + if (file.exists()) { + file.delete(); + } + + Path sourcePath = Paths.get(sourceURI); + Path destPath = Paths.get(destFile); + Log.verbose("Copying " + url + " to " + destFile); + Files.copy(sourcePath, destPath); + + return destFile; + } + + public static boolean isHTTPUrl(String url) { + return (url.startsWith("http://") || url.startsWith("https://")); + } + + public static byte[] getJNLPBits(String versionedJNLP, String jnlp) throws Exception { + String jnlpFilePath = null; + byte[] bits = null; + + if (isHTTPUrl(jnlp)) { + try { + jnlpFilePath = downloadFile(versionedJNLP, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp)); + } catch (HTTPHelperException ex) { + if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND && + !versionedJNLP.equals(jnlp)) { + Log.warning("Downloading versioned JNLP from " + versionedJNLP + " failed."); + Log.warning(ex.getMessage()); + Log.warning("Downloading " + jnlp + " instead."); + jnlpFilePath = downloadFile(jnlp, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp)); + } else { + throw ex; + } + } + JNLPConverter.markFileToDelete(jnlpFilePath); + } else { + try { + jnlpFilePath = copyFile(versionedJNLP, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp)); + } catch (FileNotFoundException ex) { + System.out.println("Error copying versioned JNLP from " + versionedJNLP); + System.out.println(ex.getMessage()); + System.out.println("Copying " + jnlp + " instead."); + jnlpFilePath = HTTPHelper.copyFile(jnlp, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp)); + } + JNLPConverter.markFileToDelete(jnlpFilePath); + } + + File jnlpFile = new File(jnlpFilePath); + if (jnlpFile.exists()) { + bits = GeneralUtil.readBytes(new FileInputStream(jnlpFile), jnlpFile.length()); + } + + return bits; + } + + public static String getFileNameFromURL(String url) throws IOException { + int index; + int index1 = url.lastIndexOf('/'); + int index2 = url.lastIndexOf('\\'); + + if (index1 >= index2) { + index = index1; + } else { + index = index2; + } + + if (index != -1) { + String name = url.substring(index + 1, url.length()); + name = name.replace("%20", " "); + if (name.endsWith(".jnlp") || name.endsWith(".jar")) { // JNLP or JAR + return name; + } else if (name.endsWith(".ico")) { // Icons + return name; + } else { + throw new IOException("Error: Unsupported file extension for " + url); + } + } else { + throw new IOException("Error: URL (" + url + ") should end with file name."); + } + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java 2019-11-18 20:28:09.948766900 -0500 +++ /dev/null 2019-11-18 20:28:11.000000000 -0500 @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -@FunctionalInterface -interface ArgAction { - void execute(); -} --- /dev/null 2019-11-18 20:28:11.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/HTTPHelperException.java 2019-11-18 20:28:06.325053100 -0500 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter; + +import java.io.IOException; + +public class HTTPHelperException extends IOException { + private int responseCode = -1; + + public HTTPHelperException(String msg) { + super(msg); + } + + public void setResponseCode(int code) { + responseCode = code; + } + + public int getResponseCode() { + return responseCode; + } +} --- /dev/null 2019-11-18 20:28:32.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/JNLPConverter.java 2019-11-18 20:28:29.427172000 -0500 @@ -0,0 +1,841 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import jnlp.converter.parser.JNLPDesc; +import jnlp.converter.parser.JNLPDesc.AssociationDesc; +import jnlp.converter.parser.JNLPDesc.IconDesc; +import jnlp.converter.parser.ResourcesDesc.JARDesc; +import jnlp.converter.parser.XMLFormat; + +public class JNLPConverter { + + private final Options options; + private JNLPDesc jnlpd = null; + private final List launchArgs = new ArrayList<>(); + + private String downloadFolder = null; + private String jnlpDownloadFolder = null; + private static String jnlpDownloadFolderStatic; + private String jarDownloadFolder = null; + private String iconDownloadFolder = null; + private String propDownloadFolder = null; + + private static String jpackagePath = null; + + private static boolean markFileToDelete = false; + + private static final String FA_EXTENSIONS = "extension"; + private static final String FA_CONTENT_TYPE = "mime-type"; + private static final String FA_DESCRIPTION = "description"; + private static final String FA_ICON = "icon"; + + public JNLPConverter(Options options) { + this.options = options; + jnlpDownloadFolderStatic = getJnlpDownloadFolder(); + markFileToDelete = (options.keep() == null); + } + + public String [] getLaunchArgs() { + return launchArgs.toArray(new String[0]); + } + + public void convert() { + try { + loadJNLPDesc(); + downloadResources(); + validate(); + buildLaunchArgs(); + saveLaunchArgs(); + runJPackage(); + } catch (Exception ex) { + Log.error(ex.getMessage()); + } + } + + private JNLPDesc getJNLPD(String jnlp) throws Exception { + URL codebase = getCodeBase(jnlp); + byte[] bits = HTTPHelper.getJNLPBits(jnlp, jnlp); + return XMLFormat.parse(bits, codebase, jnlp); + } + + private void loadJNLPDesc() throws Exception { + String jnlp = options.getJNLP(); + jnlpd = getJNLPD(jnlp); + + // Check for required options in case of FX + if (jnlpd.isFXApp()) { + if (!options.isRuntimeImageSet()) { + throw new Exception("This is a JavaFX Web-Start application which requires a runtime image capable of running JavaFX applications, which can be specified by the jpackage option --runtime-image (using --jpackage-options)."); + } + } + + // Check href. It can be same as URL we provided or new one + // if JNLP has different href or codebase. We assume that + // XMLFormat.parse() will handle any errors in href and codebase + // correctly. + String href = jnlpd.getHref(); + if (href != null && !href.equalsIgnoreCase(jnlp)) { + if (href.startsWith("file:")) { + URI hrefURI = new URI(href); + URI jnlpURI = new URI(jnlp); + + String hrefPath = hrefURI.getPath(); + String jnlpPath = jnlpURI.getPath(); + + if (!hrefPath.equalsIgnoreCase(jnlpPath)) { + jnlp = href; + jnlpd = getJNLPD(jnlp); + } + } else { + jnlp = href; + jnlpd = getJNLPD(jnlp); + } + } + + if (jnlpd.getName() == null) { + jnlpd.setName(getNameFromURL(jnlp)); + } + } + + private static String getNameFromURL(String url) throws IOException { + int index; + int index1 = url.lastIndexOf('/'); + int index2 = url.lastIndexOf('\\'); + + if (index1 >= index2) { + index = index1; + } else { + index = index2; + } + + if (index != -1) { + String name = url.substring(index + 1, url.length()); + if (name.endsWith(".jnlp")) { + return name.substring(0, name.length() - 5); + } + } + + return null; + } + + private URL getCodeBase(String jnlp) throws Exception { + int index = jnlp.lastIndexOf('/'); + if (index != -1) { + if (HTTPHelper.isHTTPUrl(jnlp)) { + return new URL(jnlp.substring(0, index + 1)); + } else { + String codeBasePath = jnlp.substring(0, index); + if (!codeBasePath.endsWith("/")) { + codeBasePath += "/"; + } + return new URI(codeBasePath).toURL(); + } + } + + return null; + } + + public static void markFileToDelete(String file) { + if (file == null || file.isEmpty()) { + return; + } + + if (markFileToDelete) { + try { + File f = new File(file); + f.deleteOnExit(); + } catch (Exception e) { + // Print exception, but do not fail conversion. + Log.warning(e.getLocalizedMessage()); + } + } + } + + public static void deleteFile(String file) { + try { + File f = new File(file); + f.delete(); + } catch (Exception e) { + Log.warning(e.getLocalizedMessage()); + } + } + + private void downloadResources() throws Exception { + List jars = jnlpd.getResources(); + for (JARDesc jar : jars) { + if (jar.getVersion() != null) { + if (!jnlpd.isVersionEnabled()) { + throw new Exception("Error: Version based download protocol is not supported without -Djnlp.versionEnabled=true."); + } + } + + String destFile = null; + if (HTTPHelper.isHTTPUrl(jar.getLocation().toString())) { + if (jar.getVersion() != null) { + try { + destFile = HTTPHelper.downloadFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); + } catch (HTTPHelperException ex) { + if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { + System.out.println("Error downloading versioned JAR from " + jar.getVersionLocation()); + System.out.println(ex.getMessage()); + System.out.println("Downloading " + jar.getLocation() + " instead."); + destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); + } else { + throw ex; + } + } + } else { + destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); + } + markFileToDelete(destFile); + } else { + if (jar.getVersion() != null) { + try { + destFile = HTTPHelper.copyFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); + } catch (FileNotFoundException ex) { + System.out.println("Error copying versioned JAR from " + jar.getVersionLocation()); + System.out.println(ex.getMessage()); + System.out.println("Copying " + jar.getLocation() + " instead."); + destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); + } + } else { + destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString())); + } + markFileToDelete(destFile); + } + + if (jar.isNativeLib()) { + unpackNativeLib(destFile); + deleteFile(destFile); + } else { + jnlpd.addFile(jar.getName()); + } + } + + IconDesc icon = jnlpd.getIcon(); + if (icon != null) { + String destFile; + + if (HTTPHelper.isHTTPUrl(icon.getLocation())) { + destFile = HTTPHelper.downloadFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation())); + } else { + destFile = HTTPHelper.copyFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation())); + } + + markFileToDelete(destFile); + icon.setLocalLocation(destFile); + } + + AssociationDesc [] associations = jnlpd.getAssociations(); + if (associations != null) { + for (AssociationDesc association : associations) { + if (association.getIconUrl() != null) { + String destFile; + if (HTTPHelper.isHTTPUrl(association.getIconUrl())) { + destFile = HTTPHelper.downloadFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl())); + } else { + destFile = HTTPHelper.copyFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl())); + } + + markFileToDelete(destFile); + association.setIconLocalLocation(destFile); + } + } + } + } + + public void unpackNativeLib(String file) throws IOException { + try (JarFile jarFile = new JarFile(file)) { + Enumeration entries = jarFile.entries(); + + while (entries.hasMoreElements()) { + JarEntry entry = (JarEntry) entries.nextElement(); + + // Skip directories + if (entry.isDirectory()) { + continue; + } + + String entryName = entry.getName(); + // Skip anything in sub-directories + if (entryName.contains("\\") || entryName.contains("/")) { + continue; + } + + // Skip anything not ending with .dll, .dylib or .so + if (!entryName.endsWith(".dll") && !entryName.endsWith(".dylib") && !entryName.endsWith(".so")) { + continue; + } + + File destFile = new File(getJarDownloadFolder(), entryName); + if (destFile.exists()) { + Log.warning(destFile.getAbsolutePath() + " already exist and will not be overwriten by native library from " + file + "."); + continue; + } + + InputStream inputStream = jarFile.getInputStream(entry); + FileOutputStream outputStream = new FileOutputStream(destFile); + + byte[] buffer = new byte[HTTPHelper.BUFFER_SIZE]; + int length; + do { + length = inputStream.read(buffer); + if (length > 0) { + outputStream.write(buffer, 0, length); + } + } while (length > 0); + + jnlpd.addFile(entryName); + } + } + } + + private void validate() { + if (jnlpd.getMainJar() == null) { + Log.error("Cannot find main jar"); + } + + if (jnlpd.getMainClass() == null) { + Log.error("Cannot find main class"); + } + } + + private void addLaunchArg(String arg, List launchArgs) { + if (arg != null && !arg.isEmpty()) { + if (!options.isOptionPresent(arg)){ + launchArgs.add(arg); + } else { + Log.info(arg + " generated by JNLPConverter is dropped, since it is overwriten via --jpackage-options"); + } + } + } + + private void addLaunchArg(String arg, String value, List launchArgs) { + if (arg != null && !arg.isEmpty() && value != null && !value.isEmpty()) { + if (!options.isOptionPresent(arg)){ + launchArgs.add(arg); + launchArgs.add(value); + } else { + Log.info(arg + "=" + value +" generated by JNLPConverter is dropped, since it is overwriten via --jpackage-options"); + } + } + } + + private void displayLaunchArgs() { + if (Log.isVerbose()) { + System.out.println(); + System.out.println("jpackage launch arguments (each argument starts on new line):"); + launchArgs.forEach((arg) -> { + System.out.println(arg); + }); + } + } + + private static int fileAssociationsCount = 0; + private String getFileAssociationsFile() { + String file = getPropDownloadFolder(); + file += File.separator; + file += "fileAssociation"; + file += String.valueOf(fileAssociationsCount); + file += ".properties"; + + fileAssociationsCount++; + + return file; + } + + private void buildLaunchArgs() { + if (options.createAppImage()) { + addLaunchArg("create-app-image", launchArgs); + } else if (options.createInstaller()) { + if (options.getInstallerType() == null) { + addLaunchArg("create-installer", launchArgs); + } else { + addLaunchArg("create-installer", launchArgs); + if (options.getInstallerType() != null) { + addLaunchArg("--installer-type", options.getInstallerType(), launchArgs); + } + } + } + + // Set verbose for jpackage if it is set for us. + if (options.verbose()) { + addLaunchArg("--verbose", launchArgs); + } + + addLaunchArg("--input", getJarDownloadFolder(), launchArgs); + addLaunchArg("--output", options.getOutput(), launchArgs); + addLaunchArg("--name", jnlpd.getName(), launchArgs); + addLaunchArg("--app-version", jnlpd.getVersion(), launchArgs); + addLaunchArg("--vendor", jnlpd.getVendor(), launchArgs); + addLaunchArg("--description", jnlpd.getDescription(), launchArgs); + addLaunchArg("--icon", jnlpd.getIconLocation(), launchArgs); + addLaunchArg("--main-jar", jnlpd.getMainJar(), launchArgs); + addLaunchArg("--main-class", jnlpd.getMainClass(), launchArgs); + + addArguments(launchArgs); + addJVMArgs(launchArgs); + + if (options.createInstaller()) { + if (jnlpd.isDesktopHint()) { + if (Platform.isWindows()) { + addLaunchArg("--win-shortcut", launchArgs); + } else { + Log.warning("Ignoring shortcut hint, since it is not supported on current platform."); + } + } + + if (jnlpd.isMenuHint()) { + if (Platform.isWindows()) { + addLaunchArg("--win-menu", launchArgs); + addLaunchArg("--win-menu-group", jnlpd.getSubMenu(), launchArgs); + } else { + Log.warning("Ignoring menu hint, since it is not supported on current platform."); + } + } + + AssociationDesc[] associations = jnlpd.getAssociations(); + if (associations != null) { + for (AssociationDesc association : associations) { + String file = getFileAssociationsFile(); + markFileToDelete(file); + + try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) { + if (association.getExtensions() != null && association.getMimeType() != null) { + out.println(FA_EXTENSIONS + "=" + quote(association.getExtensions())); + out.println(FA_CONTENT_TYPE + "=" + quote(association.getMimeType())); + + if (association.getMimeDescription() != null) { + out.println(FA_DESCRIPTION + "=" + association.getMimeDescription()); + } + + if (association.getIconLocalLocation() != null) { + out.println(FA_ICON + "=" + quote(association.getIconLocalLocation())); + } + + addLaunchArg("--file-associations", file, launchArgs); + } + } catch (Exception ex) { + Log.warning(ex.toString()); + if (association.getExtensions() != null) { + Log.warning("File assoication for " + association.getExtensions() + " will be ignored due to exception above."); + } + } + } + } + } + + // Add options from --jpackage-options + List jpackageOptions = options.getJPackageOptions(); + jpackageOptions.forEach((option) -> { + launchArgs.add(option); + }); + + displayLaunchArgs(); + } + + private String getCommandFileName() { + Platform platform = Platform.getPlatform(); + switch (platform) { + case WINDOWS: + return "run_jpackage.bat"; + case LINUX: + return "run_jpackage.sh"; + case MAC: + return "run_jpackage.sh"; + default: + Log.error("Cannot determine platform type."); + return ""; + } + } + + private void saveLaunchArgs() { + if (options.keep() != null) { + File keepFolder = new File(options.keep()); + String cmdFile = keepFolder.getAbsolutePath() + File.separator + getCommandFileName(); + try (PrintWriter out = new PrintWriter(cmdFile)) { + out.print(getJPackagePath()); + launchArgs.forEach((arg) -> { + out.print(" "); + + if (arg.contains(" ")) { + int len = arg.length(); + if (len >= 1) { + if (arg.charAt(0) != '"' && arg.charAt(len - 1) != '"') { + out.print("\"" + arg + "\""); + } else { + if (Platform.isWindows()) { + out.print(arg); + } else { + arg = escapeQuote(arg); + out.print("\"" + arg + "\""); + } + } + } + } else { + out.print(arg); + } + }); + } catch (FileNotFoundException ex) { + Log.error("Cannot save file with command line: " + ex.getLocalizedMessage()); + } + } + } + + private void runJPackage() { + List command = new ArrayList<>(); + command.add(getJPackagePath()); + command.addAll(launchArgs); + + ProcessBuilder builder = new ProcessBuilder(); + builder.inheritIO(); + builder.command(command); + + try { + Process process = builder.start(); + int exitCode = process.waitFor(); + if (exitCode != 0) { + Log.warning("jpackage retrun non zero code: " + exitCode); + } + } catch (IOException | InterruptedException ex) { + Log.error(ex.getMessage()); + } + } + + private void addArguments(List launchArgs) { + List arguments = jnlpd.getArguments(); + if (arguments.isEmpty()) { + return; + } + + String argsStr = ""; + for (int i = 0; i < arguments.size(); i++) { + String arg = arguments.get(i); + argsStr += quote(arg); + if ((i + 1) != arguments.size()) { + argsStr += " "; + } + } + + launchArgs.add("--arguments"); + if (Platform.isWindows()) { + if (argsStr.contains(" ")) { + if (argsStr.contains("\"")) { + argsStr = escapeQuote(argsStr); + } + argsStr = "\"" + argsStr + "\""; + } + } + launchArgs.add(argsStr); + } + + private void addJVMArgs(List launchArgs) { + List jvmArgs = jnlpd.getVMArgs(); + if (jvmArgs.isEmpty()) { + return; + } + + String jvmArgsStr = ""; + for (int i = 0; i < jvmArgs.size(); i++) { + String arg = jvmArgs.get(i); + jvmArgsStr += quote(arg); + if ((i + 1) != jvmArgs.size()) { + jvmArgsStr += " "; + } + } + + launchArgs.add("--java-options"); + if (Platform.isWindows()) { + if (jvmArgsStr.contains(" ")) { + if (jvmArgsStr.contains("\"")) { + jvmArgsStr = escapeQuote(jvmArgsStr); + } + jvmArgsStr = "\"" + jvmArgsStr + "\""; + } + } + launchArgs.add(jvmArgsStr); + } + + private String quote(String in) { + if (in == null) { + return null; + } + + if (in.isEmpty()) { + return ""; + } + + if (!in.contains("=")) { + // Not a property + if (in.contains(" ")) { + in = escapeQuote(in); + return "\"" + in + "\""; + } + return in; + } + + if (!in.contains(" ")) { + return in; // No need to quote + } + + int paramIndex = in.indexOf("="); + if (paramIndex <= 0) { + return in; // Something wrong, just skip quoting + } + + String param = in.substring(0, paramIndex); + String value = in.substring(paramIndex + 1); + + if (value.length() == 0) { + return in; // No need to quote + } + + value = escapeQuote(value); + + return param + "=" + "\"" + value + "\""; + } + + private String escapeQuote(String in) { + if (in == null) { + return null; + } + + if (in.isEmpty()) { + return ""; + } + + if (in.contains("\"")) { + // Use code points to preserve non-ASCII chars + StringBuilder sb = new StringBuilder(); + int codeLen = in.codePointCount(0, in.length()); + for (int i = 0; i < codeLen; i++) { + int code = in.codePointAt(i); + // Note: No need to escape '\' on Linux or OS X. + // jpackage expects us to pass arguments and properties with quotes and spaces as a map + // with quotes being escaped with additional \ for internal quotes. + // So if we want two properties below: + // -Djnlp.Prop1=Some "Value" 1 + // -Djnlp.Prop2=Some Value 2 + // jpackage will need: + // "-Djnlp.Prop1=\"Some \\"Value\\" 1\" -Djnlp.Prop2=\"Some Value 2\"" + // but since we using ProcessBuilder to run jpackage we will need to escape + // our escape symbols as well, so we will need to pass string below to ProcessBuilder: + // "-Djnlp.Prop1=\\\"Some \\\\\\\"Value\\\\\\\" 1\\\" -Djnlp.Prop2=\\\"Some Value 2\\\"" + switch (code) { + case '"': + // " -> \" -> \\\" + if (i == 0 || in.codePointAt(i - 1) != '\\') { + sb.appendCodePoint('\\'); + sb.appendCodePoint(code); + } + break; + case '\\': + // We need to escape already escaped symbols as well + if ((i + 1) < codeLen) { + int nextCode = in.codePointAt(i + 1); + if (nextCode == '"') { + // \" -> \\\" + sb.appendCodePoint('\\'); + sb.appendCodePoint('\\'); + sb.appendCodePoint('\\'); + sb.appendCodePoint(nextCode); + } else { + sb.appendCodePoint('\\'); + sb.appendCodePoint(code); + } + } else { + sb.appendCodePoint(code); + } + break; + default: + sb.appendCodePoint(code); + break; + } + } + return sb.toString(); + } + + return in; + } + + public synchronized String getDownloadFolder() { + if (downloadFolder == null) { + try { + File file; + if (options.keep() == null) { + Path path = Files.createTempDirectory("JNLPConverter"); + file = path.toFile(); + file.deleteOnExit(); + } else { + file = new File(options.keep()); + if (!file.exists()) { + file.mkdir(); + } + } + + downloadFolder = file.getAbsolutePath(); + } catch (IOException e) { + Log.error(e.getMessage()); + } + } + + return downloadFolder; + } + + public final synchronized String getJnlpDownloadFolder() { + if (jnlpDownloadFolder == null) { + File file = new File(getDownloadFolder() + File.separator + "jnlp"); + file.mkdir(); + markFileToDelete(getDownloadFolder() + File.separator + "jnlp"); + jnlpDownloadFolder = file.getAbsolutePath(); + } + + return jnlpDownloadFolder; + } + + public static String getJnlpDownloadFolderStatic() { + return jnlpDownloadFolderStatic; + } + + public synchronized String getJarDownloadFolder() { + if (jarDownloadFolder == null) { + File file = new File(getDownloadFolder() + File.separator + "jar"); + file.mkdir(); + markFileToDelete(getDownloadFolder() + File.separator + "jar"); + jarDownloadFolder = file.getAbsolutePath(); + } + + return jarDownloadFolder; + } + + public synchronized String getIconDownloadFolder() { + if (iconDownloadFolder == null) { + File file = new File(getDownloadFolder() + File.separator + "icon"); + file.mkdir(); + markFileToDelete(getDownloadFolder() + File.separator + "icon"); + iconDownloadFolder = file.getAbsolutePath(); + } + + return iconDownloadFolder; + } + + public synchronized String getPropDownloadFolder() { + if (propDownloadFolder == null) { + File file = new File(getDownloadFolder() + File.separator + "prop"); + file.mkdir(); + markFileToDelete(getDownloadFolder() + File.separator + "prop"); + propDownloadFolder = file.getAbsolutePath(); + } + + return propDownloadFolder; + } + + public synchronized static String getJPackagePath() { + if (jpackagePath == null) { + jpackagePath = System.getProperty("java.home"); + jpackagePath += File.separator; + jpackagePath += "bin"; + jpackagePath += File.separator; + + Platform platform = Platform.getPlatform(); + switch (platform) { + case WINDOWS: + jpackagePath += "jpackage.exe"; + break; + case LINUX: + jpackagePath += "jpackage"; + break; + case MAC: + jpackagePath += "jpackage"; + break; + default: + Log.error("Cannot determine platform type."); + break; + } + + Log.verbose("jpackage: " + jpackagePath); + } + + return jpackagePath; + } + + public static String getIconFormat(String icon) { + // GIF, JPEG, ICO, or PNG + if (icon.toLowerCase().endsWith(".gif")) { + return "GIF"; + } else if (icon.toLowerCase().endsWith(".jpg")) { + return "JPEG"; + } else if (icon.toLowerCase().endsWith(".ico")) { + return "ICO"; + } else if (icon.toLowerCase().endsWith(".png")) { + return "PNG"; + } + + return "UNKNOWN"; + } + + public static boolean isIconSupported(String icon) { + Platform platform = Platform.getPlatform(); + switch (platform) { + case WINDOWS: + if (icon.endsWith(".ico")) { + return true; + } else { + Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Windows for file " + icon + "."); + return false; + } + case LINUX: + if (icon.endsWith(".png")) { + return true; + } else { + Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Linux for file " + icon + "."); + return false; + } + case MAC: + Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on OS X for file " + icon + "."); + return false; + } + + return false; + } +} --- /dev/null 2019-11-18 20:28:44.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Log.java 2019-11-18 20:28:41.069153900 -0500 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ +package jnlp.converter; + +public class Log { + private static boolean verbose = false; + + public static void setVerbose(boolean verbose) { + Log.verbose = verbose; + } + + public static boolean isVerbose() { + return verbose; + } + + public static void verbose(String msg) { + if (verbose) { + System.out.println(msg); + } + } + + public static void info(String msg) { + System.out.println("Info: " + msg); + } + + public static void warning(String msg) { + System.err.println("Warning: " + msg); + } + + public static void error(String msg) { + System.err.println("Error: " + msg); + System.exit(1); + } +} --- /dev/null 2019-11-18 20:28:55.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Main.java 2019-11-18 20:28:52.499071700 -0500 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter; + +import java.io.File; + +public class Main { + + private static void showHelp() { + Options.showHelp(); + } + + private static void showVersion() { + System.out.println("Version: 1.0"); + } + + private static void createBundle(Options options) { + Log.verbose("Creating bundle for JNLP: " + options.getJNLP()); + Log.verbose("Output folder: " + options.getOutput()); + + JNLPConverter converter = new JNLPConverter(options); + converter.convert(); + } + + private static void validateJDK() { + String jpackagePath = JNLPConverter.getJPackagePath(); + File file = new File(jpackagePath); + if (!file.exists()) { + Log.error("Cannot find " + jpackagePath + ". Make sure you running JNLPConverter with supported JDK version."); + } + } + + public static void main(String[] args) { + Options options = Options.parseArgs(args); // Only valid options will be returned + + Log.setVerbose(options.verbose()); + + validateJDK(); + + if (options.help()) { + showHelp(); + } else if (options.version()) { + showVersion(); + } else { + createBundle(options); + } + + System.exit(0); + } +} --- /dev/null 2019-11-18 20:29:07.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Options.java 2019-11-18 20:29:04.080853800 -0500 @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class Options { + + private boolean createAppImage = false; + private boolean createInstaller = false; + private String installerType = null; + private String jnlp = null; + private String output = null; + private String keep = null; + private boolean help = false; + private boolean verbose = false; + private boolean version = false; + private final List jpackageOptions = new ArrayList<>(); + private boolean isRuntimeImageSet = false; + + private static final String JNLP_OPTION_PREFIX = "--jnlp="; + private static final String OUTPUT_OPTION_PREFIX = "--output="; + private static final String KEEP_OPTION_PREFIX = "--keep="; + private static final String JNLP_OPTION_SHORT_PREFIX = "-j"; + private static final String OUTPUT_OPTION_SHORT_PREFIX = "-o"; + private static final String KEEP_OPTION_SHORT_PREFIX = "-k"; + + private static final String [] INSTALLER_TYPES = {"msi", "exe", "dmg", "pkg", + "rpm", "deb"}; + + // --output, -o, --input, -i, --main-jar, --main-class + private static final String [] BLOCKED_JPACKAGE_OPTIONS = {"--output", "-o", "--input", "-i", + "--main-jar", "--main-class"}; + + private static final String RUNTIME_IMAGE_OPTION = "--runtime-image"; + + private static final String ERR_UNKNOWN_OPTION = "Unknown option: "; + private static final String ERR_MISSING_VALUE = "Value is required for option "; + private static final String ERR_MISSING_MODE = "Error: create-app-image or create-installer mode is required"; + private static final String ERR_MISSING_JNLP = "Error: --jnlp is required"; + private static final String ERR_MISSING_OUTPUT = "Error: --output is required"; + private static final String ERR_OUTPUT_EXISTS = "Error: output folder already exists"; + private static final String ERR_KEEP_EXISTS = "Error: folder for --keep argument already exists"; + private static final String ERR_INVALID_PROTOCOL_JNLP = "Error: Invalid protocol for JNLP file. Only HTTP, HTTPS and FILE protocols are supported."; + + public boolean createAppImage() { + return createAppImage; + } + + public boolean createInstaller() { + return createInstaller; + } + + public String getInstallerType() { + return installerType; + } + + public String getJNLP() { + return jnlp; + } + + public String getOutput() { + return output; + } + + public String keep() { + return keep; + } + + public boolean help() { + return help; + } + + public boolean verbose() { + return verbose; + } + + public boolean version() { + return version; + } + + public List getJPackageOptions() { + return jpackageOptions; + } + + public boolean isRuntimeImageSet() { + return isRuntimeImageSet; + } + + // Helper method to dump all options + private void display() { + System.out.println("Options:"); + System.out.println("createAppImage: " + createAppImage); + System.out.println("createInstaller: " + createInstaller); + System.out.println("installerType: " + installerType); + System.out.println("jnlp: " + jnlp); + System.out.println("output: " + output); + System.out.println("keep: " + keep); + System.out.println("help: " + help); + System.out.println("verbose: " + verbose); + System.out.println("version: " + version); + for (int i = 0; i < jpackageOptions.size(); i++) { + System.out.println("jpackageOptions[" + i + "]: " + jpackageOptions.get(i)); + } + } + + private void validate() { + if (help || version) { + return; + } + + if (!createAppImage && !createInstaller) { + optionError(ERR_MISSING_MODE); + } + + if (jnlp == null) { + optionError(ERR_MISSING_JNLP); + } else { + int index = jnlp.indexOf(":"); + if (index == -1 || index == 0) { + optionError(ERR_INVALID_PROTOCOL_JNLP); + } else { + String protocol = jnlp.substring(0, index); + if (!protocol.equalsIgnoreCase("http") && + !protocol.equalsIgnoreCase("https") && + !protocol.equalsIgnoreCase("file")) { + optionError(ERR_INVALID_PROTOCOL_JNLP); + } + } + } + + if (output == null) { + optionError(ERR_MISSING_OUTPUT); + } else { + File file = new File(output); + if (file.exists()) { + optionErrorNoHelp(ERR_OUTPUT_EXISTS); + } + } + + if (keep != null) { + File file = new File(keep); + if (file.exists()) { + optionErrorNoHelp(ERR_KEEP_EXISTS); + } + } + + jpackageOptions.forEach((option) -> { + if (isBlockedOption(option)) { + Log.error(option + " is not allowed via --jpackage-options, since it will conflict with " + + "same option generated by JNLPConverter."); + } + }); + } + + public boolean isOptionPresent(String option) { + for (String jpackageOption : jpackageOptions) { + if (jpackageOption.equalsIgnoreCase(option)) { + return true; + } + } + + return false; + } + + private boolean isBlockedOption(String option) { + for (String blockedOption : BLOCKED_JPACKAGE_OPTIONS) { + if (blockedOption.equalsIgnoreCase(option)) { + return true; + } + } + + return false; + } + + public static void showHelp() { +// System.out.println("********* Help should not be longer then 80 characters as per JEP-293 *********"); + System.out.println("Usage: java -jar JNLPConverter.jar "); + System.out.println(""); + System.out.println("where mode is one of:"); + System.out.println(" create-app-image"); + System.out.println(" Generates a platform-specific application image."); + System.out.println(" create-installer"); + System.out.println(" Generates a platform-specific installer for the application."); + System.out.println(""); + System.out.println("Possible options include:"); + System.out.println(" -j, --jnlp "); + System.out.println(" Full path to JNLP file. Supported protocols are HTTP/HTTPS/FILE."); + System.out.println(" -o, --output "); + System.out.println(" Name of the directory where generated output files are placed."); + System.out.println(" -k, --keep "); + System.out.println(" Keep JNLP, JARs and command line arguments for jpackage"); + System.out.println(" in directory provided."); + System.out.println(" --jpackage-options "); + System.out.println(" Specify additional jpackage options or overwrite provided by JNLPConverter."); + System.out.println(" All jpackage options can be specified except: --output -o, --input -i,"); + System.out.println(" --main-jar -j and --class -c."); + System.out.println(" --installer-type "); + System.out.println(" The type of the installer to create"); + System.out.println(" Valid values are: {\"exe\", \"msi\", \"rpm\", \"deb\", \"pkg\", \"dmg\"}"); + System.out.println(" If this option is not specified (in create-installer mode) all"); + System.out.println(" supported types of installable packages for the current"); + System.out.println(" platform will be created."); + System.out.println(" -h, --help, -?"); + System.out.println(" Print this help message"); + System.out.println(" -v, --verbose"); + System.out.println(" Enable verbose output."); + System.out.println(" --version"); + System.out.println(" Version information."); + System.out.println("To specify an argument for a long option, you can use --= or"); + System.out.println("-- ."); + System.out.println("To specify proxy server use standard Java properties http.proxyHost and http.proxyPort."); + } + + private static boolean isInstallerType(String type) { + for (String installerType : INSTALLER_TYPES) { + if (installerType.equals(type)) { + return true; + } + } + + return false; + } + + public static Options parseArgs(String[] args) { + Options options = new Options(); + + int index = 0; + if (args.length >= 1) { + switch (args[0]) { + case "create-app-image": + options.createAppImage = true; + index = 1; + break; + case "create-installer": + options.createInstaller = true; + index = 1; + break; + case "-h": + case "--help": + case "-?": + case "--version": + break; + default: + optionError(Options.ERR_MISSING_MODE); + break; + } + } + + for (int i = index; i < args.length; i++) { + String arg = args[i]; + + if (arg.equals("--jnlp")) { + if (++i >= args.length) { + optionError(Options.ERR_MISSING_VALUE, "--jnlp"); + } + options.jnlp = args[i]; + } else if (arg.startsWith(JNLP_OPTION_PREFIX)) { + options.jnlp = arg.substring(JNLP_OPTION_PREFIX.length()); + } else if (arg.equals("--output")) { + if (++i >= args.length) { + optionError(Options.ERR_MISSING_VALUE, "--output"); + } + options.output = args[i]; + } else if (arg.startsWith(OUTPUT_OPTION_PREFIX)) { + options.output = arg.substring(OUTPUT_OPTION_PREFIX.length()); + } else if (arg.equals("--keep")) { + if (++i >= args.length) { + optionError(Options.ERR_MISSING_VALUE, "--keep"); + } + options.keep = args[i]; + } else if (arg.startsWith(KEEP_OPTION_PREFIX)) { + options.keep = arg.substring(KEEP_OPTION_PREFIX.length()); + } else if (arg.equals("--help")) { + options.help = true; + } else if (arg.equals("--verbose")) { + options.verbose = true; + } else if (arg.equals("--version")) { + options.version = true; + } else if (arg.equals("-j")) { // short options + if (++i >= args.length) { + optionError(Options.ERR_MISSING_VALUE, "-j"); + } + options.jnlp = args[i]; + } else if (arg.startsWith(JNLP_OPTION_SHORT_PREFIX)) { + options.jnlp = arg.substring(JNLP_OPTION_SHORT_PREFIX.length()); + } else if (arg.equals("-o")) { + if (++i >= args.length) { + optionError(Options.ERR_MISSING_VALUE, "-o"); + } + options.output = args[i]; + } else if (arg.startsWith(OUTPUT_OPTION_SHORT_PREFIX)) { + options.output = arg.substring(OUTPUT_OPTION_SHORT_PREFIX.length()); + } else if (arg.equals("-k")) { + if (++i >= args.length) { + optionError(Options.ERR_MISSING_VALUE, "-k"); + } + options.keep = args[i]; + } else if (arg.startsWith(KEEP_OPTION_SHORT_PREFIX)) { + options.keep = arg.substring(KEEP_OPTION_SHORT_PREFIX.length()); + } else if (arg.equals("-h") || arg.equals("-?")) { + options.help = true; + } else if (arg.equals("-v")) { + options.verbose = true; + } else if (arg.equals("--jpackage-options")) { + for (i = (i + 1); i < args.length; i++) { + if (!options.isRuntimeImageSet) { + if (args[i].equals(RUNTIME_IMAGE_OPTION)) { + options.isRuntimeImageSet = true; + } + } + options.jpackageOptions.add(args[i]); + } + } else if (arg.equals("--installer-type")) { + if ((i + 1) < args.length) { + if (isInstallerType(args[i + 1])) { + options.installerType = args[i + 1]; + i++; + } + } + } else { + optionError(ERR_UNKNOWN_OPTION, arg); + } + } + + //options.display(); // For testing only + options.validate(); + + return options; + } + + private static void optionErrorNoHelp(String msg) { + System.out.println(msg); + System.exit(1); + } + + private static void optionError(String msg) { + System.out.println(msg); + System.out.println(); + showHelp(); + System.exit(1); + } + + private static void optionError(String msg, String option) { + System.out.println(msg + option); + System.out.println(); + showHelp(); + System.exit(1); + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Platform.java 2019-11-18 20:29:19.481991800 -0500 +++ /dev/null 2019-11-18 20:29:21.000000000 -0500 @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.regex.Pattern; - -/** - * Platform - * - * Use Platform to detect the operating system - * that is currently running. - * - * Example: - * - * Platform platform = Platform.getPlatform(); - * - * switch(platform) { - * case Platform.MAC: { - * // Do something - * break; - * } - * case Platform.WINDOWS: - * case Platform.LINUX: { - * // Do something else - * } - * } - * - */ -enum Platform {UNKNOWN, WINDOWS, LINUX, MAC; - private static final Platform platform; - private static final int majorVersion; - private static final int minorVersion; - - static { - String os = System.getProperty("os.name").toLowerCase(); - - if (os.indexOf("win") >= 0) { - platform = Platform.WINDOWS; - } - else if (os.indexOf("nix") >= 0 || os.indexOf("nux") >= 0) { - platform = Platform.LINUX; - } - else if (os.indexOf("mac") >= 0) { - platform = Platform.MAC; - } - else { - platform = Platform.UNKNOWN; - } - - String version = System.getProperty("os.version").toString(); - String[] parts = version.split(Pattern.quote(".")); - - if (parts.length > 0) { - majorVersion = Integer.parseInt(parts[0]); - - if (parts.length > 1) { - minorVersion = Integer.parseInt(parts[1]); - } - else { - minorVersion = -1; - } - } - else { - majorVersion = -1; - minorVersion = -1; - } - } - - private Platform() {} - - static Platform getPlatform() { - return platform; - } - - static int getMajorVersion() { - return majorVersion; - } - - static int getMinorVersion() { - return minorVersion; - } -} --- /dev/null 2019-11-18 20:29:21.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Platform.java 2019-11-18 20:29:15.625518400 -0500 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter; + +import java.util.regex.Pattern; + +public enum Platform { + UNKNOWN, WINDOWS, LINUX, MAC; + private static final Platform platform; + private static final int majorVersion; + private static final int minorVersion; + + static { + String os = System.getProperty("os.name").toLowerCase(); + + if (os.contains("win")) { + platform = Platform.WINDOWS; + } else if (os.contains("nix") || os.contains("nux")) { + platform = Platform.LINUX; + } else if (os.contains("mac")) { + platform = Platform.MAC; + } else { + platform = Platform.UNKNOWN; + } + + String version = System.getProperty("os.version"); + String[] parts = version.split(Pattern.quote(".")); + + if (parts.length > 0) { + majorVersion = Integer.parseInt(parts[0]); + + if (parts.length > 1) { + minorVersion = Integer.parseInt(parts[0]); + } else { + minorVersion = -1; + } + } else { + majorVersion = -1; + minorVersion = -1; + } + } + + private Platform() { + } + + public static Platform getPlatform() { + return platform; + } + + public static boolean isWindows() { + return (platform == Platform.WINDOWS); + } + + public static int getMajorVersion() { + return majorVersion; + } + + public static int getMinorVersion() { + return minorVersion; + } +} --- /dev/null 2019-11-18 20:29:41.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/GeneralUtil.java 2019-11-18 20:29:38.588992200 -0500 @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser; + +import java.util.Locale; +import java.util.ArrayList; +import java.util.StringTokenizer; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Handy class to add some utility methods for dealing with property matching + * etc. + */ +public class GeneralUtil { + + public static boolean prefixMatchStringList(String[] prefixList, String target) { + // No prefixes matches everything + if (prefixList == null) { + return true; + } + // No target, but a prefix list does not match anything + if (target == null) { + return false; + } + for (String prefix : prefixList) { + if (target.startsWith(prefix)) { + return true; + } + } + return false; + } + + private static String getOSArch() { + return System.getProperty("os.arch"); + } + + public static boolean prefixMatchArch(String[] prefixList) { + // No prefixes matches everything + if (prefixList == null) { + return true; + } + + // check for the current arch + String arch = getOSArch(); + for (String prefix : prefixList) { + if (arch.startsWith(prefix)) { + return true; + } + } + + return false; + } + + /** + * Converts a space delimited string to a list of strings + */ + public static String[] getStringList(String str) { + if (str == null) { + return null; + } + ArrayList list = new ArrayList<>(); + int i = 0; + int length = str.length(); + StringBuffer sb = null; + while (i < length) { + char ch = str.charAt(i); + switch (ch) { + case ' ': + // A space was hit. Add string to list + if (sb != null) { + list.add(sb.toString()); + sb = null; + } + break; + case '\\': + // It is a delimiter. Add next character + if (i + 1 < length) { + ch = str.charAt(++i); + if (sb == null) { + sb = new StringBuffer(); + } + sb.append(ch); + } + break; + default: + if (sb == null) { + sb = new StringBuffer(); + } sb.append(ch); + break; + } + i++; // Next character + } + // Make sure to add the last part to the list too + if (sb != null) { + list.add(sb.toString()); + } + if (list.isEmpty()) { + return null; + } + String[] results = new String[list.size()]; + return list.toArray(results); + } + + /** + * Checks if string list matches default locale + */ + public static boolean matchLocale(String[] localeList, Locale locale) { + // No locale specified matches everything + if (localeList == null) { + return true; + } + for (String localeList1 : localeList) { + if (matchLocale(localeList1, locale)) { + return true; + } + } + return false; + } + + /** + * Checks if string matches default locale + */ + public static boolean matchLocale(String localeStr, Locale locale) { + if (localeStr == null || localeStr.length() == 0) { + return true; + } + + // Compare against default locale + String language; + String country; + String variant; + + // The locale string is of the form language_country_variant + StringTokenizer st = new StringTokenizer(localeStr, "_", false); + if (st.hasMoreElements() && locale.getLanguage().length() > 0) { + language = st.nextToken(); + if (!language.equalsIgnoreCase(locale.getLanguage())) { + return false; + } + } + if (st.hasMoreElements() && locale.getCountry().length() > 0) { + country = st.nextToken(); + if (!country.equalsIgnoreCase(locale.getCountry())) { + return false; + } + } + if (st.hasMoreElements() && locale.getVariant().length() > 0) { + variant = st.nextToken(); + if (!variant.equalsIgnoreCase(locale.getVariant())) { + return false; + } + } + + return true; + } + + public static long heapValToLong(String heapValue) { + if (heapValue == null) { + return -1; + } + long multiplier = 1; + if (heapValue.toLowerCase().lastIndexOf('m') != -1) { + // units are megabytes, 1 megabyte = 1024 * 1024 bytes + multiplier = 1024 * 1024; + heapValue = heapValue.substring(0, heapValue.length() - 1); + } else if (heapValue.toLowerCase().lastIndexOf('k') != -1) { + // units are kilobytes, 1 kilobyte = 1024 bytes + multiplier = 1024; + heapValue = heapValue.substring(0, heapValue.length() - 1); + } + long theValue; + try { + theValue = Long.parseLong(heapValue); + theValue = theValue * multiplier; + } catch (NumberFormatException e) { + theValue = -1; + } + return theValue; + } + + public static byte[] readBytes(InputStream is, long size) throws IOException { + // Sanity on file size (restrict to 1M) + if (size > 1024 * 1024) { + throw new IOException("File too large"); + } + + BufferedInputStream bis; + if (is instanceof BufferedInputStream) { + bis = (BufferedInputStream) is; + } else { + bis = new BufferedInputStream(is); + } + + if (size <= 0) { + size = 10 * 1024; // Default to 10K + } + byte[] b = new byte[(int) size]; + int n; + int bytesRead = 0; + n = bis.read(b, bytesRead, b.length - bytesRead); + while (n != -1) { + bytesRead += n; + // Still room in array + if (b.length == bytesRead) { + byte[] bb = new byte[b.length * 2]; + System.arraycopy(b, 0, bb, 0, b.length); + b = bb; + } + // Read next line + n = bis.read(b, bytesRead, b.length - bytesRead); + } + bis.close(); + is.close(); + + if (bytesRead != b.length) { + byte[] bb = new byte[bytesRead]; + System.arraycopy(b, 0, bb, 0, bytesRead); + b = bb; + } + return b; + } + + public static String getOSFullName() { + return System.getProperty("os.name"); + } + + /** + * Makes sure a URL is a path URL, i.e., ends with '/' + */ + public static URL asPathURL(URL url) { + if (url == null) { + return null; + } + + String path = url.getFile(); + if (path != null && !path.endsWith("/")) { + try { + return new URL(url.getProtocol(), + url.getHost(), + url.getPort(), + url.getFile() + "/"); + } catch (MalformedURLException mue) { + // Should not happen + } + } + // Just return same URl + return url; + } + + public static Locale getDefaultLocale() { + return Locale.getDefault(); + } + + public static String toNormalizedString(URL u) { + if (u == null) { + return ""; + } + + try { + if (u.getPort() == u.getDefaultPort()) { + u = new URL(u.getProtocol().toLowerCase(), + u.getHost().toLowerCase(), -1, u.getFile()); + } else { + u = new URL(u.getProtocol().toLowerCase(), + u.getHost().toLowerCase(), u.getPort(), u.getFile()); + } + } catch (MalformedURLException ex) { + } + return u.toExternalForm(); + } + + public static boolean sameURLs(URL u1, URL u2) { + if (u1 == null || u2 == null || (u1 == u2)) { + return (u1 == u2); + } + //NB: do not use URL.sameFile() as it will do DNS lookup + // Also, do quick check before slow string comparisons + String f1 = u1.getFile(); + String f2 = u2.getFile(); + return (f1.length() == f2.length()) && sameBase(u1, u2) + && f1.equalsIgnoreCase(f2); + } + + public static boolean sameBase(URL u1, URL u2) { + return u1 != null && u2 != null && + sameHost(u1, u2) && samePort(u1, u2) && sameProtocol(u1, u2); + } + + private static boolean sameProtocol(URL u1, URL u2) { + //protocols are known to be lowercase + return u1.getProtocol().equals(u2.getProtocol()); + } + + private static boolean sameHost(URL u1, URL u2) { + String host = u1.getHost(); + String otherHost = u2.getHost(); + if (host == null || otherHost == null) { + return (host == null && otherHost == null); + } else { + //avoid slow comparison for strings of different length + return ((host.length() == otherHost.length()) + && host.equalsIgnoreCase(otherHost)); + } + } + + private static boolean samePort(URL u1, URL u2) { + return getPort(u1) == getPort(u2); + } + + public static int getPort(URL u) { + if (u.getPort() != -1) { + return u.getPort(); + } else { + return u.getDefaultPort(); + } + } + + public static URL getBase(URL url) { + if (url == null) return null; + String file = url.getFile(); + if (file != null) { + int idx = file.lastIndexOf('/'); + if (idx != -1 ) { + file = file.substring(0, idx + 1); + } + try { + return new URL( + url.getProtocol(), + url.getHost(), + url.getPort(), + file); + } catch(MalformedURLException mue) { + System.err.println(mue.getMessage()); + } + } + // Just return same URL + return url; + } + + private static String getEmbeddedVersionPath(String path, String version) { + int index = path.lastIndexOf("/"); + String filename = path.substring(index + 1); + path = path.substring(0, index + 1); + + String ext = null; + index = filename.lastIndexOf("."); + if (index != -1) { + ext = filename.substring(index + 1); + filename = filename.substring(0, index); + } + + StringBuilder filenameSB = new StringBuilder(filename); + if (version != null) { + filenameSB.append("__V"); + filenameSB.append(version); + } + if (ext != null) { + filenameSB.append("."); + filenameSB.append(ext); + } + + path += filenameSB.toString(); + return path; + } + + public static URL getEmbeddedVersionURL(URL u, String version) throws Exception { + if (u == null) { + return null; + } + + if (version == null || version.indexOf("*") != -1 + || version.indexOf("+") != -1) { + // Do not support * or + in version string + return u; + } + + URL versionURL = null; + + String protocol = u.getProtocol(); + String host = u.getHost(); + int port = u.getPort(); + String path = u.getPath(); + + path = getEmbeddedVersionPath(path, version); + + versionURL = new URL(protocol, host, port, path); + + return versionURL; + } +} --- /dev/null 2019-11-18 20:29:53.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/JNLPDesc.java 2019-11-18 20:29:49.850515900 -0500 @@ -0,0 +1,617 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import jnlp.converter.Log; + +import jnlp.converter.parser.ResourcesDesc.JARDesc; +import jnlp.converter.parser.ResourcesDesc.JREDesc; + +public class JNLPDesc { + private String specVersion = null; + private String codebase = null; + private String version = null; + private String href = null; + private String name = null; + private String title = null; + private String vendor = null; + private String mainJar = null; + private String [] descriptions = null; + private IconDesc [] icons = null; + private ShortcutDesc shortcuts = null; + private AssociationDesc [] associations = null; + private String mainClass = null; + private final List arguments = new ArrayList<>(); + private final List files = new ArrayList<>(); + private final List resources = new ArrayList<>(); + private final List vmArgs = new ArrayList<>(); + private boolean isLibrary = false; + private boolean isInstaller = false; + private boolean isJRESet = false; + private ResourcesDesc resourcesDesc; + private boolean isVersionEnabled = false; + private boolean isSandbox = true; + private boolean isFXApp = false; + + public void setSpecVersion(String specVersion) { + this.specVersion = specVersion; + + // Valid values are 1.0, 1.5, 6.0, 6.0.10, 6.0.18, 7.0, 8.20, 9 or a wildcard such as 1.0+. + if (!specVersion.startsWith("1.0") && + !specVersion.startsWith("1.5") && + !specVersion.startsWith("6.0") && + !specVersion.startsWith("6.0.10") && + !specVersion.startsWith("6.0.18") && + !specVersion.startsWith("7.0") && + !specVersion.startsWith("8.20") && + !specVersion.startsWith("9")) { + System.out.println("Warning: Invalid version of the JNLP specification found: " + + specVersion + ". Valid values are 1.0, 1.5, 6.0, 6.0.10, 6.0.18, 7.0," + + " 8.20, 9 or a wildcard such as 1.0+."); + } + } + + public String getSpecVersion() { + return specVersion; + } + + public void setCodebase(String codebase) { + this.codebase = codebase; + } + + public String getCodebase() { + return codebase; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getVersion() { + return version; + } + + public void setHref(String href) { + this.href = href; + } + + public String getHref() { + return href; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getTitle() { + return title; + } + + public void setVendor(String vendor) { + this.vendor = vendor; + } + + public String getVendor() { + return vendor; + } + + public void setMainJar(String mainJar) { + if (this.mainJar == null) { + this.mainJar = mainJar; + } else { + Log.warning("Main jar already set to '" + this.mainJar + "'. " + + "Attempt to set main jar to '" + mainJar + "' will be ignored."); + } + } + + public String getMainJar() { + return mainJar; + } + + public void setDescriptions(String [] descriptions) { + this.descriptions = descriptions; + } + + public String getDescription() { + String description = null; + if (descriptions != null) { + if (descriptions[InformationDesc.DESC_DEFAULT] != null) { + description = descriptions[InformationDesc.DESC_DEFAULT]; + } else if (descriptions[InformationDesc.DESC_SHORT] != null) { + description = descriptions[InformationDesc.DESC_SHORT]; + } else if (descriptions[InformationDesc.DESC_ONELINE] != null) { + description = descriptions[InformationDesc.DESC_ONELINE]; + } else if (descriptions[InformationDesc.DESC_TOOLTIP] != null) { + description = descriptions[InformationDesc.DESC_TOOLTIP]; + } + + if (description != null) { + if (description.contains("\r") || description.contains("\n")) { + Log.warning("Multiple lines of text in description is not supported and description will be converted to single line by replacing new lines with spaces."); + Log.warning("Original description:"); + Log.warning(description); + String descs[] = description.split("\n"); + description = ""; + for (String desc : descs) { + desc = desc.trim(); + if (desc.endsWith("\r")) { // In case new line was \r\n + if (desc.length() != 1) { + desc = desc.substring(0, desc.length() - 1); + } else { + continue; + } + } + + if (desc.isEmpty()) { + continue; + } + + if (!description.isEmpty()) { + description += " "; + } + + description += desc; + } + Log.warning("Converted description:"); + Log.warning(description); + } + } + } + + return description; + } + + public void setIcons(IconDesc [] icons) { + this.icons = icons; + } + + public IconDesc getIcon() { + for (IconDesc icon : icons) { + if (icon.getKind() == IconDesc.ICON_KIND_DEFAULT) { + return icon; + } + } + + for (IconDesc icon : icons) { + if (icon.getKind() == IconDesc.ICON_KIND_SHORTCUT) { + return icon; + } + } + + return null; + } + + public String getIconLocation() { + IconDesc icon = getIcon(); + if (icon != null) { + return icon.getLocalLocation(); + } + + return null; + } + + public void setShortcuts(ShortcutDesc shortcuts) { + this.shortcuts = shortcuts; + } + + public boolean isDesktopHint() { + if (shortcuts != null) { + return shortcuts.getDesktop(); + } + + return false; + } + + public boolean isMenuHint() { + if (shortcuts != null) { + return shortcuts.getMenu(); + } + + return false; + } + + public String getSubMenu() { + if (shortcuts != null) { + return shortcuts.getSubmenu(); + } + + return null; + } + + public void setAssociations(AssociationDesc [] associations) { + this.associations = associations; + } + + public AssociationDesc [] getAssociations() { + return associations; + } + + public void setMainClass(String mainClass, boolean isJavafxDesc) { + if (isJavafxDesc) { + this.mainClass = mainClass; + } else if (this.mainClass == null) { + this.mainClass = mainClass; + } + } + + public String getMainClass() { + return mainClass; + } + + public void addArguments(String argument) { + if (argument != null && !argument.isEmpty()) { + arguments.add(argument); + } + } + + public List getArguments() { + return arguments; + } + + public void setProperty(String name, String value) { + if (name.equalsIgnoreCase("jnlp.versionEnabled") && value.equalsIgnoreCase("true")) { + isVersionEnabled = true; + return; + } + + addVMArg("-D" + name + "=" + value); + } + + public boolean isVersionEnabled() { + return isVersionEnabled; + } + + public boolean isSandbox() { + return isSandbox; + } + + public void setIsSandbox(boolean value) { + isSandbox = value; + } + + public boolean isFXApp() { + return isFXApp; + } + + public void setIsFXApp(boolean value) { + isFXApp = value; + } + + public void addFile(String file) { + if (file != null) { + files.add(file); + } + } + + public List getFiles() { + return files; + } + + private boolean isResourceExists(JARDesc resource) { + for (JARDesc r : resources) { + if (r.getLocation().equals(resource.getLocation())) { + return true; + } + } + + return false; + } + + public void addResource(JARDesc resource) { + if (resource != null) { + if (isResourceExists(resource)) { + Log.warning("Ignoring repeated resource " + resource.getLocation()); + return; + } + resources.add(resource); + } + } + + public List getResources() { + return resources; + } + + public void addVMArg(String arg) { + if (arg != null) { + vmArgs.add(arg); + } + } + + public List getVMArgs() { + return vmArgs; + } + + public void setIsLibrary(boolean isLibrary) { + this.isLibrary = isLibrary; + } + + public boolean isLibrary() { + return isLibrary; + } + + public void setIsInstaller(boolean isInstaller) { + this.isInstaller = isInstaller; + } + + public boolean isInstaller() { + return isInstaller; + } + + public void setIsJRESet(boolean isJRESet) { + this.isJRESet = isJRESet; + } + + public boolean isJRESet() { + return isJRESet; + } + + public void setResourcesDesc(ResourcesDesc resourcesDesc) { + this.resourcesDesc = resourcesDesc; + } + + public ResourcesDesc getResourcesDesc() { + return resourcesDesc; + } + + public void parseResourceDesc() throws Exception { + if (resourcesDesc != null && !resourcesDesc.isEmpty()) { + setMainJar(resourcesDesc.getMainJar().getName()); + + JARDesc[] jars = resourcesDesc.getAllJarDescs(); + for (JARDesc jar : jars) { + addResource(jar); + } + + JREDesc jreDesc = resourcesDesc.getJreDesc(); + if (jreDesc != null) { + String [] args = jreDesc.getVmArgsList(); + if (args != null) { + for (String arg : args) { + addVMArg(arg); + } + } + + if (jreDesc.getMinHeap() != -1) { + addVMArg("-Xms" + jreDesc.getMinHeap()); + } + + if (jreDesc.getMaxHeap() != -1) { + addVMArg("-Xmx" + jreDesc.getMaxHeap()); + } + } + + Properties props = resourcesDesc.getResourceProperties(); + Enumeration e = props.propertyNames(); + while (e.hasMoreElements()) { + String key = (String) e.nextElement(); + String value = props.getProperty(key); + setProperty(key, value); + } + } + } + + public static class InformationDesc { + + private final String _title; + private final String _vendor; + private final String[] _descriptions; + private final IconDesc[] _icons; + private ShortcutDesc _shortcutHints; + private AssociationDesc[] _associations; + + public InformationDesc(String title, String vendor, + String[] descriptions, + IconDesc[] icons, + ShortcutDesc shortcutHints, + AssociationDesc[] associations) { + _title = (title == null) ? "" : title; + _vendor = (vendor == null) ? "" : vendor; + if (descriptions == null) { + descriptions = new String[NOF_DESC]; + } + _descriptions = descriptions; + _icons = icons; + _shortcutHints = shortcutHints; + _associations = associations; + } + + /** + * Constants for the getInfoDescription + */ + final public static int DESC_DEFAULT = 0; + final public static int DESC_SHORT = 1; + final public static int DESC_ONELINE = 2; + final public static int DESC_TOOLTIP = 3; + final public static int NOF_DESC = 4; + + /** + * Information + */ + public String getTitle() { + return _title; + } + + public String getVendor() { + return _vendor; + } + + public IconDesc[] getIcons() { + return _icons; + } + + public ShortcutDesc getShortcut() { + if (_shortcutHints == null) { + return null; + } + return new ShortcutDesc(_shortcutHints.getDesktop(), _shortcutHints.getMenu(), _shortcutHints.getSubmenu()); + } + + public AssociationDesc[] getAssociations() { + return _associations; + } + + /** + * Sets new shortcut hints. + * + * @param shortcutDesc the new shortcut hints to set + */ + public void setShortcut(ShortcutDesc shortcut) { + _shortcutHints = shortcut; + } + + /** + * Sets new associations. + * + * @param assoc the association to set + */ + public void setAssociation(AssociationDesc assoc) { + if (assoc == null) { + _associations = null; + } else { + _associations = new AssociationDesc[]{assoc}; + } + } + + /** + * Returns the description of the given kind. will return null if none + * there + */ + public String getDescription(int kind) { + return _descriptions[kind]; + } + + public String[] getDescription() { + return _descriptions; + } + } + + public static class IconDesc { + + private final String _location; + private String _localLocation; + private final int _kind; + + final public static int ICON_KIND_DEFAULT = 0; + final public static int ICON_KIND_SHORTCUT = 5; + + public IconDesc(URL location, int kind) { + _location = location.toExternalForm(); + _kind = kind; + } + + public String getLocation() { + return _location; + } + + public void setLocalLocation(String localLocation) { + _localLocation = localLocation; + } + + public String getLocalLocation() { + return _localLocation; + } + + public int getKind() { + return _kind; + } + } + + public static class ShortcutDesc { + + private final boolean _desktop; + private final boolean _menu; + private final String _submenu; + + public ShortcutDesc(boolean desktop, boolean menu, String submenu) { + _desktop = desktop; + _menu = menu; + _submenu = submenu; + } + + public boolean getDesktop() { + return _desktop; + } + + public boolean getMenu() { + return _menu; + } + + public String getSubmenu() { + return _submenu; + } + } + + public static class AssociationDesc { + + private final String _extensions; + private final String _mimeType; + private final String _description; + private final String _icon; + private String _iconLocalLocation; + + public AssociationDesc(String extensions, String mimeType, String description, URL icon) { + _extensions = extensions; + _mimeType = mimeType; + _description = description; + _icon = (icon != null) ? icon.toExternalForm() : null; + } + + public void setIconLocalLocation(String localLocation) { + _iconLocalLocation = localLocation; + } + + public String getIconLocalLocation() { + return _iconLocalLocation; + } + + public String getExtensions() { + return _extensions; + } + + public String getMimeType() { + return _mimeType; + } + + public String getMimeDescription() { + return _description; + } + + public String getIconUrl() { + return _icon; + } + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java 2019-11-18 20:30:05.105673000 -0500 +++ /dev/null 2019-11-18 20:30:06.000000000 -0500 @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal.resources; - -public class ResourceLocator { - -} --- /dev/null 2019-11-18 20:30:07.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourceType.java 2019-11-18 20:30:01.433518600 -0500 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser; + +/* + * Public super class for all resource entries + */ +interface ResourceType { + /** Visit this specific type */ + void visit(ResourceVisitor visitor) throws Exception; +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java 2019-11-18 20:30:27.965748500 -0500 +++ /dev/null 2019-11-18 20:30:29.000000000 -0500 @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal.resources; - -public class ResourceLocator { - -} --- /dev/null 2019-11-18 20:30:29.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourceVisitor.java 2019-11-18 20:30:24.325298300 -0500 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser; + +import jnlp.converter.parser.ResourcesDesc.JARDesc; +import jnlp.converter.parser.ResourcesDesc.PropertyDesc; +import jnlp.converter.parser.ResourcesDesc.JREDesc; +import jnlp.converter.parser.ResourcesDesc.ExtensionDesc; + +/** + * A visitor class for the various ResourceType objects + * with dummy visit methods for all types. + */ +public class ResourceVisitor { + + public void visitJARDesc(JARDesc jad) { + } + + public void visitPropertyDesc(PropertyDesc prd) { + } + + public void visitExtensionDesc(ExtensionDesc ed) throws Exception { + } + + public void visitJREDesc(JREDesc jrd) { + } +} --- /dev/null 2019-11-18 20:30:50.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourcesDesc.java 2019-11-18 20:30:46.942684100 -0500 @@ -0,0 +1,665 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser; + +import java.net.URL; +import java.util.Properties; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import jnlp.converter.HTTPHelper; + +/** + * This class contains information about the codebase and properties, i.e., how + * to locate the classes and optional-packages + */ +public class ResourcesDesc implements ResourceType { + + private final List _list; + private volatile JNLPDesc _parent = null; + + /** + * Create empty resource list + */ + public ResourcesDesc() { + _list = new CopyOnWriteArrayList<>(); + } + + public JNLPDesc getParent() { + return _parent; + } + + void setParent(JNLPDesc parent) { + _parent = parent; + for (int i = 0; i < _list.size(); i++) { + Object o = _list.get(i); + if (o instanceof JREDesc) { + JREDesc jredesc = (JREDesc) o; + if (jredesc.getNestedResources() != null) { + jredesc.getNestedResources().setParent(parent); + } + } + } + } + + public void addResource(ResourceType rd) { + if (rd != null) { + _list.add(rd); + } + } + + boolean isEmpty() { + return _list.isEmpty(); + } + + public JARDesc[] getLocalJarDescs() { + ArrayList jds = new ArrayList<>(_list.size()); + for (ResourceType rt : _list) { + if (rt instanceof JARDesc) { + jds.add((JARDesc) rt); + } + } + return jds.toArray(new JARDesc[jds.size()]); + } + + public JREDesc getJreDesc() { + for (ResourceType rt : _list) { + if (rt instanceof JREDesc) { + return (JREDesc)rt; + } + } + + return null; + } + + public ExtensionDesc[] getExtensionDescs() throws Exception { + final ArrayList extList = new ArrayList<>(); + visit(new ResourceVisitor() { + @Override + public void visitExtensionDesc(ExtensionDesc ed) throws Exception { + // add all extensiondesc recursively + addExtToList(extList); + } + }); + return extList.toArray(new ExtensionDesc[extList.size()]); + } + + public JARDesc[] getAllJarDescs() throws Exception { + List jarList = new ArrayList<>(); + addJarsToList(jarList); + return jarList.toArray(new JARDesc[jarList.size()]); + } + + /** + * Add to a list of all the ExtensionDesc. This method goes recusivly through + * all ExtensionDesc + */ + private void addExtToList(final List list) throws Exception { + // Iterate through list an add ext jnlp to the list. + visit(new ResourceVisitor() { + @Override + public void visitExtensionDesc(ExtensionDesc ed) throws Exception { + if (ed.getExtensionDesc() != null) { + ed.getExtensionDesc().getMainJar(); + ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc(); + if (rd != null) { + rd.addExtToList(list); + } + } + list.add(ed); + } + }); + } + + private void addJarsToList(final List list) throws Exception { + + // Iterate through list an add resources to the list. + // The ordering of resources are preserved + visit(new ResourceVisitor() { + @Override + public void visitJARDesc(JARDesc jd) { + list.add(jd); + } + + @Override + public void visitExtensionDesc(ExtensionDesc ed) throws Exception { + if (ed.getExtensionDesc() != null) { + ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc(); + if (rd != null) { + rd.addJarsToList(list); + } + } + } + }); + } + + /** + * Get all the resources needed when a specific resource is requested. + * Returns null if no resource was found + */ + public JARDesc[] getResource(final URL location) throws Exception { + final JARDesc[] resources = new JARDesc[1]; + // Find the given resource + visit(new ResourceVisitor() { + @Override + public void visitJARDesc(JARDesc jd) { + if (GeneralUtil.sameURLs(jd.getLocation(), location)) { + resources[0] = jd; + } + } + }); + + // Found no resource? + if (resources[0] == null) { + return null; + } + + // No part, so just one resource + return resources; + } + + /* Returns the Expected Main Jar + * first jar with attribute main="true" + * else first jar if none has that attribute + * will look in extensions, and nested resource blocks if matching + */ + protected JARDesc getMainJar() throws Exception { + // Normal trick to get around final arguments to inner classes + final JARDesc[] results = new JARDesc[2]; + + visit(new ResourceVisitor() { + @Override + public void visitJARDesc(JARDesc jd) { + if (jd.isJavaFile()) { + // Keep track of first Java File + if (results[0] == null || results[0].isNativeLib()) { + results[0] = jd; + } + // Keep tack of Java File marked main + if (jd.isMainJarFile()) { + results[1] = jd; + } + } else if (jd.isNativeLib()) { + // if jnlp extension has only native lib + if (results[0] == null) { + results[0] = jd; + } + } + } + + @Override + public void visitExtensionDesc(ExtensionDesc ed) throws Exception { + // only check if no main yet and it is not an installer + if (results[1] == null && !ed.isInstaller()) { + JNLPDesc extLd = ed.getExtensionDesc(); + if (extLd != null && extLd.isLibrary()) { + ResourcesDesc rd = extLd.getResourcesDesc(); + if (rd != null) { + // look for main jar in extension resource + rd.visit(this); + } + } + } + } + }); + + // Default is the first, if none is specified as main. This might + // return NULL if there is no JAR resources. + JARDesc first = results[0]; + JARDesc main = results[1]; + + // if main is null then return first; + // libraries have no such thing as a main jar, so return first; + // otherwise return main + // only returns null if there are no jars. + return (main == null) ? first : main; + } + + /* + * Get the properties defined for this object + */ + public Properties getResourceProperties() throws Exception { + final Properties props = new Properties(); + visit(new ResourceVisitor() { + @Override + public void visitPropertyDesc(PropertyDesc pd) { + props.setProperty(pd.getKey(), pd.getValue()); + } + + @Override + public void visitExtensionDesc(ExtensionDesc ed) throws Exception { + JNLPDesc jnlpd = ed.getExtensionDesc(); + ResourcesDesc rd = jnlpd.getResourcesDesc(); + if (rd != null) { + Properties extProps = rd.getResourceProperties(); + Enumeration e = extProps.propertyNames(); + while (e.hasMoreElements()) { + String key = (String) e.nextElement(); + String value = extProps.getProperty(key); + props.setProperty(key, value); + } + } + } + }); + return props; + } + + /* + * Get the properties defined for this object, in the right order. + */ + public List getResourcePropertyList() throws Exception { + final LinkedList propList = new LinkedList<>(); + visit(new ResourceVisitor() { + @Override + public void visitPropertyDesc(PropertyDesc pd) { + propList.add(new Property(pd.getKey(), pd.getValue())); + } + }); + return propList; + } + + /** + * visitor dispatch + */ + @Override + public void visit(ResourceVisitor rv) throws Exception { + for (int i = 0; i < _list.size(); i++) { + ResourceType rt = _list.get(i); + rt.visit(rv); + } + } + + public void addNested(ResourcesDesc nested) throws Exception { + if (nested != null) { + nested.visit(new ResourceVisitor() { + @Override + public void visitJARDesc(JARDesc jd) { + _list.add(jd); + } + + @Override + public void visitPropertyDesc(PropertyDesc pd) { + _list.add(pd); + } + + @Override + public void visitExtensionDesc(ExtensionDesc ed) { + _list.add(ed); + } + }); + } + + } + + public static class JARDesc implements ResourceType { + + private URL _location; + private String _locationString; + private String _version; + private boolean _isNativeLib; + private boolean _isMainFile; // Only used for Java JAR files (a main JAR file is implicitly eager) + private ResourcesDesc _parent; // Back-pointer to the Resources that contains this JAR + + public JARDesc(URL location, String version, boolean isMainFile, boolean isNativeLib, ResourcesDesc parent) { + _location = location; + _locationString = GeneralUtil.toNormalizedString(location); + _version = version; + _isMainFile = isMainFile; + _isNativeLib = isNativeLib; + _parent = parent; + } + + /** + * Type of JAR resource + */ + public boolean isNativeLib() { + return _isNativeLib; + } + + public boolean isJavaFile() { + return !_isNativeLib; + } + + /** + * Returns URL/version for JAR file + */ + public URL getVersionLocation() throws Exception { + if (getVersion() == null) { + return _location; + } else { + return GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion()); + } + } + + public URL getLocation() { + return _location; + } + + public String getVersion() { + return _version; + } + + public String getName() { + // File can be separated by '/' or '\\' + int index; + int index1 = _locationString.lastIndexOf('/'); + int index2 = _locationString.lastIndexOf('\\'); + + if (index1 >= index2) { + index = index1; + } else { + index = index2; + } + + if (index != -1) { + return _locationString.substring(index + 1, _locationString.length()); + } + + return null; + } + + /** + * Returns if this is the main JAR file + */ + public boolean isMainJarFile() { + return _isMainFile; + } + + /** + * Get parent LaunchDesc + */ + public ResourcesDesc getParent() { + return _parent; + } + + /** + * Visitor dispatch + */ + public void visit(ResourceVisitor rv) { + rv.visitJARDesc(this); + } + } + + public static class PropertyDesc implements ResourceType { + + private String _key; + private String _value; + + public PropertyDesc(String key, String value) { + _key = key; + _value = value; + } + + // Accessors + public String getKey() { + return _key; + } + + public String getValue() { + return _value; + } + + /** + * Visitor dispatch + */ + public void visit(ResourceVisitor rv) { + rv.visitPropertyDesc(this); + } + + } + + public static class JREDesc implements ResourceType { + + private String _version; + private long _maxHeap; + private long _minHeap; + private String _vmargs; + private ResourcesDesc _resourceDesc; + private JNLPDesc _extensioDesc; + private String _archList; + + /* + * Constructor to create new instance based on the requirements from JNLP file. + */ + public JREDesc(String version, long minHeap, long maxHeap, String vmargs, + ResourcesDesc resourcesDesc, String archList) { + + _version = version; + _maxHeap = maxHeap; + _minHeap = minHeap; + _vmargs = vmargs; + _resourceDesc = resourcesDesc; + _extensioDesc = null; + _archList = archList; + } + + public String[] getArchList() { + return GeneralUtil.getStringList(_archList); + } + + public String getVersion() { + return _version; + } + + public long getMinHeap() { + return _minHeap; + } + + public long getMaxHeap() { + return _maxHeap; + } + + public String getVmArgs() { + return _vmargs; + } + + public String[] getVmArgsList() { + return GeneralUtil.getStringList(_vmargs); + } + + public ResourcesDesc getNestedResources() { + return _resourceDesc; + } + + public JNLPDesc getExtensionDesc() { + return _extensioDesc; + } + + public void setExtensionDesc(JNLPDesc ld) { + _extensioDesc = ld; + } + + /* visitor dispatch */ + public void visit(ResourceVisitor rv) { + rv.visitJREDesc(this); + } + } + + public static class Property implements Cloneable { + + public static final String JNLP_VERSION_ENABLED = "jnlp.versionEnabled"; + + String key; + String value; + + public Property(String spec) { + spec = spec.trim(); + if (!spec.startsWith("-D") || spec.length() < 3) { + throw new IllegalArgumentException("Property invalid"); + } + + int endKey = spec.indexOf("="); + if (endKey < 0) { + // it's legal to have no assignment + this.key = spec.substring(2); // skip "-D" + this.value = ""; + } else { + this.key = spec.substring(2, endKey); + this.value = spec.substring(endKey + 1); + } + } + + public static Property createProperty(String spec) { + Property prop = null; + try { + prop = new Property(spec); + } catch (IllegalArgumentException iae) { + } + return prop; + } + + public Property(String key, String value) { + this.key = key; + if (value != null) { + this.value = value; + } else { + this.value = ""; + } + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + // @return String representation, unquoted, unified presentation + public String toString() { + if (value.length() == 0) { + return "-D" + key; + } + return "-D" + key + "=" + value; + } + + public void addTo(Properties props) { + props.setProperty(key, value); + } + + // Hash Object + public boolean equals(Object o) { + if (!(o instanceof Property)) { + return false; + } + Property op = (Property) o; + int hashTheirs = op.hashCode(); + int hashThis = hashCode(); + return hashTheirs == hashThis; + } + + public int hashCode() { + return key.hashCode(); + } + + private static List jnlpProps = Arrays.asList(new Object[]{ + JNLP_VERSION_ENABLED + }); + + public static boolean isJnlpProperty(String spec) { + try { + Property p = new Property(spec); + return isJnlpPropertyKey(p.getKey()); + } catch (Exception e) { + return false; + } + } + + public static boolean isJnlpPropertyKey(String key) { + return key != null && jnlpProps.contains(key); + } + } + + public static class ExtensionDesc implements ResourceType { + // Tag elements + + private final URL _location; + private final String _locationString; + private final String _version; + private final URL _codebase; + + // Link to launchDesc + private JNLPDesc _extensionLd; // Link to launchDesc for extension + + public ExtensionDesc(URL location, String version) { + _location = location; + _locationString = GeneralUtil.toNormalizedString(location); + _version = version; + _codebase = GeneralUtil.asPathURL(GeneralUtil.getBase(location)); + _extensionLd = null; + } + + public boolean isInstaller() throws Exception { + if (getExtensionDesc() != null) { + return _extensionLd.isInstaller(); + } + return false; + } + + public URL getLocation() { + return _location; + } + + public String getVersionLocation() throws Exception { + if (getVersion() == null) { + return _locationString; + } else { + return GeneralUtil.toNormalizedString(GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion())); + } + } + + public String getVersion() { + return _version; + } + + public URL getCodebase() { + return _codebase; + } + + /* + * Information about the resources + */ + public JNLPDesc getExtensionDesc() throws Exception { + if (_extensionLd == null) { + byte[] bits = HTTPHelper.getJNLPBits(getVersionLocation(), _locationString); + _extensionLd = XMLFormat.parse(bits, getCodebase(), getVersionLocation()); + } + return _extensionLd; + } + + public void setExtensionDesc(JNLPDesc desc) { + _extensionLd = desc; + } + + /** + * Visitor dispatch + */ + public void visit(ResourceVisitor rv) throws Exception { + rv.visitExtensionDesc(this); + } + } +} --- /dev/null 2019-11-18 20:31:01.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/VersionID.java 2019-11-18 20:30:58.332304400 -0500 @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * VersionID contains a JNLP version ID. + * + * The VersionID also contains a prefix indicator that can + * be used when stored with a VersionString + * + */ +public class VersionID implements Comparable { + private final String[] _tuple; // Array of Integer or String objects + private final boolean _usePrefixMatch; // star (*) prefix + private final boolean _useGreaterThan; // plus (+) greather-than + private final boolean _isCompound; // and (&) operator + private final VersionID _rest; // remaining part after the & + + /** + * Creates a VersionID object from a given String. + * @param str version string to parse + */ + public VersionID(String str) { + if (str == null || str.length() == 0) { + _tuple = new String[0]; + _useGreaterThan = false; + _usePrefixMatch = false; + _isCompound = false; + _rest = null; + return; + } + + // Check for compound + int amp = str.indexOf("&"); + if (amp >= 0) { + _isCompound = true; + VersionID firstPart = new VersionID(str.substring(0, amp)); + _rest = new VersionID(str.substring(amp+1)); + _tuple = firstPart._tuple; + _usePrefixMatch = firstPart._usePrefixMatch; + _useGreaterThan = firstPart._useGreaterThan; + } else { + _isCompound = false; + _rest = null; + // Check for postfix + if (str.endsWith("+")) { + _useGreaterThan = true; + _usePrefixMatch = false; + str = str.substring(0, str.length() - 1); + } else if (str.endsWith("*")) { + _useGreaterThan = false; + _usePrefixMatch = true; + str = str.substring(0, str.length() - 1); + } else { + _useGreaterThan = false; + _usePrefixMatch = false; + } + + ArrayList list = new ArrayList<>(); + int start = 0; + for (int i = 0; i < str.length(); i++) { + // Split at each separator character + if (".-_".indexOf(str.charAt(i)) != -1) { + if (start < i) { + String value = str.substring(start, i); + list.add(value); + } + start = i + 1; + } + } + if (start < str.length()) { + list.add(str.substring(start, str.length())); + } + _tuple = list.toArray(new String[0]); + } + } + + /** @return true if no flags are set */ + public boolean isSimpleVersion() { + return !_useGreaterThan && !_usePrefixMatch && !_isCompound; + } + + /** Match 'this' versionID against vid. + * The _usePrefixMatch/_useGreaterThan flag is used to determine if a + * prefix match of an exact match should be performed + * if _isCompound, must match _rest also. + */ + public boolean match(VersionID vid) { + if (_isCompound) { + if (!_rest.match(vid)) { + return false; + } + } + return (_usePrefixMatch) ? this.isPrefixMatchTuple(vid) : + (_useGreaterThan) ? vid.isGreaterThanOrEqualTuple(this) : + matchTuple(vid); + } + + /** Compares if two version IDs are equal */ + @Override + public boolean equals(Object o) { + if (matchTuple(o)) { + VersionID ov = (VersionID) o; + if (_rest == null || _rest.equals(ov._rest)) { + if ((_useGreaterThan == ov._useGreaterThan) && + (_usePrefixMatch == ov._usePrefixMatch)) { + return true; + } + } + } + return false; + } + + /** Computes a hash code for a VersionID */ + @Override + public int hashCode() { + boolean first = true; + int hashCode = 0; + for (String tuple : _tuple) { + if (first) { + first = false; + hashCode = tuple.hashCode(); + } else { + hashCode = hashCode ^ tuple.hashCode(); + } + } + return hashCode; + } + + /** Compares if two version IDs are equal */ + private boolean matchTuple(Object o) { + // Check for null and type + if (o == null || !(o instanceof VersionID)) { + return false; + } + VersionID vid = (VersionID) o; + + // Normalize arrays + String[] t1 = normalize(_tuple, vid._tuple.length); + String[] t2 = normalize(vid._tuple, _tuple.length); + + // Check contents + for (int i = 0; i < t1.length; i++) { + Object o1 = getValueAsObject(t1[i]); + Object o2 = getValueAsObject(t2[i]); + if (!o1.equals(o2)) { + return false; + } + } + + return true; + } + + private Object getValueAsObject(String value) { + if (value.length() > 0 && value.charAt(0) != '-') { + try { + return Integer.valueOf(value); + } catch (NumberFormatException nfe) { + /* fall through */ + } + } + return value; + } + + public boolean isGreaterThan(VersionID vid) { + if (vid == null) { + return false; + } + return isGreaterThanOrEqualHelper(vid, false, true); + } + + public boolean isGreaterThanOrEqual(VersionID vid) { + if (vid == null) { + return false; + } + return isGreaterThanOrEqualHelper(vid, true, true); + } + + boolean isGreaterThanOrEqualTuple(VersionID vid) { + return isGreaterThanOrEqualHelper(vid, true, false); + } + + /** Compares if 'this' is greater than vid */ + private boolean isGreaterThanOrEqualHelper(VersionID vid, + boolean allowEqual, boolean useRest) { + + if (useRest && _isCompound) { + if (!_rest.isGreaterThanOrEqualHelper(vid, allowEqual, true)) { + return false; + } + } + // Normalize the two strings + String[] t1 = normalize(_tuple, vid._tuple.length); + String[] t2 = normalize(vid._tuple, _tuple.length); + + for (int i = 0; i < t1.length; i++) { + // Compare current element + Object e1 = getValueAsObject(t1[i]); + Object e2 = getValueAsObject(t2[i]); + if (e1.equals(e2)) { + // So far so good + } else { + if (e1 instanceof Integer && e2 instanceof Integer) { + // if both can be parsed as ints, compare ints + return ((Integer)e1).intValue() > ((Integer)e2).intValue(); + } else { + if (e1 instanceof Integer) { + return false; // e1 (numeric) < e2 (non-numeric) + } else if (e2 instanceof Integer) { + return true; // e1 (non-numeric) > e2 (numeric) + } + + String s1 = t1[i]; + String s2 = t2[i]; + + return s1.compareTo(s2) > 0; + } + + } + } + // If we get here, they are equal + return allowEqual; + } + + /** Checks if 'this' is a prefix of vid */ + private boolean isPrefixMatchTuple(VersionID vid) { + + // Make sure that vid is at least as long as the prefix + String[] t2 = normalize(vid._tuple, _tuple.length); + + for (int i = 0; i < _tuple.length; i++) { + Object e1 = _tuple[i]; + Object e2 = t2[i]; + if (e1.equals(e2)) { + // So far so good + } else { + // Not a prefix + return false; + } + } + return true; + } + + /** Normalize an array to a certain lengh */ + private String[] normalize(String[] list, int minlength) { + if (list.length < minlength) { + // Need to do padding + String[] newlist = new String[minlength]; + System.arraycopy(list, 0, newlist, 0, list.length); + Arrays.fill(newlist, list.length, newlist.length, "0"); + return newlist; + } else { + return list; + } + } + + @Override + public int compareTo(VersionID o) { + if (o == null || !(o instanceof VersionID)) { + return -1; + } + VersionID vid = o; + return equals(vid) ? 0 : (isGreaterThanOrEqual(vid) ? 1 : -1); + } + + /** Show it as a string */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < _tuple.length - 1; i++) { + sb.append(_tuple[i]); + sb.append('.'); + } + if (_tuple.length > 0) { + sb.append(_tuple[_tuple.length - 1]); + } + if (_useGreaterThan) { + sb.append('+'); + } + if (_usePrefixMatch) { + sb.append('*'); + } + if (_isCompound) { + sb.append("&"); + sb.append(_rest); + } + return sb.toString(); + } +} --- /dev/null 2019-11-18 20:31:13.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/VersionString.java 2019-11-18 20:31:09.672838800 -0500 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser; + +import java.util.List; +import java.util.ArrayList; +import java.util.StringTokenizer; + +/* + * Utility class that knows to handle version strings + * A version string is of the form: + * + * (version-id ('+'?) ' ') * + * + */ +public class VersionString { + private final ArrayList _versionIds; + + /** Constructs a VersionString object from string */ + public VersionString(String vs) { + _versionIds = new ArrayList<>(); + if (vs != null) { + StringTokenizer st = new StringTokenizer(vs, " ", false); + while (st.hasMoreElements()) { + // Note: The VersionID class takes care of a postfixed '+' + _versionIds.add(new VersionID(st.nextToken())); + } + } + } + + public VersionString(VersionID id) { + _versionIds = new ArrayList<>(); + if (id != null) { + _versionIds.add(id); + } + } + + public boolean isSimpleVersion() { + if (_versionIds.size() == 1) { + return _versionIds.get(0).isSimpleVersion(); + } + return false; + } + + /** Check if this VersionString object contains the VersionID m */ + public boolean contains(VersionID m) { + for (int i = 0; i < _versionIds.size(); i++) { + VersionID vi = _versionIds.get(i); + boolean check = vi.match(m); + if (check) { + return true; + } + } + return false; + } + + /** Check if this VersionString object contains the VersionID m, given as a string */ + public boolean contains(String versionid) { + return contains(new VersionID(versionid)); + } + + /** Check if this VersionString object contains anything greater than m */ + public boolean containsGreaterThan(VersionID m) { + for (int i = 0; i < _versionIds.size(); i++) { + VersionID vi = _versionIds.get(i); + boolean check = vi.isGreaterThan(m); + if (check) { + return true; + } + } + return false; + } + + /** Check if this VersionString object contains anything greater than the VersionID m, given as a string */ + public boolean containsGreaterThan(String versionid) { + return containsGreaterThan(new VersionID(versionid)); + } + + /** Check if the versionString 'vs' contains the VersionID 'vi' */ + public static boolean contains(String vs, String vi) { + return (new VersionString(vs)).contains(vi); + } + + /** Pretty-print object */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < _versionIds.size(); i++) { + sb.append(_versionIds.get(i).toString()); + sb.append(' '); + } + return sb.toString(); + } + + public List getAllVersionIDs() { + return _versionIds; + } +} --- /dev/null 2019-11-18 20:31:24.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/XMLFormat.java 2019-11-18 20:31:21.000677800 -0500 @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser; + +import java.net.URL; +import java.util.Arrays; +import java.util.ArrayList; +import jnlp.converter.JNLPConverter; +import jnlp.converter.parser.exception.MissingFieldException; +import jnlp.converter.parser.exception.BadFieldException; +import jnlp.converter.parser.exception.JNLParseException; +import jnlp.converter.parser.xml.XMLEncoding; +import jnlp.converter.parser.xml.XMLParser; +import jnlp.converter.parser.xml.XMLNode; +import jnlp.converter.parser.JNLPDesc.AssociationDesc; +import jnlp.converter.parser.JNLPDesc.IconDesc; +import jnlp.converter.parser.JNLPDesc.InformationDesc; +import jnlp.converter.parser.JNLPDesc.ShortcutDesc; +import jnlp.converter.parser.ResourcesDesc.JARDesc; +import jnlp.converter.parser.ResourcesDesc.JREDesc; +import jnlp.converter.Log; +import jnlp.converter.parser.ResourcesDesc.ExtensionDesc; +import jnlp.converter.parser.ResourcesDesc.PropertyDesc; +import org.xml.sax.SAXParseException; + +public class XMLFormat { + + public static XMLNode parseBits(byte[] bits) throws JNLParseException { + return parse(decode(bits)); + } + + private static String decode(byte[] bits) throws JNLParseException { + try { + return XMLEncoding.decodeXML(bits); + } catch (Exception e) { + throw new JNLParseException(e, + "exception determining encoding of jnlp file", 0); + } + } + + private static XMLNode parse(String source) throws JNLParseException { + try { + return (new XMLParser(source).parse()); + } catch (SAXParseException spe) { + throw new JNLParseException(spe, + "exception parsing jnlp file", spe.getLineNumber()); + } catch (Exception e) { + throw new JNLParseException(e, + "exception parsing jnlp file", 0); + } + } + + /** + * thisCodebase, if set, is used to determine the codebase, + * if JNLP codebase is not absolute. + * + * @param thisCodebase base URL of this JNLPDesc location + */ + public static JNLPDesc parse(byte[] bits, URL thisCodebase, String jnlp) + throws Exception { + + JNLPDesc jnlpd = new JNLPDesc(); + String source = decode(bits).trim(); + XMLNode root = parse(source); + + if (root == null || root.getName() == null) { + throw new JNLParseException(null, null, 0); + } + + // Check that root element is a tag + if (!root.getName().equals("jnlp")) { + throw (new MissingFieldException(source, "")); + } + + // Read attributes (path is empty, i.e., "") + // (spec, version, codebase, href) + String specVersion = XMLUtils.getAttribute(root, "", "spec", "1.0+"); + jnlpd.setSpecVersion(specVersion); + String version = XMLUtils.getAttribute(root, "", "version"); + jnlpd.setVersion(version); + + // Make sure the codebase URL ends with a '/'. + // + // Regarding the JNLP spec, + // the thisCodebase is used to determine the codebase. + // codebase = new URL(thisCodebase, codebase) + URL codebase = GeneralUtil.asPathURL(XMLUtils.getAttributeURL(source, + thisCodebase, root, "", "codebase")); + if (codebase == null && thisCodebase != null) { + codebase = thisCodebase; + } + jnlpd.setCodebase(codebase.toExternalForm()); + + // Get href for JNLP file + URL href = XMLUtils.getAttributeURL(source, codebase, root, "", "href"); + jnlpd.setHref(href.toExternalForm()); + + // Read attributes + if (XMLUtils.isElementPath(root, "")) { + jnlpd.setIsSandbox(false); + } else if (XMLUtils.isElementPath(root, + "")) { + jnlpd.setIsSandbox(false); + } + + // We can be fxapp, and also be applet, or application, or neither + boolean isFXApp = false; + boolean isApplet = false; + if (XMLUtils.isElementPath(root, "")) { + // no new type for javafx-desc - needs one of the others + buildFXAppDesc(source, root, "", jnlpd); + jnlpd.setIsFXApp(true); + isFXApp = true; + } + + /* + * Note - the jnlp specification says there must be exactly one of + * the descriptor types. This code has always violated (or at least + * not checked for) that condition. + * Instead it uses precedent order app, component, installer, applet + * and ignores any other descriptors given. + */ + if (XMLUtils.isElementPath(root, "")) { + buildApplicationDesc(source, root, jnlpd); + } else if (XMLUtils.isElementPath(root, "")) { + jnlpd.setIsLibrary(true); + } else if (XMLUtils.isElementPath(root, "")) { + Log.warning(" is not supported and will be ignored in " + jnlp); + jnlpd.setIsInstaller(true); + } else if (XMLUtils.isElementPath(root, "")) { + isApplet = true; + } else { + if (!isFXApp) { + throw (new MissingFieldException(source, + "(||" + + "|)")); + } + } + + if (isApplet && !isFXApp) { + Log.error("Applet based applications deployed with element are not supported."); + } + + if (!jnlpd.isLibrary() && !jnlpd.isInstaller()) { + buildInformationDesc(source, codebase, root, jnlpd); + } + + if (!jnlpd.isInstaller()) { + buildResourcesDesc(source, codebase, root, false, jnlpd); + } + + if (!jnlpd.isLibrary() && !jnlpd.isInstaller()) { + jnlpd.parseResourceDesc(); + } + + if (!jnlpd.isInstaller()) { + if (jnlpd.isSandbox()) { + if (jnlpd.isLibrary()) { + Log.warning(jnlp + " is sandbox extension. JNLPConverter does not support sandbox environment and converted application will run without security manager."); + } else { + Log.warning("This is sandbox Web-Start application. JNLPConverter does not support sandbox environment and converted application will run without security manager."); + } + } + } + + return jnlpd; + } + + /** + * Create a combine informationDesc in the two informationDesc. + * The information present in id1 overwrite the information present in id2 + */ + private static InformationDesc combineInformationDesc( + InformationDesc id1, InformationDesc id2) { + if (id1 == null) { + return id2; + } + if (id2 == null) { + return id1; + } + + String t1 = id1.getTitle(); + String title = (t1 != null && t1.length() > 0) ? + t1 : id2.getTitle(); + String v1 = id1.getVendor(); + String vendor = (v1 != null && v1.length() > 0) ? + v1 : id2.getVendor(); + + /** Copy descriptions */ + String[] descriptions = new String[InformationDesc.NOF_DESC]; + for (int i = 0; i < descriptions.length; i++) { + descriptions[i] = (id1.getDescription(i) != null) + ? id1.getDescription(i) : id2.getDescription(i); + } + + /** Icons */ + ArrayList iconList = new ArrayList<>(); + if (id2.getIcons() != null) { + iconList.addAll(Arrays.asList(id2.getIcons())); + } + if (id1.getIcons() != null) { + iconList.addAll(Arrays.asList(id1.getIcons())); + } + IconDesc[] icons = new IconDesc[iconList.size()]; + icons = iconList.toArray(icons); + + ShortcutDesc hints = (id1.getShortcut() != null) ? + id1.getShortcut() : id2.getShortcut(); + + AssociationDesc[] asd = ( AssociationDesc[] ) addArrays( + (Object[])id1.getAssociations(), (Object[])id2.getAssociations()); + + return new InformationDesc(title, + vendor, + descriptions, + icons, + hints, + asd); + } + + /** Extract data from tag */ + private static void buildInformationDesc(final String source, final URL codebase, XMLNode root, JNLPDesc jnlpd) + throws MissingFieldException, BadFieldException { + final ArrayList list = new ArrayList<>(); + + // Iterates over all nodes ignoring the type + XMLUtils.visitElements(root, + "", new XMLUtils.ElementVisitor() { + @Override + public void visitElement(XMLNode e) throws + BadFieldException, MissingFieldException { + + // Check for right os, arch, and locale + String[] os = GeneralUtil.getStringList( + XMLUtils.getAttribute(e, "", "os", null)); + String[] arch = GeneralUtil.getStringList( + XMLUtils.getAttribute(e, "", "arch", null)); + String[] locale = GeneralUtil.getStringList( + XMLUtils.getAttribute(e, "", "locale", null)); + if (GeneralUtil.prefixMatchStringList( + os, GeneralUtil.getOSFullName()) && + GeneralUtil.prefixMatchArch(arch) && + matchDefaultLocale(locale)) + { + // Title, vendor + String title = XMLUtils.getElementContents(e, ""); + String vendor = XMLUtils.getElementContents(e, "<vendor>"); + + // Descriptions + String[] descriptions = + new String[InformationDesc.NOF_DESC]; + descriptions[InformationDesc.DESC_DEFAULT] = + XMLUtils.getElementContentsWithAttribute( + e, "<description>", "kind", "", null); + descriptions[InformationDesc.DESC_ONELINE] = + XMLUtils.getElementContentsWithAttribute( + e, "<description>", "kind", "one-line", null); + descriptions[InformationDesc.DESC_SHORT] = + XMLUtils.getElementContentsWithAttribute( + e, "<description>", "kind", "short", null); + descriptions[InformationDesc.DESC_TOOLTIP] = + XMLUtils.getElementContentsWithAttribute( + e, "<description>", "kind", "tooltip", null); + + // Icons + IconDesc[] icons = getIconDescs(source, codebase, e); + + // Shortcut hints + ShortcutDesc shortcuts = getShortcutDesc(e); + + // Association hints + AssociationDesc[] associations = getAssociationDesc( + source, codebase, e); + + list.add(new InformationDesc( + title, vendor, descriptions, icons, + shortcuts, associations)); + } + } + }); + + /* Combine all information desc. information in a single one for + * the current locale using the following priorities: + * 1. locale == language_country_variant + * 2. locale == lauguage_country + * 3. locale == lauguage + * 4. no or empty locale + */ + InformationDesc normId = new InformationDesc(null, null, null, null, null, null); + for (InformationDesc id : list) { + normId = combineInformationDesc(id, normId); + } + + jnlpd.setTitle(normId.getTitle()); + jnlpd.setVendor(normId.getVendor()); + jnlpd.setDescriptions(normId.getDescription()); + jnlpd.setIcons(normId.getIcons()); + jnlpd.setShortcuts(normId.getShortcut()); + jnlpd.setAssociations(normId.getAssociations()); + } + + private static Object[] addArrays (Object[] a1, Object[] a2) { + if (a1 == null) { + return a2; + } + if (a2 == null) { + return a1; + } + ArrayList<Object> list = new ArrayList<>(); + int i; + for (i=0; i<a1.length; list.add(a1[i++])); + for (i=0; i<a2.length; list.add(a2[i++])); + return list.toArray(a1); + } + + public static boolean matchDefaultLocale(String[] localeStr) { + return GeneralUtil.matchLocale(localeStr, GeneralUtil.getDefaultLocale()); + } + + /** Extract data from <resources> tag. There is only one. */ + static void buildResourcesDesc(final String source, + final URL codebase, XMLNode root, final boolean ignoreJres, JNLPDesc jnlpd) + throws MissingFieldException, BadFieldException { + // Extract classpath directives + final ResourcesDesc rdesc = new ResourcesDesc(); + + // Iterate over all entries + XMLUtils.visitElements(root, "<resources>", + new XMLUtils.ElementVisitor() { + @Override + public void visitElement(XMLNode e) + throws MissingFieldException, BadFieldException { + // Check for right os, archictecture, and locale + String[] os = GeneralUtil.getStringList( + XMLUtils.getAttribute(e, "", "os", null)); + final String arch = XMLUtils.getAttribute(e, "", "arch", null); + String[] locale = GeneralUtil.getStringList( + XMLUtils.getAttribute(e, "", "locale", null)); + if (GeneralUtil.prefixMatchStringList( + os, GeneralUtil.getOSFullName()) + && matchDefaultLocale(locale)) { + // Now visit all children in this node + XMLUtils.visitChildrenElements(e, + new XMLUtils.ElementVisitor() { + @Override + public void visitElement(XMLNode e2) + throws MissingFieldException, BadFieldException { + handleResourceElement(source, codebase, + e2, rdesc, ignoreJres, arch, jnlpd); + } + }); + } + } + }); + + if (!rdesc.isEmpty()) { + jnlpd.setResourcesDesc(rdesc); + } + } + + private static IconDesc[] getIconDescs(final String source, + final URL codebase, XMLNode e) + throws MissingFieldException, BadFieldException { + final ArrayList<IconDesc> answer = new ArrayList<>(); + XMLUtils.visitElements(e, "<icon>", new XMLUtils.ElementVisitor() { + @Override + public void visitElement(XMLNode icon) throws + MissingFieldException, BadFieldException { + String kindStr = XMLUtils.getAttribute(icon, "", "kind", ""); + URL href = XMLUtils.getRequiredURL(source, codebase, icon, "", "href"); + + if (href != null) { + if (!JNLPConverter.isIconSupported(href.toExternalForm())) { + return; + } + } + + int kind; + if (kindStr == null || kindStr.isEmpty() || kindStr.equals("default")) { + kind = IconDesc.ICON_KIND_DEFAULT; + } else if (kindStr.equals("shortcut")) { + kind = IconDesc.ICON_KIND_SHORTCUT; + } else { + Log.warning("Ignoring unsupported icon \"" + href + "\" with kind \"" + kindStr + "\"."); + return; + } + + answer.add(new IconDesc(href, kind)); + } + }); + return answer.toArray(new IconDesc[answer.size()]); + } + + private static ShortcutDesc getShortcutDesc(XMLNode e) + throws MissingFieldException, BadFieldException { + final ArrayList<ShortcutDesc> shortcuts = new ArrayList<>(); + + XMLUtils.visitElements(e, "<shortcut>", new XMLUtils.ElementVisitor() { + @Override + public void visitElement(XMLNode shortcutNode) + throws MissingFieldException, BadFieldException { + boolean desktopHinted = + XMLUtils.isElementPath(shortcutNode, "<desktop>"); + boolean menuHinted = + XMLUtils.isElementPath(shortcutNode, "<menu>"); + String submenuHinted = + XMLUtils.getAttribute(shortcutNode, "<menu>", "submenu"); + shortcuts.add(new ShortcutDesc(desktopHinted, menuHinted, submenuHinted)); + } + }); + + if (shortcuts.size() > 0) { + return shortcuts.get(0); + } + return null; + } + + private static AssociationDesc[] getAssociationDesc(final String source, + final URL codebase, XMLNode e) + throws MissingFieldException, BadFieldException { + final ArrayList<AssociationDesc> answer = new ArrayList<>(); + XMLUtils.visitElements(e, "<association>", + new XMLUtils.ElementVisitor() { + @Override + public void visitElement(XMLNode node) + throws MissingFieldException, BadFieldException { + + String extensions = XMLUtils.getAttribute( + node, "", "extensions"); + + String mimeType = XMLUtils.getAttribute( + node, "", "mime-type"); + String description = XMLUtils.getElementContents( + node, "<description>"); + + URL icon = XMLUtils.getAttributeURL( + source, codebase, node, "<icon>", "href"); + + if (!JNLPConverter.isIconSupported(icon.toExternalForm())) { + icon = null; + } + + if (extensions == null && mimeType == null) { + throw new MissingFieldException(source, + "<association>(<extensions><mime-type>)"); + } else if (extensions == null) { + throw new MissingFieldException(source, + "<association><extensions>"); + } else if (mimeType == null) { + throw new MissingFieldException(source, + "<association><mime-type>"); + } + + // don't support uppercase extension and mime-type on gnome. + if ("gnome".equals(System.getProperty("sun.desktop"))) { + extensions = extensions.toLowerCase(); + mimeType = mimeType.toLowerCase(); + } + + answer.add(new AssociationDesc(extensions, mimeType, + description, icon)); + } + }); + return answer.toArray( + new AssociationDesc[answer.size()]); + } + + /** Handle the individual entries in a resource desc */ + private static void handleResourceElement(String source, URL codebase, + XMLNode e, ResourcesDesc rdesc, boolean ignoreJres, String arch, JNLPDesc jnlpd) + throws MissingFieldException, BadFieldException { + + String tag = e.getName(); + + boolean matchArch = GeneralUtil.prefixMatchArch( + GeneralUtil.getStringList(arch)); + + + if (matchArch && (tag.equals("jar") || tag.equals("nativelib"))) { + /* + * jar/nativelib elements + */ + URL href = XMLUtils.getRequiredURL(source, codebase, e, "", "href"); + String version = XMLUtils.getAttribute(e, "", "version", null); + + String mainStr = XMLUtils.getAttribute(e, "", "main"); + boolean isNativeLib = tag.equals("nativelib"); + + boolean isMain = "true".equalsIgnoreCase(mainStr); + + JARDesc jd = new JARDesc(href, version, isMain, isNativeLib, rdesc); + rdesc.addResource(jd); + } else if (matchArch && tag.equals("property")) { + /* + * property tag + */ + String name = XMLUtils.getRequiredAttribute(source, e, "", "name"); + String value = XMLUtils.getRequiredAttributeEmptyOK( + source, e, "", "value"); + + rdesc.addResource(new PropertyDesc(name, value)); + } else if (matchArch && tag.equals("extension")) { + URL href = XMLUtils.getRequiredURL(source, codebase, e, "", "href"); + String version = XMLUtils.getAttribute(e, "", "version", null); + rdesc.addResource(new ExtensionDesc(href, version)); + } else if ((tag.equals("java") || tag.equals("j2se")) && !ignoreJres) { + /* + * j2se element + */ + String version = + XMLUtils.getRequiredAttribute(source, e, "", "version"); + String minheapstr = + XMLUtils.getAttribute(e, "", "initial-heap-size"); + String maxheapstr = + XMLUtils.getAttribute(e, "", "max-heap-size"); + + String vmargs = + XMLUtils.getAttribute(e, "", "java-vm-args"); + + if (jnlpd.isJRESet()) { + if (vmargs == null) { + vmargs = "none"; + } + Log.warning("Ignoring repeated element <" + tag + "> with version " + version + + " and java-vm-args: " + vmargs); + return; + } + + long minheap = GeneralUtil.heapValToLong(minheapstr); + long maxheap = GeneralUtil.heapValToLong(maxheapstr); + + ResourcesDesc cbs = null; + buildResourcesDesc(source, codebase, e, true, null); + + // JRE + JREDesc jreDesc = new JREDesc( + version, + minheap, + maxheap, + vmargs, + cbs, + arch); + + rdesc.addResource(jreDesc); + + jnlpd.setIsJRESet(true); + } + } + + /** Extract data from the application-desc tag */ + private static void buildApplicationDesc(final String source, + XMLNode root, JNLPDesc jnlpd) throws MissingFieldException, BadFieldException { + + String mainclass = XMLUtils.getClassName(source, root, + "<application-desc>", "main-class", false); + String appType = XMLUtils.getAttribute(root, "<application-desc>", + "type", "Java"); + String progressclass = XMLUtils.getClassName(source, root, + "<application-desc>", "progress-class", false); + if (progressclass != null && !progressclass.isEmpty()) { + Log.warning("JNLPConverter does not support progress indication. \"" + progressclass + "\" will not be loaded and will be ignored."); + } + + if (!("Java".equalsIgnoreCase(appType) || + "JavaFx".equalsIgnoreCase(appType))) { + throw new BadFieldException(source, XMLUtils.getPathString(root) + + "<application-desc>type", appType); + } + + if ("JavaFx".equalsIgnoreCase(appType)) { + jnlpd.setIsFXApp(true); + } + + XMLUtils.visitElements(root, "<application-desc><argument>", new XMLUtils.ElementVisitor() { + @Override + public void visitElement(XMLNode e) throws MissingFieldException, BadFieldException { + String arg = XMLUtils.getElementContents(e, "", null); + if (arg == null) { + throw new BadFieldException(source, XMLUtils.getPathString(e), ""); + } + jnlpd.addArguments(arg); + } + }); + + XMLUtils.visitElements(root, "<application-desc><param>", + new XMLUtils.ElementVisitor() { + @Override + public void visitElement(XMLNode e) throws MissingFieldException, + BadFieldException { + String pn = XMLUtils.getRequiredAttribute( + source, e, "", "name"); + String pv = XMLUtils.getRequiredAttributeEmptyOK( + source, e, "", "value"); + jnlpd.setProperty(pn, pv); + } + }); + jnlpd.setMainClass(mainclass, false); + } + + /** Extract data from the javafx-desc tag */ + private static void buildFXAppDesc(final String source, + XMLNode root, String element, JNLPDesc jnlpd) + throws MissingFieldException, BadFieldException { + String mainclass = XMLUtils.getClassName(source, root, element, + "main-class", true); + String name = XMLUtils.getRequiredAttribute(source, root, + "<javafx-desc>", "name"); + + /* extract arguments */ + XMLUtils.visitElements(root, "<javafx-desc><argument>", new XMLUtils.ElementVisitor() { + @Override + public void visitElement(XMLNode e) throws MissingFieldException, BadFieldException { + String arg = XMLUtils.getElementContents(e, "", null); + if (arg == null) { + throw new BadFieldException(source, XMLUtils.getPathString(e), ""); + } + jnlpd.addArguments(arg); + } + }); + + /* extract parameters */ + XMLUtils.visitElements(root, "<javafx-desc><param>", + new XMLUtils.ElementVisitor() { + @Override + public void visitElement(XMLNode e) throws MissingFieldException, + BadFieldException { + String pn = XMLUtils.getRequiredAttribute( + source, e, "", "name"); + String pv = XMLUtils.getRequiredAttributeEmptyOK( + source, e, "", "value"); + jnlpd.setProperty(pn, pv); + } + }); + + jnlpd.setMainClass(mainclass, true); + jnlpd.setName(name); + } +} --- /dev/null 2019-11-18 20:31:35.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/XMLUtils.java 2019-11-18 20:31:32.449869600 -0500 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser; + +import java.net.URL; +import java.net.MalformedURLException; + +import jnlp.converter.parser.exception.BadFieldException; +import jnlp.converter.parser.exception.MissingFieldException; +import jnlp.converter.parser.xml.XMLNode; + +/** Contains handy methods for looking up information + * stored in XMLNodes. + */ +public class XMLUtils { + + /** Returns the value of an integer attribute */ + public static int getIntAttribute(String source, XMLNode root, String path, String name, int defaultvalue) + throws BadFieldException { + String value = getAttribute(root, path, name); + if (value == null) { + return defaultvalue; + } + + try { + return Integer.parseInt(value); + } catch (NumberFormatException nfe) { + throw new BadFieldException(source, getPathString(root) + path + name, value); + } + } + + /** Returns the value of a given attribute, or null if not set */ + public static String getAttribute(XMLNode root, String path, String name) + throws BadFieldException { + return getAttribute(root, path, name, null); + } + + /** Returns the value of a given attribute */ + public static String getRequiredAttributeEmptyOK(String source, + XMLNode root, String path, String name) throws MissingFieldException { + String value = null; + XMLNode elem = findElementPath(root, path); + if (elem != null) { + value = elem.getAttribute(name); + } + if (value == null) { + throw new MissingFieldException(source, + getPathString(root)+ path + name); + } + return value; + } + + /*** Returns the value of an attribute, which must be a valid class name */ + public static String getClassName(String source, XMLNode root, + String path, String name, boolean required) + throws BadFieldException, MissingFieldException { + + String className; + if (required) { + className = getRequiredAttribute(source, root, path, name); + } else { + className = getAttribute(root, path, name); + } + if (className != null && className.endsWith(".class")) { + int i = className.lastIndexOf(".class"); + String cname = className.substring(0, i); + return cname; + } + return className; + } + + /** Returns the value of a given attribute, or null if not set */ + public static String getRequiredAttribute(String source, XMLNode root, + String path, String name) throws MissingFieldException, BadFieldException { + String s = getAttribute(root, path, name, null); + if (s == null) { + throw new MissingFieldException(source, getPathString(root) + path + name); + } + s = s.trim(); + return (s.length() == 0) ? null : s; + } + + /** Returns the value of a given attribute, or the default value 'def' if not set */ + public static String getAttribute(XMLNode root, String path, String name, + String def) throws BadFieldException { + XMLNode elem = findElementPath(root, path); + if (elem == null) { + return def; + } + String value = elem.getAttribute(name); + return (value == null || value.length() == 0) ? def : value; + } + + /** Expands a URL into an absolute URL from a relative URL */ + public static URL getAttributeURL(String source, URL base, XMLNode root, String path, String name) throws BadFieldException { + String value = getAttribute(root, path, name); + if (value == null) return null; + try { + if (value.startsWith("jar:")) { + int bang = value.indexOf("!/"); + if (bang > 0) { + String entry = value.substring(bang); + String urlString = value.substring(4, bang); + URL url = (base == null) ? + new URL(urlString) : new URL(base, urlString); + return new URL("jar:" + url.toString() + entry); + } + } + return (base == null) ? new URL(value) : new URL(base, value); + } catch(MalformedURLException mue) { + if (mue.getMessage().contains("https")) { + throw new BadFieldException(source, "<jnlp>", "https"); + } + throw new BadFieldException(source, getPathString(root) + path + name, value); + } + } + + /** Returns the value of an attribute as a URL or null if not set */ + public static URL getAttributeURL(String source, XMLNode root, String path, String name) throws BadFieldException { + return getAttributeURL(source, null, root, path, name); + } + + public static URL getRequiredURL(String source, URL base, XMLNode root, String path, String name) throws BadFieldException, MissingFieldException { + URL url = getAttributeURL(source, base, root, path, name); + if (url == null) { + throw new MissingFieldException(source, getPathString(root) + path + name); + } + return url; + } + + /** Returns the value of an attribute as a URL. Throws a MissingFieldException if the + * attribute is not defined + */ + public static URL getRequiredURL(String source, XMLNode root, String path, String name) throws BadFieldException, MissingFieldException { + return getRequiredURL(source, null, root, path, name); + } + + /** Returns true if the path exists in the document, otherwise false */ + public static boolean isElementPath(XMLNode root, String path) { + return findElementPath(root, path) != null; + } + + public static URL getElementURL(String source, XMLNode root, String path) throws BadFieldException { + String value = getElementContents(root, path); + try { + return new URL(value); + } catch(MalformedURLException mue) { + throw new BadFieldException(source, getPathString(root) + path, value); + } + } + + /** Returns a string describing the current location in the DOM */ + public static String getPathString(XMLNode e) { + return (e == null || !(e.isElement())) ? "" : getPathString(e.getParent()) + "<" + e.getName() + ">"; + } + + /** Returns the contents of an element with the given path and an attribute matching a specific value. Returns + * NULL if not found + */ + public static String getElementContentsWithAttribute(XMLNode root, String path, String attr, String val, String defaultvalue) + throws BadFieldException, MissingFieldException { + XMLNode e = getElementWithAttribute(root, path, attr, val); + if (e == null) { + return defaultvalue; + } + return getElementContents(e, "", defaultvalue); + } + + public static URL getAttributeURLWithAttribute(String source, XMLNode root, String path, String attrcond, String val, + String name, URL defaultvalue) + throws BadFieldException, MissingFieldException { + XMLNode e = getElementWithAttribute(root, path, attrcond, val); + if (e == null) { + return defaultvalue; + } + URL url = getAttributeURL(source, e, "", name); + if (url == null) { + return defaultvalue; + } + return url; + } + + /** Returns an element with the given path and an attribute matching a specific value. Returns + * NULL if not found + */ + public static XMLNode getElementWithAttribute(XMLNode root, String path, final String attr, final String val) + throws BadFieldException, MissingFieldException { + final XMLNode[] result = {null}; + visitElements(root, path, new ElementVisitor() { + public void visitElement(XMLNode e) throws BadFieldException, MissingFieldException { + if (result[0] == null && e.getAttribute(attr).equals(val)) { + result[0] = e; + } + } + }); + return result[0]; + } + + /** Like getElementContents(...) but with a defaultValue of null */ + public static String getElementContents(XMLNode root, String path) { + return getElementContents(root, path, null); + } + + /** Returns the value of the last element tag in the path, e.g., <..><tag>value</tag>. The DOM is assumes + * to be normalized. If no value is found, the defaultvalue is returned + */ + public static String getElementContents(XMLNode root, String path, String defaultvalue) { + XMLNode e = findElementPath(root, path); + if (e == null) { + return defaultvalue; + } + XMLNode n = e.getNested(); + if (n != null && !n.isElement()) { + return n.getName(); + } + return defaultvalue; + } + + /** Parses a path string of the form <tag1><tag2><tag3> and returns the specific Element + * node for that tag, or null if it does not exist. If multiple elements exists with same + * path the first is returned + */ + public static XMLNode findElementPath(XMLNode elem, String path) { + // End condition. Root null -> path does not exist + if (elem == null) { + return null; + } + + // End condition. String empty, return current root + if (path == null || path.length() == 0) { + return elem; + } + + // Strip of first tag + int idx = path.indexOf('>'); + if (!(path.charAt(0) == '<')) { + throw new IllegalArgumentException("bad path. Missing begin tag"); + } + if (idx == -1) { + throw new IllegalArgumentException("bad path. Missing end tag"); + } + String head = path.substring(1, idx); + String tail = path.substring(idx + 1); + return findElementPath(findChildElement(elem, head), tail); + } + + /** Returns an child element with the current tag name or null. */ + public static XMLNode findChildElement(XMLNode elem, String tag) { + XMLNode n = elem.getNested(); + while (n != null) { + if (n.isElement() && n.getName().equals(tag)) { + return n; + } + n = n.getNext(); + } + return null; + } + + /** Iterator class */ + public abstract static class ElementVisitor { + abstract public void visitElement(XMLNode e) throws BadFieldException, MissingFieldException; + } + + /** Visits all elements which matches the <path>. The iteration is only + * done on the last element in the path. + */ + public static void visitElements(XMLNode root, String path, ElementVisitor ev) + throws BadFieldException, MissingFieldException { + // Get last element in path + int idx = path.lastIndexOf('<'); + if (idx == -1) { + throw new IllegalArgumentException( + "bad path. Must contain atleast one tag"); + } + if (path.length() == 0 || path.charAt(path.length() - 1) != '>') { + throw new IllegalArgumentException("bad path. Must end with a >"); + } + String head = path.substring(0, idx); + String tag = path.substring(idx + 1, path.length() - 1); + + XMLNode elem = findElementPath(root, head); + if (elem == null) { + return; + } + + // Iterate through all child nodes + XMLNode n = elem.getNested(); + while (n != null) { + if (n.isElement() && n.getName().equals(tag)) { + ev.visitElement(n); + } + n = n.getNext(); + } + } + + public static void visitChildrenElements(XMLNode elem, ElementVisitor ev) + throws BadFieldException, MissingFieldException { + // Iterate through all child nodes + XMLNode n = elem.getNested(); + while (n != null) { + if (n.isElement()) { + ev.visitElement(n); + } + n = n.getNext(); + } + } +} --- /dev/null 2019-11-18 20:31:47.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/BadFieldException.java 2019-11-18 20:31:44.027411300 -0500 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser.exception; + +public class BadFieldException extends Exception { + + private final String _field; + private final String _value; + + public BadFieldException(String source, String field, String value) { + super(); + _value = value; + _field = field; + } + + /** + * Returns the name of the offending field + */ + public String getField() { + return _field; + } + + /** + * Returns the value of the offending field + */ + public String getValue() { + return _value; + } + + /** + * toString implementation + */ + @Override + public String toString() { + return "BadFieldException[ " + getField() + "," + getValue() + "]"; + } + + @Override + public String getMessage(){ + return toString(); + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java 2019-11-18 20:31:59.284709200 -0500 +++ /dev/null 2019-11-18 20:32:00.000000000 -0500 @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal.resources; - -public class ResourceLocator { - -} --- /dev/null 2019-11-18 20:32:01.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/JNLParseException.java 2019-11-18 20:31:55.666953500 -0500 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser.exception; + +/** + * Exception thrown if a parse error occurred when interpreting + * the launch descriptor + */ + +public class JNLParseException extends Exception { + + private final String _msg; + private final int _line; + + public JNLParseException(Exception exception, String msg, int line) { + super(exception); + _msg = msg; + _line = line; + } + + @Override + public String getMessage() { + return _msg; + } + + private int getLine() { + return _line; + } + + @Override + public String toString() { + return "JNLParseException[ " + getMessage() + "] at " + getLine(); + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java 2019-11-18 20:32:21.473067300 -0500 +++ /dev/null 2019-11-18 20:32:22.000000000 -0500 @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal.resources; - -public class ResourceLocator { - -} --- /dev/null 2019-11-18 20:32:23.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/MissingFieldException.java 2019-11-18 20:32:17.828006800 -0500 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser.exception; + +public class MissingFieldException extends Exception { + + private final String _field; + + public MissingFieldException(String source, String field) { + super(); + _field = field; + } + + /** + * Returns the name of the offending field + */ + private String getField() { + return _field; + } + + /** + * toString implementation + */ + @Override + public String toString() { + return "MissingFieldException[ " + getField() + "]"; + } + + @Override + public String getMessage(){ + return toString(); + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java 2019-11-18 20:32:44.536778400 -0500 +++ /dev/null 2019-11-18 20:32:46.000000000 -0500 @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal.resources; - -public class ResourceLocator { - -} --- /dev/null 2019-11-18 20:32:46.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLAttribute.java 2019-11-18 20:32:40.785788600 -0500 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser.xml; + +/** + * Class that contains information about a specific attribute + */ +public class XMLAttribute { + + private final String _name; + private final String _value; + private XMLAttribute _next; + + public XMLAttribute(String name, String value) { + _name = XMLNode.stripNameSpace(name); + _value = value; + } + + public String getName() { + return _name; + } + + public String getValue() { + return _value; + } + + public XMLAttribute getNext() { + return _next; + } + + public void setNext(XMLAttribute next) { + _next = next; + } +} --- /dev/null 2019-11-18 20:33:07.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLEncoding.java 2019-11-18 20:33:03.797794300 -0500 @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser.xml; + +import java.io.ByteArrayInputStream; +import java.io.EOFException; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.Reader; +import java.io.UnsupportedEncodingException; + +public class XMLEncoding { + /** + * Decodes a byte stream into a String by testing for a Byte Order Mark + * (BOM) or an XML declaration. + * <br /> + * Detection begins by examining the first four octets of the stream for a + * BOM. If a BOM is not found, then an encoding declaration is looked for + * at the beginning of the stream. If the encoding still can not be + * determined at this point, then UTF-8 is assumed. + * + * @param data an array of bytes containing an encoded XML document. + * + * @return A string containing the decoded XML document. + */ + public static String decodeXML(byte [] data) throws IOException { + int start = 0; + String encoding; + + if (data.length < BOM_LENGTH) { + throw (new EOFException("encoding.error.not.xml")); + } + // no else required; successfully read stream + int firstFour = ((0xff000000 & ((int) data[0] << 24)) | + (0x00ff0000 & ((int) data[1] << 16)) | + (0x0000ff00 & ((int) data[2] << 8)) | + (0x000000ff & (int) data[3])); + + // start by examining the first four bytes for a BOM + switch (firstFour) { + case EBCDIC: + // examine the encoding declaration + encoding = examineEncodingDeclaration(data, IBM037_ENC); + break; + + case XML_DECLARATION: + // assume UTF-8, but examine the encoding declaration + encoding = examineEncodingDeclaration(data, UTF_8_ENC); + break; + + case UTF_16BE: + encoding = UTF_16BE_ENC; + break; + + case UTF_16LE: + encoding = UTF_16LE_ENC; + break; + + case UNUSUAL_OCTET_1: + case UNUSUAL_OCTET_2: + throw (new UnsupportedEncodingException("encoding.error.unusual.octet")); + + case UTF_32_BE_BOM: + case UTF_32_LE_BOM: + encoding = UTF_32_ENC; + break; + + default: + int firstThree = firstFour & 0xffffff00; + + switch (firstThree) { + case UTF_8_BOM: + // the InputStreamReader class doen't properly handle + // the Byte Order Mark (BOM) in UTF-8 streams, so don't + // putback those 3 bytes. + start = 3; + encoding = UTF_8_ENC; + break; + + default: + int firstTwo = firstFour & 0xffff0000; + + switch (firstTwo) { + case UTF_16_BE_BOM: + case UTF_16_LE_BOM: + encoding = UTF_16_ENC; + break; + + default: + // this is probably UTF-8 without the encoding + // declaration + encoding = UTF_8_ENC; + break; + } + break; + } + break; + } + + return (new String(data, start, data.length - start, encoding)); + } + + /** + * [3] S ::= ( #x20 | #x09 | #x0d | #x0a ) + * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' + * [24] VersionInfo ::= S 'version' Eq ( '"' VersionNum '"' | + * "'" VersionNum "'" ) + * [25] Eq ::= S? '=' S? + * [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')+ + * [80] EncodingDecl ::= S 'encoding' Eq ( '"' EncName '"' | + * "'" EncName "'" ) + * [81] EncName ::= [a-zA-Z] ([a-zA-Z0-9_.] | '-')* + */ + private static String examineEncodingDeclaration(byte [] data, + String encoding) throws IOException { + boolean loop = false; + boolean recognized = false; + boolean almost = false; + boolean question = false; + boolean done = false; + boolean found = false; + int pos = 0; + int ch = -1; + Reader reader = null; + String result = ((encoding != null) ? encoding : UTF_8_ENC); + + reader = new InputStreamReader(new ByteArrayInputStream(data), result); + ch = reader.read(); + + // if this is an XML declaration, it will start with the text '<?xml' + for (int i = 0; ((i < XML_DECL_START.length()) && (done == false)); i++) { + if (ch != XML_DECL_START.charAt(i)) { + // This doesn't look like an XML declaration. This method + // should only be called if the stream contains an XML + // declaration in the encoding that is passed into the method. + done = true; + break; + } + // no else required; still matches + ch = reader.read(); + } + + // there must be at least one whitespace character next. + loop = true; + while ((loop == true) && (done == false)) { + switch (ch) { + case SPACE: + case TAB: // intentional + case LINEFEED: // fall + case RETURN: // through + ch = reader.read(); + break; + + case -1: + // unexpected EOF + done = true; + break; + + default: + // non-whitespace + loop = false; + break; + } + } + + // now look for the text 'encoding', but if the end of the XML + // declaration (signified by the text '?>') comes first, then + // assume the encoding is UTF-8 + loop = true; + while ((loop == true) && (done == false)) { + if (ch == -1) { + // unexpected EOF + done = true; + break; + } else if (recognized == true) { + // this is the encoding declaration as long as the next few + // characters are whitespace and/or the equals ('=') sign + switch (ch) { + case SPACE: // intentional + case TAB: // fall + case LINEFEED: // through + case RETURN: + // don't need to do anything + break; + + case EQUAL: + if (almost == false) { + // got the equal, now find a quote + almost = true; + } else { + // this is not valid XML, so punt + recognized = false; + done = true; + } + break; + + case DOUBLE_QUOTE: // intentional + case SINGLE_QUOTE: // fall through + if (almost == true) { + // got the quote, so move on to get the value + loop = false; + } else { + // got a quote before the equal; this is not valid + // XML, so punt + recognized = false; + done = true; + } + break; + + default: + // non-whitespace + recognized = false; + if (almost == true) { + // this is not valid XML, so punt + done = true; + } + // no else required; this wasn't the encoding + // declaration + break; + } + + if (recognized == false) { + // this isn't the encoding declaration, so go back to the + // top without reading the next character + pos = 0; + continue; + } + // no else required; still looking good + } else if (ch == ENCODING_DECL.charAt(pos++)) { + if (ENCODING_DECL.length() == pos) { + // this looks like the encoding declaration + recognized = true; + } + // no else required; this might be the encoding declaration + } else if (ch == '?') { + question = true; + pos = 0; + } else if ((ch == '>') && (question == true)) { + // there is no encoding declaration, so assume that the initial + // encoding guess was correct + done = true; + continue; + } else { + // still searching for the encoding declaration + pos = 0; + } + + ch = reader.read(); + } + + if (done == false) { + StringBuilder buffer = new StringBuilder(MAX_ENC_NAME); + + if (((ch >= 'a') && (ch <= 'z')) | + ((ch >= 'A') && (ch <= 'Z'))) { + // add the character to the result + buffer.append((char) ch); + + loop = true; + while ((loop == true) && (done == false)) { + ch = reader.read(); + + if (((ch >= 'a') && (ch <= 'z')) || + ((ch >= 'A') && (ch <= 'Z')) || + ((ch >= '0') && (ch <= '9')) || + (ch == '_') || (ch == '.') || (ch == '-')) { + // add the character to the result + buffer.append((char) ch); + } else if ((ch == DOUBLE_QUOTE) || (ch == SINGLE_QUOTE)) { + // finished! + found = true; + done = true; + result = buffer.toString(); + } else { + // this is not a valid encoding name, so punt + done = true; + } + } + } else { + // this is not a valid encoding name, so punt + done = true; + } + } + // no else required; already failed to find the encoding somewhere else + + return (result); + } + + private static final int BOM_LENGTH = 4; + private static final int MAX_ENC_NAME = 512; + + private static final int SPACE = 0x00000020; + private static final int TAB = 0x00000009; + private static final int LINEFEED = 0x0000000a; + private static final int RETURN = 0x0000000d; + private static final int EQUAL = '='; + private static final int DOUBLE_QUOTE = '\"'; + private static final int SINGLE_QUOTE = '\''; + + private static final int UTF_32_BE_BOM = 0x0000feff; + private static final int UTF_32_LE_BOM = 0xfffe0000; + private static final int UTF_16_BE_BOM = 0xfeff0000; + private static final int UTF_16_LE_BOM = 0xfffe0000; + private static final int UTF_8_BOM = 0xefbbbf00; + private static final int UNUSUAL_OCTET_1 = 0x00003c00; + private static final int UNUSUAL_OCTET_2 = 0x003c0000; + private static final int UTF_16BE = 0x003c003f; + private static final int UTF_16LE = 0x3c003f00; + private static final int EBCDIC = 0x4c6fa794; + private static final int XML_DECLARATION = 0x3c3f786d; + + private static final String UTF_32_ENC = "UTF-32"; + private static final String UTF_16_ENC = "UTF-16"; + private static final String UTF_16BE_ENC = "UTF-16BE"; + private static final String UTF_16LE_ENC = "UTF-16LE"; + private static final String UTF_8_ENC = "UTF-8"; + private static final String IBM037_ENC = "IBM037"; + + private static final String XML_DECL_START = "<?xml"; + private static final String ENCODING_DECL = "encoding"; +} --- /dev/null 2019-11-18 20:33:18.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLNode.java 2019-11-18 20:33:15.329266700 -0500 @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser.xml; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Class that contains information about an XML Node + */ +public class XMLNode { + private final boolean _isElement; // Element/PCTEXT + private final String _name; + private final XMLAttribute _attr; + private XMLNode _parent; // Parent Node + private XMLNode _nested; // Nested XML tags + private XMLNode _next; // Following XML tag on the same level + + public final static String WILDCARD = "*"; + + /** Creates a PCTEXT node */ + public XMLNode(String name) { + _isElement = false; + _name = name; + _attr = null; + _nested = null; + _next = null; + _parent = null; + } + + /** Creates a ELEMENT node */ + public XMLNode(String name, XMLAttribute attr) { + _isElement = true; + _name = stripNameSpace(name); + _attr = attr; + _nested = null; + _next = null; + _parent = null; + } + + public String getName() { + return _name; + } + + public XMLAttribute getAttributes() { + return _attr; + } + + public XMLNode getNested() { + return _nested; + } + + public XMLNode getNext() { + return _next; + } + + public boolean isElement() { + return _isElement; + } + + public void setParent(XMLNode parent) { + _parent = parent; + } + + public XMLNode getParent() { + return _parent; + } + + public void setNext(XMLNode next) { + _next = next; + } + + public void setNested(XMLNode nested) { + _nested = nested; + } + + public static String stripNameSpace(String name) { + if (name != null && !name.startsWith("xmlns:")) { + int i = name.lastIndexOf(":"); + if (i >= 0 && i < name.length()) { + return name.substring(i+1); + } + } + return name; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 83 * hash + (this._name != null ? this._name.hashCode() : 0); + hash = 83 * hash + (this._attr != null ? this._attr.hashCode() : 0); + hash = 83 * hash + (this._nested != null ? this._nested.hashCode() : 0); + hash = 83 * hash + (this._next != null ? this._next.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof XMLNode)) return false; + XMLNode other = (XMLNode)o; + boolean result = + match(_name, other._name) && + match(_attr, other._attr) && + match(_nested, other._nested) && + match(_next, other._next); + return result; + } + + public String getAttribute(String name) { + XMLAttribute cur = _attr; + while(cur != null) { + if (name.equals(cur.getName())) return cur.getValue(); + cur = cur.getNext(); + } + return ""; + } + + private static boolean match(Object o1, Object o2) { + if (o1 == null) { + return (o2 == null); + } + return o1.equals(o2); + } + + public void printToStream(PrintWriter out) { + printToStream(out, false); + } + + public void printToStream(PrintWriter out, boolean trim) { + printToStream(out, 0, trim); + } + + public void printToStream(PrintWriter out, int n, boolean trim) { + if (!isElement()) { + String value = _name; // value node (where name is data of parent) + if (trim && value.length() > 512) { + value = "..."; + } + out.print(value); + } else { + if (_nested == null) { + String attrString = (_attr == null) ? "" : (" " + _attr.toString()); + lineln(out, n, "<" + _name + attrString + "/>"); + } else { + String attrString = (_attr == null) ? "" : (" " + _attr.toString()); + lineln(out, n, "<" + _name + attrString + ">"); + _nested.printToStream(out, n + 1, trim); + if (_nested.isElement()) { + lineln(out, n, "</" + _name + ">"); + } else { + out.print("</" + _name + ">"); + } + } + } + if (_next != null) { + _next.printToStream(out, n, trim); + } + } + + private static void lineln(PrintWriter out, int indent, String s) { + out.println(""); + for(int i = 0; i < indent; i++) { + out.print(" "); + } + out.print(s); + } + + @Override + public String toString() { + return toString(false); + } + + public String toString(boolean hideLongElementValue) { + StringWriter sw = new StringWriter(1000); + PrintWriter pw = new PrintWriter(sw); + printToStream(pw, hideLongElementValue); + pw.close(); + return sw.toString(); + } +} --- /dev/null 2019-11-18 20:33:30.000000000 -0500 +++ new/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLParser.java 2019-11-18 20:33:26.809410100 -0500 @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jnlp.converter.parser.xml; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.InputSource; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import java.io.StringReader; +import java.io.IOException; +import java.util.Stack; + +public class XMLParser extends DefaultHandler { + + private XMLNode _root; + private final String _source; + private Stack<XMLNode> _inProgress; + private String _characters; + + // although defined in com.sun.org.apache.xerces.internal.impl.Constants, + // we should not be able to access that, so defined here + private final static String DTD_DOWNLOAD = + "http://apache.org/xml/features/nonvalidating/load-external-dtd"; + + private static final SAXParserFactory SPF = SAXParserFactory.newInstance(); + + /* + * Construct an <code>XMLParser</code>. + * + * @param source - the source text to parse. + */ + public XMLParser(String source) { + _source = source.trim(); + } + + public XMLNode parse() throws SAXException { + // normally we parse without validating, but leave option to parse + // with validation, possibly controlled by config option. + return parse(false); + } + + public XMLNode parse(boolean validating) throws SAXException { + _root = null; + _inProgress = new Stack<>(); + + try { + InputSource is = new InputSource(new StringReader(_source)); + SPF.setValidating(validating); + // only download dtd file from DOCTYPE if we are doing validation + try { + SPF.setFeature(DTD_DOWNLOAD, validating); + } catch (Exception e) { + } + SAXParser sp = SPF.newSAXParser(); + sp.parse(is, this); + } catch (ParserConfigurationException | IOException pce) { + throw new SAXException(pce); + } + + return _root; + } + + @Override + public void startElement(String uri, String localeName, String qName, + Attributes attributes) throws SAXException { + + XMLAttribute first = null; + XMLAttribute last = null; + int len = attributes.getLength(); + + for (int i = 0; i < len; i++) { + XMLAttribute att = new XMLAttribute( + // in old implementation attribute names and values were trimmed + attributes.getQName(i).trim(), attributes.getValue(i).trim()); + if (first == null) { + first = att; + } + if (last != null) { + last.setNext(att); + } + last = att; + } + _inProgress.push(new XMLNode(qName, first)); + _characters = null; + } + + @Override + public void endElement(String uri, String localeName, String elementName) + throws SAXException { + XMLNode node = _inProgress.pop(); + // <information> + // <title>Title + // Vendor "Some whitespaces" + // + // In example above when we receive end of we will + // have _characters set to whitespace and new line and it will be + // added as child node to . This will break our cache code + // which will think that JNLP file changed on server even if it is not + // and thus we might not load properly. + // + // + // test with whitespaces + // + // From example above we want to include whitespaces for . + // + // + // abc + // xyz (might be whitespaces) + // + // In JNLP spec we do not have cases when node have nested nodes as + // well as text which is whitespaces only. + // + // So to fix it lets check if ending node have nested nodes, then do + // not add whitespaces only node. + if (node != null && node.getNested() != null && _characters != null) { + String trimCharacters = _characters.trim(); + if ((trimCharacters == null) || (trimCharacters.length() == 0)) { + _characters = null; // No need to add whitespaces only + } + } + if ((_characters != null) && (_characters.trim().length() > 0)) { + addChild(node, new XMLNode(_characters)); + } + + if (_inProgress.isEmpty()) { + _root = node; + } else { + addChild(_inProgress.peek(), node); + } + _characters = null; + } + + @Override + public void characters(char[] chars, int start, int length) + throws SAXException { + String s = new String(chars, start, length); + _characters = ((_characters == null) ? s : _characters + s); + } + + @Override + public void ignorableWhitespace(char[] chars, int start, int length) + throws SAXException { + String s = new String(chars, start, length); + _characters = ((_characters == null) ? s : _characters + s); + } + + private void addChild(XMLNode parent, XMLNode child) { + child.setParent(parent); + + XMLNode sibling = parent.getNested(); + if (sibling == null) { + parent.setNested(child); // set us as only child + } else { + while (sibling.getNext() != null) { + sibling = sibling.getNext(); + } + sibling.setNext(child); // sets us as youngest child + } + } +} --- /dev/null 2019-11-18 20:33:41.000000000 -0500 +++ new/src/demo/share/nbproject/jpackage/JNLPConverter/build.xml 2019-11-18 20:33:38.307055400 -0500 @@ -0,0 +1,80 @@ + + + + + + + + + + + Builds, tests, and runs the project JNLPConverter. + + + + + + + + + + --- /dev/null 2019-11-18 20:33:53.000000000 -0500 +++ new/src/demo/share/nbproject/jpackage/JNLPConverter/manifest.mf 2019-11-18 20:33:49.901958000 -0500 @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + --- /dev/null 2019-11-18 20:34:04.000000000 -0500 +++ new/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/build-impl.xml 2019-11-18 20:34:01.380463600 -0500 @@ -0,0 +1,1403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --- /dev/null 2019-11-18 20:34:16.000000000 -0500 +++ new/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/genfiles.properties 2019-11-18 20:34:12.802917100 -0500 @@ -0,0 +1,8 @@ +build.xml.data.CRC32=9017e41e +build.xml.script.CRC32=5cf818d6 +build.xml.stylesheet.CRC32=8064a381@1.80.1.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=9017e41e +nbproject/build-impl.xml.script.CRC32=d0290d6d +nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48 --- /dev/null 2019-11-18 20:34:27.000000000 -0500 +++ new/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/project.properties 2019-11-18 20:34:24.359536700 -0500 @@ -0,0 +1,96 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=JNLPConverter +application.vendor= +auxiliary.org-netbeans-spi-editor-hints-projects.perProjectHintSettingsFile=nbproject/cfg_hints.xml +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/JNLPConverter.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.jnlpconverter-src=../../../jpackage/jnlpconverter/src +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.external.vm=true +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=default +jnlp.offline-allowed=false +jnlp.signed=false +jnlp.signing= +jnlp.signing.alias= +jnlp.signing.keystore= +main.class=jnlp.converter.Main +# Optional override of default Application-Library-Allowable-Codebase attribute identifying the locations where your signed RIA is expected to be found. +manifest.custom.application.library.allowable.codebase= +# Optional override of default Caller-Allowable-Codebase attribute identifying the domains from which JavaScript code can make calls to your RIA without security prompts. +manifest.custom.caller.allowable.codebase= +# Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed +manifest.custom.codebase= +# Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions) +manifest.custom.permissions= +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.src.dir=${file.reference.jnlpconverter-src} --- /dev/null 2019-11-18 20:34:39.000000000 -0500 +++ new/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/project.xml 2019-11-18 20:34:35.944438100 -0500 @@ -0,0 +1,13 @@ + + + org.netbeans.modules.java.j2seproject + + + JNLPConverter + + + + + + + --- /dev/null 2019-11-18 20:34:50.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/DesktopIntegration.java 2019-11-18 20:34:47.384058500 -0500 @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.imageio.ImageIO; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import static jdk.incubator.jpackage.internal.LinuxAppBundler.ICON_PNG; +import static jdk.incubator.jpackage.internal.LinuxAppImageBuilder.DEFAULT_ICON; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +/** + * Helper to create files for desktop integration. + */ +final class DesktopIntegration { + + static final String DESKTOP_COMMANDS_INSTALL = "DESKTOP_COMMANDS_INSTALL"; + static final String DESKTOP_COMMANDS_UNINSTALL = "DESKTOP_COMMANDS_UNINSTALL"; + static final String UTILITY_SCRIPTS = "UTILITY_SCRIPTS"; + + DesktopIntegration(PlatformPackage thePackage, + Map params) { + + associations = FileAssociation.fetchFrom(params).stream() + .filter(fa -> !fa.mimeTypes.isEmpty()) + .map(LinuxFileAssociation::new) + .collect(Collectors.toUnmodifiableList()); + + launchers = ADD_LAUNCHERS.fetchFrom(params); + + this.thePackage = thePackage; + + final File customIconFile = ICON_PNG.fetchFrom(params); + + iconResource = createResource(DEFAULT_ICON, params) + .setCategory(I18N.getString("resource.menu-icon")) + .setExternal(customIconFile); + desktopFileResource = createResource("template.desktop", params) + .setCategory(I18N.getString("resource.menu-shortcut-descriptor")) + .setPublicName(APP_NAME.fetchFrom(params) + ".desktop"); + + // XDG recommends to use vendor prefix in desktop file names as xdg + // commands copy files to system directories. + // Package name should be a good prefix. + final String desktopFileName = String.format("%s-%s.desktop", + thePackage.name(), APP_NAME.fetchFrom(params)); + final String mimeInfoFileName = String.format("%s-%s-MimeInfo.xml", + thePackage.name(), APP_NAME.fetchFrom(params)); + + mimeInfoFile = new DesktopFile(mimeInfoFileName); + + if (!associations.isEmpty() || SHORTCUT_HINT.fetchFrom(params) || customIconFile != null) { + // + // Create primary .desktop file if one of conditions is met: + // - there are file associations configured + // - user explicitely requested to create a shortcut + // - custom icon specified + // + desktopFile = new DesktopFile(desktopFileName); + iconFile = new DesktopFile(APP_NAME.fetchFrom(params) + + IOUtils.getSuffix(Path.of(DEFAULT_ICON))); + } else { + desktopFile = null; + iconFile = null; + } + + desktopFileData = Collections.unmodifiableMap( + createDataForDesktopFile(params)); + + nestedIntegrations = launchers.stream().map( + launcherParams -> new DesktopIntegration(thePackage, + launcherParams)).collect(Collectors.toList()); + } + + List requiredPackages() { + return Stream.of(List.of(this), nestedIntegrations).flatMap( + List::stream).map(DesktopIntegration::requiredPackagesSelf).flatMap( + List::stream).distinct().collect(Collectors.toList()); + } + + Map create() throws IOException { + associations.forEach(assoc -> assoc.data.verify()); + + if (iconFile != null) { + // Create application icon file. + iconResource.saveToFile(iconFile.srcPath()); + } + + Map data = new HashMap<>(desktopFileData); + + final ShellCommands shellCommands; + if (desktopFile != null) { + // Create application desktop description file. + createDesktopFile(data); + + // Shell commands will be created only if desktop file + // should be installed. + shellCommands = new ShellCommands(); + } else { + shellCommands = null; + } + + if (!associations.isEmpty()) { + // Create XML file with mime types corresponding to file associations. + createFileAssociationsMimeInfoFile(); + + shellCommands.setFileAssociations(); + + // Create icon files corresponding to file associations + addFileAssociationIconFiles(shellCommands); + } + + // Create shell commands to install/uninstall integration with desktop of the app. + if (shellCommands != null) { + shellCommands.applyTo(data); + } + + boolean needCleanupScripts = !associations.isEmpty(); + + // Take care of additional launchers if there are any. + // Process every additional launcher as the main application launcher. + // Collect shell commands to install/uninstall integration with desktop + // of the additional launchers and append them to the corresponding + // commands of the main launcher. + List installShellCmds = new ArrayList<>(Arrays.asList( + data.get(DESKTOP_COMMANDS_INSTALL))); + List uninstallShellCmds = new ArrayList<>(Arrays.asList( + data.get(DESKTOP_COMMANDS_UNINSTALL))); + for (var integration: nestedIntegrations) { + if (!integration.associations.isEmpty()) { + needCleanupScripts = true; + } + + Map launcherData = integration.create(); + + installShellCmds.add(launcherData.get(DESKTOP_COMMANDS_INSTALL)); + uninstallShellCmds.add(launcherData.get( + DESKTOP_COMMANDS_UNINSTALL)); + } + + data.put(DESKTOP_COMMANDS_INSTALL, stringifyShellCommands( + installShellCmds)); + data.put(DESKTOP_COMMANDS_UNINSTALL, stringifyShellCommands( + uninstallShellCmds)); + + if (needCleanupScripts) { + // Pull in utils.sh scrips library. + try (InputStream is = OverridableResource.readDefault("utils.sh"); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader reader = new BufferedReader(isr)) { + data.put(UTILITY_SCRIPTS, reader.lines().collect( + Collectors.joining(System.lineSeparator()))); + } + } else { + data.put(UTILITY_SCRIPTS, ""); + } + + return data; + } + + private List requiredPackagesSelf() { + if (desktopFile != null) { + return List.of("xdg-utils"); + } + return Collections.emptyList(); + } + + private Map createDataForDesktopFile( + Map params) { + Map data = new HashMap<>(); + data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params)); + data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params)); + data.put("APPLICATION_ICON", + iconFile != null ? iconFile.installPath().toString() : null); + data.put("DEPLOY_BUNDLE_CATEGORY", MENU_GROUP.fetchFrom(params)); + data.put("APPLICATION_LAUNCHER", + thePackage.installedApplicationLayout().launchersDirectory().resolve( + LinuxAppImageBuilder.getLauncherName(params)).toString()); + + return data; + } + + /** + * Shell commands to integrate something with desktop. + */ + private class ShellCommands { + + ShellCommands() { + registerIconCmds = new ArrayList<>(); + unregisterIconCmds = new ArrayList<>(); + + registerDesktopFileCmd = String.join(" ", "xdg-desktop-menu", + "install", desktopFile.installPath().toString()); + unregisterDesktopFileCmd = String.join(" ", "xdg-desktop-menu", + "uninstall", desktopFile.installPath().toString()); + } + + void setFileAssociations() { + registerFileAssociationsCmd = String.join(" ", "xdg-mime", + "install", + mimeInfoFile.installPath().toString()); + unregisterFileAssociationsCmd = String.join(" ", "xdg-mime", + "uninstall", mimeInfoFile.installPath().toString()); + + // + // Add manual cleanup of system files to get rid of + // the default mime type handlers. + // + // Even after mime type is unregisterd with `xdg-mime uninstall` + // command and desktop file deleted with `xdg-desktop-menu uninstall` + // command, records in + // `/usr/share/applications/defaults.list` (Ubuntu 16) or + // `/usr/local/share/applications/defaults.list` (OracleLinux 7) + // files remain referencing deleted mime time and deleted + // desktop file which makes `xdg-mime query default` output name + // of non-existing desktop file. + // + String cleanUpCommand = String.join(" ", + "uninstall_default_mime_handler", + desktopFile.installPath().getFileName().toString(), + String.join(" ", getMimeTypeNamesFromFileAssociations())); + + unregisterFileAssociationsCmd = stringifyShellCommands( + unregisterFileAssociationsCmd, cleanUpCommand); + } + + void addIcon(String mimeType, Path iconFile) { + addIcon(mimeType, iconFile, getSquareSizeOfImage(iconFile.toFile())); + } + + void addIcon(String mimeType, Path iconFile, int imgSize) { + imgSize = normalizeIconSize(imgSize); + final String dashMime = mimeType.replace('/', '-'); + registerIconCmds.add(String.join(" ", "xdg-icon-resource", + "install", "--context", "mimetypes", "--size", + Integer.toString(imgSize), iconFile.toString(), dashMime)); + unregisterIconCmds.add(String.join(" ", "xdg-icon-resource", + "uninstall", dashMime, "--size", Integer.toString(imgSize))); + } + + void applyTo(Map data) { + List cmds = new ArrayList<>(); + + cmds.add(registerDesktopFileCmd); + cmds.add(registerFileAssociationsCmd); + cmds.addAll(registerIconCmds); + data.put(DESKTOP_COMMANDS_INSTALL, stringifyShellCommands(cmds)); + + cmds.clear(); + cmds.add(unregisterDesktopFileCmd); + cmds.add(unregisterFileAssociationsCmd); + cmds.addAll(unregisterIconCmds); + data.put(DESKTOP_COMMANDS_UNINSTALL, stringifyShellCommands(cmds)); + } + + private String registerDesktopFileCmd; + private String unregisterDesktopFileCmd; + + private String registerFileAssociationsCmd; + private String unregisterFileAssociationsCmd; + + private List registerIconCmds; + private List unregisterIconCmds; + } + + /** + * Desktop integration file. xml, icon, etc. + * Resides somewhere in application installation tree. + * Has two paths: + * - path where it should be placed at package build time; + * - path where it should be installed by package manager; + */ + private class DesktopFile { + + DesktopFile(String fileName) { + installPath = thePackage + .installedApplicationLayout() + .destktopIntegrationDirectory().resolve(fileName); + srcPath = thePackage + .sourceApplicationLayout() + .destktopIntegrationDirectory().resolve(fileName); + } + + private final Path installPath; + private final Path srcPath; + + Path installPath() { + return installPath; + } + + Path srcPath() { + return srcPath; + } + } + + private void appendFileAssociation(XMLStreamWriter xml, + FileAssociation assoc) throws XMLStreamException { + + for (var mimeType : assoc.mimeTypes) { + xml.writeStartElement("mime-type"); + xml.writeAttribute("type", mimeType); + + final String description = assoc.description; + if (description != null && !description.isEmpty()) { + xml.writeStartElement("comment"); + xml.writeCharacters(description); + xml.writeEndElement(); + } + + for (String ext : assoc.extensions) { + xml.writeStartElement("glob"); + xml.writeAttribute("pattern", "*." + ext); + xml.writeEndElement(); + } + + xml.writeEndElement(); + } + } + + private void createFileAssociationsMimeInfoFile() throws IOException { + IOUtils.createXml(mimeInfoFile.srcPath(), xml -> { + xml.writeStartElement("mime-info"); + xml.writeDefaultNamespace( + "http://www.freedesktop.org/standards/shared-mime-info"); + + for (var assoc : associations) { + appendFileAssociation(xml, assoc.data); + } + + xml.writeEndElement(); + }); + } + + private void addFileAssociationIconFiles(ShellCommands shellCommands) + throws IOException { + Set processedMimeTypes = new HashSet<>(); + for (var assoc : associations) { + if (assoc.iconSize <= 0) { + // No icon. + continue; + } + + for (var mimeType : assoc.data.mimeTypes) { + if (processedMimeTypes.contains(mimeType)) { + continue; + } + + processedMimeTypes.add(mimeType); + + // Create icon name for mime type from mime type. + DesktopFile faIconFile = new DesktopFile(mimeType.replace( + File.separatorChar, '-') + IOUtils.getSuffix( + assoc.data.iconPath)); + + IOUtils.copyFile(assoc.data.iconPath.toFile(), + faIconFile.srcPath().toFile()); + + shellCommands.addIcon(mimeType, faIconFile.installPath(), + assoc.iconSize); + } + } + } + + private void createDesktopFile(Map data) throws IOException { + List mimeTypes = getMimeTypeNamesFromFileAssociations(); + data.put("DESKTOP_MIMES", "MimeType=" + String.join(";", mimeTypes)); + + // prepare desktop shortcut + desktopFileResource + .setSubstitutionData(data) + .saveToFile(desktopFile.srcPath()); + } + + private List getMimeTypeNamesFromFileAssociations() { + return associations.stream() + .map(fa -> fa.data.mimeTypes) + .flatMap(List::stream) + .collect(Collectors.toUnmodifiableList()); + } + + private static int getSquareSizeOfImage(File f) { + try { + BufferedImage bi = ImageIO.read(f); + return Math.max(bi.getWidth(), bi.getHeight()); + } catch (IOException e) { + Log.verbose(e); + } + return 0; + } + + private static int normalizeIconSize(int iconSize) { + // If register icon with "uncommon" size, it will be ignored. + // So find the best matching "common" size. + List commonIconSizes = List.of(16, 22, 32, 48, 64, 128); + + int idx = Collections.binarySearch(commonIconSizes, iconSize); + if (idx < 0) { + // Given icon size is greater than the largest common icon size. + return commonIconSizes.get(commonIconSizes.size() - 1); + } + + if (idx == 0) { + // Given icon size is less or equal than the smallest common icon size. + return commonIconSizes.get(idx); + } + + int commonIconSize = commonIconSizes.get(idx); + if (iconSize < commonIconSize) { + // It is better to scale down original icon than to scale it up for + // better visual quality. + commonIconSize = commonIconSizes.get(idx - 1); + } + + return commonIconSize; + } + + private static String stringifyShellCommands(String... commands) { + return stringifyShellCommands(Arrays.asList(commands)); + } + + private static String stringifyShellCommands(List commands) { + return String.join(System.lineSeparator(), commands.stream().filter( + s -> s != null && !s.isEmpty()).collect(Collectors.toList())); + } + + private static class LinuxFileAssociation { + LinuxFileAssociation(FileAssociation fa) { + this.data = fa; + if (fa.iconPath != null && Files.isReadable(fa.iconPath)) { + iconSize = getSquareSizeOfImage(fa.iconPath.toFile()); + } else { + iconSize = -1; + } + } + + final FileAssociation data; + final int iconSize; + } + + private final PlatformPackage thePackage; + + private final List associations; + + private final List> launchers; + + private final OverridableResource iconResource; + private final OverridableResource desktopFileResource; + + private final DesktopFile mimeInfoFile; + private final DesktopFile desktopFile; + private final DesktopFile iconFile; + + private final List nestedIntegrations; + + private final Map desktopFileData; + + private static final BundlerParamInfo MENU_GROUP = + new StandardBundlerParam<>( + Arguments.CLIOptions.LINUX_MENU_GROUP.getId(), + String.class, + params -> I18N.getString("param.menu-group.default"), + (s, p) -> s + ); + + private static final StandardBundlerParam SHORTCUT_HINT = + new StandardBundlerParam<>( + Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId(), + Boolean.class, + params -> false, + (s, p) -> (s == null || "null".equalsIgnoreCase(s)) + ? false : Boolean.valueOf(s) + ); +} --- /dev/null 2019-11-18 20:35:02.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/LibProvidersLookup.java 2019-11-18 20:34:58.899461900 -0500 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Builds list of packages providing dynamic libraries for the given set of files. + */ +final public class LibProvidersLookup { + static boolean supported() { + return (new ToolValidator(TOOL_LDD).validate() == null); + } + + public LibProvidersLookup() { + } + + LibProvidersLookup setPackageLookup(PackageLookup v) { + packageLookup = v; + return this; + } + + List execute(Path root) throws IOException { + // Get the list of files in the root for which to look up for needed shared libraries + List allPackageFiles; + try (Stream stream = Files.walk(root)) { + allPackageFiles = stream.filter(Files::isRegularFile).filter( + LibProvidersLookup::canDependOnLibs).collect( + Collectors.toList()); + } + + Collection neededLibs = getNeededLibsForFiles(allPackageFiles); + + // Get the list of unique package names. + List neededPackages = neededLibs.stream().map(libPath -> { + try { + List packageNames = packageLookup.apply(libPath).filter( + Objects::nonNull).filter(Predicate.not(String::isBlank)).distinct().collect( + Collectors.toList()); + Log.verbose(String.format("%s is provided by %s", libPath, packageNames)); + return packageNames; + } catch (IOException ex) { + // Ignore and keep going + Log.verbose(ex); + List packageNames = Collections.emptyList(); + return packageNames; + } + }).flatMap(List::stream).sorted().distinct().collect(Collectors.toList()); + + return neededPackages; + } + + private static List getNeededLibsForFile(Path path) throws IOException { + List result = new ArrayList<>(); + int ret = Executor.of(TOOL_LDD, path.toString()).setOutputConsumer(lines -> { + lines.map(line -> { + Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line); + if (matcher.find()) { + return matcher.group(1); + } + return null; + }).filter(Objects::nonNull).map(Path::of).forEach(result::add); + }).execute(); + + if (ret != 0) { + // objdump failed. This is OK if the tool was applied to not a binary file + return Collections.emptyList(); + } + + return result; + } + + private static Collection getNeededLibsForFiles(List paths) { + // Depending on tool used, the set can contain full paths (ldd) or + // only file names (objdump). + Set allLibs = paths.stream().map(path -> { + List libs; + try { + libs = getNeededLibsForFile(path); + } catch (IOException ex) { + Log.verbose(ex); + libs = Collections.emptyList(); + } + return libs; + }).flatMap(List::stream).collect(Collectors.toSet()); + + // `allLibs` contains names of all .so needed by files from `paths` list. + // If there are mutual dependencies between binaries from `paths` list, + // then names or full paths to these binaries are in `allLibs` set. + // Remove these items from `allLibs`. + Set excludedNames = paths.stream().map(Path::getFileName).collect( + Collectors.toSet()); + Iterator it = allLibs.iterator(); + while (it.hasNext()) { + Path libName = it.next().getFileName(); + if (excludedNames.contains(libName)) { + it.remove(); + } + } + + return allLibs; + } + + private static boolean canDependOnLibs(Path path) { + return path.toFile().canExecute() || path.toString().endsWith(".so"); + } + + @FunctionalInterface + public interface PackageLookup { + Stream apply(Path path) throws IOException; + } + + private PackageLookup packageLookup; + + private static final String TOOL_LDD = "ldd"; + + // + // Typical ldd output: + // + // ldd: warning: you do not have execution permission for `/tmp/jdk.incubator.jpackage17911687595930080396/images/opt/simplepackagetest/lib/runtime/lib/libawt_headless.so' + // linux-vdso.so.1 => (0x00007ffce6bfd000) + // libawt.so => /tmp/jdk.incubator.jpackage17911687595930080396/images/opt/simplepackagetest/lib/runtime/lib/libawt.so (0x00007f4e00c75000) + // libjvm.so => not found + // libjava.so => /tmp/jdk.incubator.jpackage17911687595930080396/images/opt/simplepackagetest/lib/runtime/lib/libjava.so (0x00007f4e00c41000) + // libm.so.6 => /lib64/libm.so.6 (0x00007f4e00834000) + // libdl.so.2 => /lib64/libdl.so.2 (0x00007f4e00630000) + // libc.so.6 => /lib64/libc.so.6 (0x00007f4e00262000) + // libjvm.so => not found + // libjvm.so => not found + // libverify.so => /tmp/jdk.incubator.jpackage17911687595930080396/images/opt/simplepackagetest/lib/runtime/lib/libverify.so (0x00007f4e00c2e000) + // /lib64/ld-linux-x86-64.so.2 (0x00007f4e00b36000) + // libjvm.so => not found + // + private static final Pattern LIB_IN_LDD_OUTPUT_REGEX = Pattern.compile( + "^\\s*\\S+\\s*=>\\s*(\\S+)\\s+\\(0[xX]\\p{XDigit}+\\)"); +} --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java 2019-11-18 20:35:14.387388900 -0500 +++ /dev/null 2019-11-18 20:35:16.000000000 -0500 @@ -1,205 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.ResourceBundle; - -import static jdk.jpackage.internal.StandardBundlerParam.*; - -public class LinuxAppBundler extends AbstractImageBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.LinuxResources"); - - public static final BundlerParamInfo ICON_PNG = - new StandardBundlerParam<>( - "icon.png", - File.class, - params -> { - File f = ICON.fetchFrom(params); - if (f != null && !f.getName().toLowerCase().endsWith(".png")) { - Log.error(MessageFormat.format( - I18N.getString("message.icon-not-png"), f)); - return null; - } - return f; - }, - (s, p) -> new File(s)); - - public static final BundlerParamInfo LINUX_INSTALL_DIR = - new StandardBundlerParam<>( - "linux-install-dir", - String.class, - params -> { - String dir = INSTALL_DIR.fetchFrom(params); - if (dir != null) { - if (dir.endsWith("/")) { - dir = dir.substring(0, dir.length()-1); - } - return dir; - } - return "/opt"; - }, - (s, p) -> s - ); - - public static final BundlerParamInfo LINUX_PACKAGE_DEPENDENCIES = - new StandardBundlerParam<>( - Arguments.CLIOptions.LINUX_PACKAGE_DEPENDENCIES.getId(), - String.class, - params -> { - return ""; - }, - (s, p) -> s - ); - - @Override - public boolean validate(Map p) - throws UnsupportedPlatformException, ConfigException { - try { - if (p == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - - return doValidate(p); - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - private boolean doValidate(Map p) - throws UnsupportedPlatformException, ConfigException { - if (Platform.getPlatform() != Platform.LINUX) { - throw new UnsupportedPlatformException(); - } - - imageBundleValidation(p); - - return true; - } - - // it is static for the sake of sharing with "installer" bundlers - // that may skip calls to validate/bundle in this class! - public static File getRootDir(File outDir, Map p) { - return new File(outDir, APP_NAME.fetchFrom(p)); - } - - public static String getLauncherCfgName(Map p) { - return "app/" + APP_NAME.fetchFrom(p) +".cfg"; - } - - File doBundle(Map p, File outputDirectory, - boolean dependentTask) throws PackagerException { - if (StandardBundlerParam.isRuntimeInstaller(p)) { - return PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); - } else { - return doAppBundle(p, outputDirectory, dependentTask); - } - } - - private File doAppBundle(Map p, - File outputDirectory, boolean dependentTask) throws PackagerException { - try { - File rootDirectory = createRoot(p, outputDirectory, dependentTask, - APP_NAME.fetchFrom(p)); - AbstractAppImageBuilder appBuilder = new LinuxAppImageBuilder(p, - outputDirectory.toPath()); - if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) { - JLinkBundlerHelper.execute(p, appBuilder); - } else { - StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder); - } - return rootDirectory; - } catch (PackagerException pe) { - throw pe; - } catch (Exception ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - @Override - public String getName() { - return I18N.getString("app.bundler.name"); - } - - @Override - public String getDescription() { - return I18N.getString("app.bundler.description"); - } - - @Override - public String getID() { - return "linux.app"; - } - - @Override - public String getBundleType() { - return "IMAGE"; - } - - @Override - public Collection> getBundleParameters() { - return getAppBundleParameters(); - } - - public static Collection> getAppBundleParameters() { - return Arrays.asList( - APP_NAME, - APP_RESOURCES, - ARGUMENTS, - CLASSPATH, - JAVA_OPTIONS, - MAIN_CLASS, - MAIN_JAR, - VERSION, - VERBOSE - ); - } - - @Override - public File execute(Map params, - File outputParentDir) throws PackagerException { - return doBundle(params, outputParentDir, false); - } - - @Override - public boolean supported(boolean runtimeInstaller) { - return (Platform.getPlatform() == Platform.LINUX); - } -} --- /dev/null 2019-11-18 20:35:16.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/LinuxAppBundler.java 2019-11-18 20:35:10.656195300 -0500 @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.text.MessageFormat; +import java.util.*; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +public class LinuxAppBundler extends AbstractImageBundler { + + static final BundlerParamInfo ICON_PNG = + new StandardBundlerParam<>( + "icon.png", + File.class, + params -> { + File f = ICON.fetchFrom(params); + if (f != null && !f.getName().toLowerCase().endsWith(".png")) { + Log.error(MessageFormat.format( + I18N.getString("message.icon-not-png"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + + static final BundlerParamInfo LINUX_INSTALL_DIR = + new StandardBundlerParam<>( + "linux-install-dir", + String.class, + params -> { + String dir = INSTALL_DIR.fetchFrom(params); + if (dir != null) { + if (dir.endsWith("/")) { + dir = dir.substring(0, dir.length()-1); + } + return dir; + } + return "/opt"; + }, + (s, p) -> s + ); + + static final BundlerParamInfo LINUX_PACKAGE_DEPENDENCIES = + new StandardBundlerParam<>( + Arguments.CLIOptions.LINUX_PACKAGE_DEPENDENCIES.getId(), + String.class, + params -> { + return ""; + }, + (s, p) -> s + ); + + @Override + public boolean validate(Map params) + throws ConfigException { + try { + Objects.requireNonNull(params); + return doValidate(params); + } catch (RuntimeException re) { + if (re.getCause() instanceof ConfigException) { + throw (ConfigException) re.getCause(); + } else { + throw new ConfigException(re); + } + } + } + + private boolean doValidate(Map params) + throws ConfigException { + + imageBundleValidation(params); + + return true; + } + + File doBundle(Map params, File outputDirectory, + boolean dependentTask) throws PackagerException { + if (StandardBundlerParam.isRuntimeInstaller(params)) { + return PREDEFINED_RUNTIME_IMAGE.fetchFrom(params); + } else { + return doAppBundle(params, outputDirectory, dependentTask); + } + } + + private File doAppBundle(Map params, + File outputDirectory, boolean dependentTask) + throws PackagerException { + try { + File rootDirectory = createRoot(params, outputDirectory, + dependentTask, APP_NAME.fetchFrom(params)); + AbstractAppImageBuilder appBuilder = new LinuxAppImageBuilder( + params, outputDirectory.toPath()); + if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(params) == null ) { + JLinkBundlerHelper.execute(params, appBuilder); + } else { + StandardBundlerParam.copyPredefinedRuntimeImage( + params, appBuilder); + } + return rootDirectory; + } catch (PackagerException pe) { + throw pe; + } catch (Exception ex) { + Log.verbose(ex); + throw new PackagerException(ex); + } + } + + @Override + public String getName() { + return I18N.getString("app.bundler.name"); + } + + @Override + public String getID() { + return "linux.app"; + } + + @Override + public String getBundleType() { + return "IMAGE"; + } + + @Override + public File execute(Map params, + File outputParentDir) throws PackagerException { + return doBundle(params, outputParentDir, false); + } + + @Override + public boolean supported(boolean runtimeInstaller) { + return true; + } + + @Override + public boolean isDefault() { + return false; + } + +} --- /dev/null 2019-11-18 20:35:36.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/LinuxAppImageBuilder.java 2019-11-18 20:35:33.413203500 -0500 @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +public class LinuxAppImageBuilder extends AbstractAppImageBuilder { + + private static final String LIBRARY_NAME = "libapplauncher.so"; + final static String DEFAULT_ICON = "java32.png"; + + private final ApplicationLayout appLayout; + + public static final BundlerParamInfo ICON_PNG = + new StandardBundlerParam<>( + "icon.png", + File.class, + params -> { + File f = ICON.fetchFrom(params); + if (f != null && !f.getName().toLowerCase().endsWith(".png")) { + Log.error(MessageFormat.format(I18N.getString( + "message.icon-not-png"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + + private static ApplicationLayout createAppLayout(Map params, + Path imageOutDir) { + return ApplicationLayout.linuxAppImage().resolveAt( + imageOutDir.resolve(APP_NAME.fetchFrom(params))); + } + + public LinuxAppImageBuilder(Map params, Path imageOutDir) + throws IOException { + super(params, createAppLayout(params, imageOutDir).runtimeDirectory()); + + appLayout = createAppLayout(params, imageOutDir); + } + + private void writeEntry(InputStream in, Path dstFile) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.copy(in, dstFile); + } + + public static String getLauncherName(Map params) { + return APP_NAME.fetchFrom(params); + } + + private Path getLauncherCfgPath(Map params) { + return appLayout.appDirectory().resolve( + APP_NAME.fetchFrom(params) + ".cfg"); + } + + @Override + public Path getAppDir() { + return appLayout.appDirectory(); + } + + @Override + public Path getAppModsDir() { + return appLayout.appModsDirectory(); + } + + @Override + protected String getCfgAppDir() { + return Path.of("$ROOTDIR").resolve( + ApplicationLayout.linuxAppImage().appDirectory()).toString() + + File.separator; + } + + @Override + protected String getCfgRuntimeDir() { + return Path.of("$ROOTDIR").resolve( + ApplicationLayout.linuxAppImage().runtimeDirectory()).toString(); + } + + @Override + public void prepareApplicationFiles(Map params) + throws IOException { + Map originalParams = new HashMap<>(params); + + appLayout.roots().stream().forEach(dir -> { + try { + IOUtils.writableOutputDir(dir); + } catch (PackagerException pe) { + throw new RuntimeException(pe); + } + }); + + // create the primary launcher + createLauncherForEntryPoint(params); + + // Copy library to the launcher folder + try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { + writeEntry(is_lib, appLayout.dllDirectory().resolve(LIBRARY_NAME)); + } + + // create the additional launchers, if any + List> entryPoints + = StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); + for (Map entryPoint : entryPoints) { + createLauncherForEntryPoint( + AddLauncherArguments.merge(originalParams, entryPoint)); + } + + // Copy class path entries to Java folder + copyApplication(params); + + // Copy icon to Resources folder + copyIcon(params); + } + + @Override + public void prepareJreFiles(Map params) + throws IOException {} + + private void createLauncherForEntryPoint( + Map params) throws IOException { + // Copy executable to launchers folder + Path executableFile = appLayout.launchersDirectory().resolve(getLauncherName(params)); + try (InputStream is_launcher = + getResourceAsStream("jpackageapplauncher")) { + writeEntry(is_launcher, executableFile); + } + + executableFile.toFile().setExecutable(true, false); + executableFile.toFile().setWritable(true, true); + + writeCfgFile(params, getLauncherCfgPath(params).toFile()); + } + + private void copyIcon(Map params) + throws IOException { + + Path iconTarget = appLayout.destktopIntegrationDirectory().resolve( + APP_NAME.fetchFrom(params) + IOUtils.getSuffix(Path.of( + DEFAULT_ICON))); + + createResource(DEFAULT_ICON, params) + .setCategory("icon") + .setExternal(ICON_PNG.fetchFrom(params)) + .saveToFile(iconTarget); + } + + private void copyApplication(Map params) + throws IOException { + for (RelativeFileSet appResources : + APP_RESOURCES_LIST.fetchFrom(params)) { + if (appResources == null) { + throw new RuntimeException("Null app resources?"); + } + File srcdir = appResources.getBaseDirectory(); + for (String fname : appResources.getIncludedFiles()) { + copyEntry(appLayout.appDirectory(), srcdir, fname); + } + } + } + +} --- /dev/null 2019-11-18 20:35:48.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/LinuxDebBundler.java 2019-11-18 20:35:44.968421500 -0500 @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.*; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.text.MessageFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import static jdk.incubator.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + + +public class LinuxDebBundler extends LinuxPackageBundler { + + // Debian rules for package naming are used here + // https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Source + // + // Package names must consist only of lower case letters (a-z), + // digits (0-9), plus (+) and minus (-) signs, and periods (.). + // They must be at least two characters long and + // must start with an alphanumeric character. + // + private static final Pattern DEB_PACKAGE_NAME_PATTERN = + Pattern.compile("^[a-z][a-z\\d\\+\\-\\.]+"); + + private static final BundlerParamInfo PACKAGE_NAME = + new StandardBundlerParam<> ( + Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId(), + String.class, + params -> { + String nm = APP_NAME.fetchFrom(params); + + if (nm == null) return null; + + // make sure to lower case and spaces/underscores become dashes + nm = nm.toLowerCase().replaceAll("[ _]", "-"); + return nm; + }, + (s, p) -> { + if (!DEB_PACKAGE_NAME_PATTERN.matcher(s).matches()) { + throw new IllegalArgumentException(new ConfigException( + MessageFormat.format(I18N.getString( + "error.invalid-value-for-package-name"), s), + I18N.getString( + "error.invalid-value-for-package-name.advice"))); + } + + return s; + }); + + private final static String TOOL_DPKG_DEB = "dpkg-deb"; + private final static String TOOL_DPKG = "dpkg"; + private final static String TOOL_FAKEROOT = "fakeroot"; + + private final static String DEB_ARCH; + static { + String debArch; + try { + debArch = Executor.of(TOOL_DPKG, "--print-architecture").saveOutput( + true).executeExpectSuccess().getOutput().get(0); + } catch (IOException ex) { + debArch = null; + } + DEB_ARCH = debArch; + } + + private static final BundlerParamInfo FULL_PACKAGE_NAME = + new StandardBundlerParam<>( + "linux.deb.fullPackageName", String.class, params -> { + return PACKAGE_NAME.fetchFrom(params) + + "_" + VERSION.fetchFrom(params) + + "-" + RELEASE.fetchFrom(params) + + "_" + DEB_ARCH; + }, (s, p) -> s); + + private static final BundlerParamInfo EMAIL = + new StandardBundlerParam<> ( + Arguments.CLIOptions.LINUX_DEB_MAINTAINER.getId(), + String.class, + params -> "Unknown", + (s, p) -> s); + + private static final BundlerParamInfo MAINTAINER = + new StandardBundlerParam<> ( + BundleParams.PARAM_MAINTAINER, + String.class, + params -> VENDOR.fetchFrom(params) + " <" + + EMAIL.fetchFrom(params) + ">", + (s, p) -> s); + + private static final BundlerParamInfo SECTION = + new StandardBundlerParam<>( + Arguments.CLIOptions.LINUX_CATEGORY.getId(), + String.class, + params -> "misc", + (s, p) -> s); + + private static final BundlerParamInfo LICENSE_TEXT = + new StandardBundlerParam<> ( + "linux.deb.licenseText", + String.class, + params -> { + try { + String licenseFile = LICENSE_FILE.fetchFrom(params); + if (licenseFile != null) { + return Files.readString(Path.of(licenseFile)); + } + } catch (IOException e) { + Log.verbose(e); + } + return "Unknown"; + }, + (s, p) -> s); + + public LinuxDebBundler() { + super(PACKAGE_NAME); + } + + @Override + public void doValidate(Map params) + throws ConfigException { + + // Show warning if license file is missing + if (LICENSE_FILE.fetchFrom(params) == null) { + Log.verbose(I18N.getString("message.debs-like-licenses")); + } + } + + @Override + protected List getToolValidators( + Map params) { + return Stream.of(TOOL_DPKG_DEB, TOOL_DPKG, TOOL_FAKEROOT).map( + ToolValidator::new).collect(Collectors.toList()); + } + + @Override + protected File buildPackageBundle( + Map replacementData, + Map params, File outputParentDir) throws + PackagerException, IOException { + + prepareProjectConfig(replacementData, params); + adjustPermissionsRecursive(createMetaPackage(params).sourceRoot().toFile()); + return buildDeb(params, outputParentDir); + } + + private static final Pattern PACKAGE_NAME_REGEX = Pattern.compile("^(^\\S+):"); + + @Override + protected void initLibProvidersLookup( + Map params, + LibProvidersLookup libProvidersLookup) { + + // + // `dpkg -S` command does glob pattern lookup. If not the absolute path + // to the file is specified it might return mltiple package names. + // Even for full paths multiple package names can be returned as + // it is OK for multiple packages to provide the same file. `/opt` + // directory is such an example. So we have to deal with multiple + // packages per file situation. + // + // E.g.: `dpkg -S libc.so.6` command reports three packages: + // libc6-x32: /libx32/libc.so.6 + // libc6:amd64: /lib/x86_64-linux-gnu/libc.so.6 + // libc6-i386: /lib32/libc.so.6 + // `:amd64` is architecture suffix and can (should) be dropped. + // Still need to decide what package to choose from three. + // libc6-x32 and libc6-i386 both depend on libc6: + // $ dpkg -s libc6-x32 + // Package: libc6-x32 + // Status: install ok installed + // Priority: optional + // Section: libs + // Installed-Size: 10840 + // Maintainer: Ubuntu Developers + // Architecture: amd64 + // Source: glibc + // Version: 2.23-0ubuntu10 + // Depends: libc6 (= 2.23-0ubuntu10) + // + // We can dive into tracking dependencies, but this would be overly + // complicated. + // + // For simplicity lets consider the following rules: + // 1. If there is one item in `dpkg -S` output, accept it. + // 2. If there are multiple items in `dpkg -S` output and there is at + // least one item with the default arch suffix (DEB_ARCH), + // accept only these items. + // 3. If there are multiple items in `dpkg -S` output and there are + // no with the default arch suffix (DEB_ARCH), accept all items. + // So lets use this heuristics: don't accept packages for whom + // `dpkg -p` command fails. + // 4. Arch suffix should be stripped from accepted package names. + // + + libProvidersLookup.setPackageLookup(file -> { + Set archPackages = new HashSet<>(); + Set otherPackages = new HashSet<>(); + + Executor.of(TOOL_DPKG, "-S", file.toString()) + .saveOutput(true).executeExpectSuccess() + .getOutput().forEach(line -> { + Matcher matcher = PACKAGE_NAME_REGEX.matcher(line); + if (matcher.find()) { + String name = matcher.group(1); + if (name.endsWith(":" + DEB_ARCH)) { + // Strip arch suffix + name = name.substring(0, + name.length() - (DEB_ARCH.length() + 1)); + archPackages.add(name); + } else { + otherPackages.add(name); + } + } + }); + + if (!archPackages.isEmpty()) { + return archPackages.stream(); + } + return otherPackages.stream(); + }); + } + + @Override + protected List verifyOutputBundle( + Map params, Path packageBundle) { + List errors = new ArrayList<>(); + + String controlFileName = "control"; + + List properties = List.of( + new PackageProperty("Package", PACKAGE_NAME.fetchFrom(params), + "APPLICATION_PACKAGE", controlFileName), + new PackageProperty("Version", String.format("%s-%s", + VERSION.fetchFrom(params), RELEASE.fetchFrom(params)), + "APPLICATION_VERSION-APPLICATION_RELEASE", + controlFileName), + new PackageProperty("Architecture", DEB_ARCH, "APPLICATION_ARCH", + controlFileName)); + + List cmdline = new ArrayList<>(List.of(TOOL_DPKG_DEB, "-f", + packageBundle.toString())); + properties.forEach(property -> cmdline.add(property.name)); + try { + Map actualValues = Executor.of(cmdline.toArray(String[]::new)) + .saveOutput(true) + .executeExpectSuccess() + .getOutput().stream() + .map(line -> line.split(":\\s+", 2)) + .collect(Collectors.toMap( + components -> components[0], + components -> components[1])); + properties.forEach(property -> errors.add(property.verifyValue( + actualValues.get(property.name)))); + } catch (IOException ex) { + // Ignore error as it is not critical. Just report it. + Log.verbose(ex); + } + + return errors; + } + + /* + * set permissions with a string like "rwxr-xr-x" + * + * This cannot be directly backport to 22u which is built with 1.6 + */ + private void setPermissions(File file, String permissions) { + Set filePermissions = + PosixFilePermissions.fromString(permissions); + try { + if (file.exists()) { + Files.setPosixFilePermissions(file.toPath(), filePermissions); + } + } catch (IOException ex) { + Log.error(ex.getMessage()); + Log.verbose(ex); + } + + } + + public static boolean isDebian() { + // we are just going to run "dpkg -s coreutils" and assume Debian + // or deritive if no error is returned. + try { + Executor.of(TOOL_DPKG, "-s", "coreutils").executeExpectSuccess(); + return true; + } catch (IOException e) { + // just fall thru + } + return false; + } + + private void adjustPermissionsRecursive(File dir) throws IOException { + Files.walkFileTree(dir.toPath(), new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, + BasicFileAttributes attrs) + throws IOException { + if (file.endsWith(".so") || !Files.isExecutable(file)) { + setPermissions(file.toFile(), "rw-r--r--"); + } else if (Files.isExecutable(file)) { + setPermissions(file.toFile(), "rwxr-xr-x"); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException e) + throws IOException { + if (e == null) { + setPermissions(dir.toFile(), "rwxr-xr-x"); + return FileVisitResult.CONTINUE; + } else { + // directory iteration failed + throw e; + } + } + }); + } + + private class DebianFile { + + DebianFile(Path dstFilePath, String comment) { + this.dstFilePath = dstFilePath; + this.comment = comment; + } + + DebianFile setExecutable() { + permissions = "rwxr-xr-x"; + return this; + } + + void create(Map data, Map params) + throws IOException { + createResource("template." + dstFilePath.getFileName().toString(), + params) + .setCategory(I18N.getString(comment)) + .setSubstitutionData(data) + .saveToFile(dstFilePath); + if (permissions != null) { + setPermissions(dstFilePath.toFile(), permissions); + } + } + + private final Path dstFilePath; + private final String comment; + private String permissions; + } + + private void prepareProjectConfig(Map data, + Map params) throws IOException { + + Path configDir = createMetaPackage(params).sourceRoot().resolve("DEBIAN"); + List debianFiles = new ArrayList<>(); + debianFiles.add(new DebianFile( + configDir.resolve("control"), + "resource.deb-control-file")); + debianFiles.add(new DebianFile( + configDir.resolve("preinst"), + "resource.deb-preinstall-script").setExecutable()); + debianFiles.add(new DebianFile( + configDir.resolve("prerm"), + "resource.deb-prerm-script").setExecutable()); + debianFiles.add(new DebianFile( + configDir.resolve("postinst"), + "resource.deb-postinstall-script").setExecutable()); + debianFiles.add(new DebianFile( + configDir.resolve("postrm"), + "resource.deb-postrm-script").setExecutable()); + + if (!StandardBundlerParam.isRuntimeInstaller(params)) { + debianFiles.add(new DebianFile( + getConfig_CopyrightFile(params).toPath(), + "resource.copyright-file")); + } + + for (DebianFile debianFile : debianFiles) { + debianFile.create(data, params); + } + } + + @Override + protected Map createReplacementData( + Map params) throws IOException { + Map data = new HashMap<>(); + + data.put("APPLICATION_MAINTAINER", MAINTAINER.fetchFrom(params)); + data.put("APPLICATION_SECTION", SECTION.fetchFrom(params)); + data.put("APPLICATION_COPYRIGHT", COPYRIGHT.fetchFrom(params)); + data.put("APPLICATION_LICENSE_TEXT", LICENSE_TEXT.fetchFrom(params)); + data.put("APPLICATION_ARCH", DEB_ARCH); + data.put("APPLICATION_INSTALLED_SIZE", Long.toString( + createMetaPackage(params).sourceApplicationLayout().sizeInBytes() >> 10)); + + return data; + } + + private File getConfig_CopyrightFile(Map params) { + PlatformPackage thePackage = createMetaPackage(params); + return thePackage.sourceRoot().resolve(Path.of(".", + LINUX_INSTALL_DIR.fetchFrom(params), PACKAGE_NAME.fetchFrom( + params), "share/doc/copyright")).toFile(); + } + + private File buildDeb(Map params, + File outdir) throws IOException { + File outFile = new File(outdir, + FULL_PACKAGE_NAME.fetchFrom(params)+".deb"); + Log.verbose(MessageFormat.format(I18N.getString( + "message.outputting-to-location"), outFile.getAbsolutePath())); + + PlatformPackage thePackage = createMetaPackage(params); + + List cmdline = new ArrayList<>(); + cmdline.addAll(List.of(TOOL_FAKEROOT, TOOL_DPKG_DEB)); + if (Log.isVerbose()) { + cmdline.add("--verbose"); + } + cmdline.addAll(List.of("-b", thePackage.sourceRoot().toString(), + outFile.getAbsolutePath())); + + // run dpkg + Executor.of(cmdline.toArray(String[]::new)).executeExpectSuccess(); + + Log.verbose(MessageFormat.format(I18N.getString( + "message.output-to-location"), outFile.getAbsolutePath())); + + return outFile; + } + + @Override + public String getName() { + return I18N.getString("deb.bundler.name"); + } + + @Override + public String getID() { + return "deb"; + } + + @Override + public boolean supported(boolean runtimeInstaller) { + return Platform.isLinux() && (new ToolValidator(TOOL_DPKG_DEB).validate() == null); + } + + @Override + public boolean isDefault() { + return isDebian(); + } +} --- /dev/null 2019-11-18 20:35:59.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/LinuxPackageBundler.java 2019-11-18 20:35:56.477067100 -0500 @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.*; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.*; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import static jdk.incubator.jpackage.internal.DesktopIntegration.*; +import static jdk.incubator.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR; +import static jdk.incubator.jpackage.internal.LinuxAppBundler.LINUX_PACKAGE_DEPENDENCIES; +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + + +abstract class LinuxPackageBundler extends AbstractBundler { + + LinuxPackageBundler(BundlerParamInfo packageName) { + this.packageName = packageName; + } + + @Override + final public boolean validate(Map params) + throws ConfigException { + + // run basic validation to ensure requirements are met + // we are not interested in return code, only possible exception + APP_BUNDLER.fetchFrom(params).validate(params); + + validateInstallDir(LINUX_INSTALL_DIR.fetchFrom(params)); + + validateFileAssociations(FILE_ASSOCIATIONS.fetchFrom(params)); + + // If package name has some restrictions, the string converter will + // throw an exception if invalid + packageName.getStringConverter().apply(packageName.fetchFrom(params), + params); + + for (var validator: getToolValidators(params)) { + ConfigException ex = validator.validate(); + if (ex != null) { + throw ex; + } + } + + withFindNeededPackages = LibProvidersLookup.supported(); + if (!withFindNeededPackages) { + final String advice; + if ("deb".equals(getID())) { + advice = "message.deb-ldd-not-available.advice"; + } else { + advice = "message.rpm-ldd-not-available.advice"; + } + // Let user know package dependencies will not be generated. + Log.error(String.format("%s\n%s", I18N.getString( + "message.ldd-not-available"), I18N.getString(advice))); + } + + // Packaging specific validation + doValidate(params); + + return true; + } + + @Override + final public String getBundleType() { + return "INSTALLER"; + } + + @Override + final public File execute(Map params, + File outputParentDir) throws PackagerException { + IOUtils.writableOutputDir(outputParentDir.toPath()); + + PlatformPackage thePackage = createMetaPackage(params); + + Function initAppImageLayout = imageRoot -> { + ApplicationLayout layout = appImageLayout(params); + layout.pathGroup().setPath(new Object(), + AppImageFile.getPathInAppImage(Path.of(""))); + return layout.resolveAt(imageRoot.toPath()); + }; + + try { + File appImage = StandardBundlerParam.getPredefinedAppImage(params); + + // we either have an application image or need to build one + if (appImage != null) { + initAppImageLayout.apply(appImage).copy( + thePackage.sourceApplicationLayout()); + } else { + appImage = APP_BUNDLER.fetchFrom(params).doBundle(params, + thePackage.sourceRoot().toFile(), true); + ApplicationLayout srcAppLayout = initAppImageLayout.apply( + appImage); + if (appImage.equals(PREDEFINED_RUNTIME_IMAGE.fetchFrom(params))) { + // Application image points to run-time image. + // Copy it. + srcAppLayout.copy(thePackage.sourceApplicationLayout()); + } else { + // Application image is a newly created directory tree. + // Move it. + srcAppLayout.move(thePackage.sourceApplicationLayout()); + if (appImage.exists()) { + // Empty app image directory might remain after all application + // directories have been moved. + appImage.delete(); + } + } + } + + if (!StandardBundlerParam.isRuntimeInstaller(params)) { + desktopIntegration = new DesktopIntegration(thePackage, params); + } else { + desktopIntegration = null; + } + + Map data = createDefaultReplacementData(params); + if (desktopIntegration != null) { + data.putAll(desktopIntegration.create()); + } else { + Stream.of(DESKTOP_COMMANDS_INSTALL, DESKTOP_COMMANDS_UNINSTALL, + UTILITY_SCRIPTS).forEach(v -> data.put(v, "")); + } + + data.putAll(createReplacementData(params)); + + File packageBundle = buildPackageBundle(Collections.unmodifiableMap( + data), params, outputParentDir); + + verifyOutputBundle(params, packageBundle.toPath()).stream() + .filter(Objects::nonNull) + .forEachOrdered(ex -> { + Log.verbose(ex.getLocalizedMessage()); + Log.verbose(ex.getAdvice()); + }); + + return packageBundle; + } catch (IOException ex) { + Log.verbose(ex); + throw new PackagerException(ex); + } + } + + private List getListOfNeededPackages( + Map params) throws IOException { + + PlatformPackage thePackage = createMetaPackage(params); + + final List xdgUtilsPackage; + if (desktopIntegration != null) { + xdgUtilsPackage = desktopIntegration.requiredPackages(); + } else { + xdgUtilsPackage = Collections.emptyList(); + } + + final List neededLibPackages; + if (withFindNeededPackages) { + LibProvidersLookup lookup = new LibProvidersLookup(); + initLibProvidersLookup(params, lookup); + + neededLibPackages = lookup.execute(thePackage.sourceRoot()); + } else { + neededLibPackages = Collections.emptyList(); + } + + // Merge all package lists together. + // Filter out empty names, sort and remove duplicates. + List result = Stream.of(xdgUtilsPackage, neededLibPackages).flatMap( + List::stream).filter(Predicate.not(String::isEmpty)).sorted().distinct().collect( + Collectors.toList()); + + Log.verbose(String.format("Required packages: %s", result)); + + return result; + } + + private Map createDefaultReplacementData( + Map params) throws IOException { + Map data = new HashMap<>(); + + data.put("APPLICATION_PACKAGE", createMetaPackage(params).name()); + data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params)); + data.put("APPLICATION_VERSION", VERSION.fetchFrom(params)); + data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params)); + data.put("APPLICATION_RELEASE", RELEASE.fetchFrom(params)); + + String defaultDeps = String.join(", ", getListOfNeededPackages(params)); + String customDeps = LINUX_PACKAGE_DEPENDENCIES.fetchFrom(params).strip(); + if (!customDeps.isEmpty() && !defaultDeps.isEmpty()) { + customDeps = ", " + customDeps; + } + data.put("PACKAGE_DEFAULT_DEPENDENCIES", defaultDeps); + data.put("PACKAGE_CUSTOM_DEPENDENCIES", customDeps); + + return data; + } + + abstract protected List verifyOutputBundle( + Map params, Path packageBundle); + + abstract protected void initLibProvidersLookup( + Map params, + LibProvidersLookup libProvidersLookup); + + abstract protected List getToolValidators( + Map params); + + abstract protected void doValidate(Map params) + throws ConfigException; + + abstract protected Map createReplacementData( + Map params) throws IOException; + + abstract protected File buildPackageBundle( + Map replacementData, + Map params, File outputParentDir) throws + PackagerException, IOException; + + final protected PlatformPackage createMetaPackage( + Map params) { + return new PlatformPackage() { + @Override + public String name() { + return packageName.fetchFrom(params); + } + + @Override + public Path sourceRoot() { + return IMAGES_ROOT.fetchFrom(params).toPath().toAbsolutePath(); + } + + @Override + public ApplicationLayout sourceApplicationLayout() { + return appImageLayout(params).resolveAt( + applicationInstallDir(sourceRoot())); + } + + @Override + public ApplicationLayout installedApplicationLayout() { + return appImageLayout(params).resolveAt( + applicationInstallDir(Path.of("/"))); + } + + private Path applicationInstallDir(Path root) { + Path installDir = Path.of(LINUX_INSTALL_DIR.fetchFrom(params), + name()); + if (installDir.isAbsolute()) { + installDir = Path.of("." + installDir.toString()).normalize(); + } + return root.resolve(installDir); + } + }; + } + + private ApplicationLayout appImageLayout( + Map params) { + if (StandardBundlerParam.isRuntimeInstaller(params)) { + return ApplicationLayout.javaRuntime(); + } + return ApplicationLayout.linuxAppImage(); + } + + private static void validateInstallDir(String installDir) throws + ConfigException { + if (installDir.startsWith("/usr/") || installDir.equals("/usr")) { + throw new ConfigException(MessageFormat.format(I18N.getString( + "error.unsupported-install-dir"), installDir), null); + } + + if (installDir.isEmpty()) { + throw new ConfigException(MessageFormat.format(I18N.getString( + "error.invalid-install-dir"), "/"), null); + } + + boolean valid = false; + try { + final Path installDirPath = Path.of(installDir); + valid = installDirPath.isAbsolute(); + if (valid && !installDirPath.normalize().toString().equals( + installDirPath.toString())) { + // Don't allow '/opt/foo/..' or /opt/. + valid = false; + } + } catch (InvalidPathException ex) { + } + + if (!valid) { + throw new ConfigException(MessageFormat.format(I18N.getString( + "error.invalid-install-dir"), installDir), null); + } + } + + private static void validateFileAssociations( + List> associations) throws + ConfigException { + // only one mime type per association, at least one file extention + int assocIdx = 0; + for (var assoc : associations) { + ++assocIdx; + List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); + if (mimes == null || mimes.isEmpty()) { + String msgKey = "error.no-content-types-for-file-association"; + throw new ConfigException( + MessageFormat.format(I18N.getString(msgKey), assocIdx), + I18N.getString(msgKey + ".advise")); + + } + + if (mimes.size() > 1) { + String msgKey = "error.too-many-content-types-for-file-association"; + throw new ConfigException( + MessageFormat.format(I18N.getString(msgKey), assocIdx), + I18N.getString(msgKey + ".advise")); + } + } + } + + private final BundlerParamInfo packageName; + private boolean withFindNeededPackages; + private DesktopIntegration desktopIntegration; + + private static final BundlerParamInfo APP_BUNDLER = + new StandardBundlerParam<>( + "linux.app.bundler", + LinuxAppBundler.class, + (params) -> new LinuxAppBundler(), + null + ); + +} --- /dev/null 2019-11-18 20:36:11.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/LinuxRpmBundler.java 2019-11-18 20:36:07.887380700 -0500 @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.*; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; +import static jdk.incubator.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; + +/** + * There are two command line options to configure license information for RPM + * packaging: --linux-rpm-license-type and --license-file. Value of + * --linux-rpm-license-type command line option configures "License:" section + * of RPM spec. Value of --license-file command line option specifies a license + * file to be added to the package. License file is a sort of documentation file + * but it will be installed even if user selects an option to install the + * package without documentation. --linux-rpm-license-type is the primary option + * to set license information. --license-file makes little sense in case of RPM + * packaging. + */ +public class LinuxRpmBundler extends LinuxPackageBundler { + + // Fedora rules for package naming are used here + // https://fedoraproject.org/wiki/Packaging:NamingGuidelines?rd=Packaging/NamingGuidelines + // + // all Fedora packages must be named using only the following ASCII + // characters. These characters are displayed here: + // + // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+ + // + private static final Pattern RPM_PACKAGE_NAME_PATTERN = + Pattern.compile("[a-z\\d\\+\\-\\.\\_]+", Pattern.CASE_INSENSITIVE); + + public static final BundlerParamInfo PACKAGE_NAME = + new StandardBundlerParam<> ( + Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId(), + String.class, + params -> { + String nm = APP_NAME.fetchFrom(params); + if (nm == null) return null; + + // make sure to lower case and spaces become dashes + nm = nm.toLowerCase().replaceAll("[ ]", "-"); + + return nm; + }, + (s, p) -> { + if (!RPM_PACKAGE_NAME_PATTERN.matcher(s).matches()) { + String msgKey = "error.invalid-value-for-package-name"; + throw new IllegalArgumentException( + new ConfigException(MessageFormat.format( + I18N.getString(msgKey), s), + I18N.getString(msgKey + ".advice"))); + } + + return s; + } + ); + + public static final BundlerParamInfo LICENSE_TYPE = + new StandardBundlerParam<>( + Arguments.CLIOptions.LINUX_RPM_LICENSE_TYPE.getId(), + String.class, + params -> I18N.getString("param.license-type.default"), + (s, p) -> s + ); + + public static final BundlerParamInfo GROUP = + new StandardBundlerParam<>( + Arguments.CLIOptions.LINUX_CATEGORY.getId(), + String.class, + params -> null, + (s, p) -> s); + + private final static String DEFAULT_SPEC_TEMPLATE = "template.spec"; + + public final static String TOOL_RPM = "rpm"; + public final static String TOOL_RPMBUILD = "rpmbuild"; + public final static DottedVersion TOOL_RPMBUILD_MIN_VERSION = DottedVersion.lazy( + "4.0"); + + public LinuxRpmBundler() { + super(PACKAGE_NAME); + } + + @Override + public void doValidate(Map params) + throws ConfigException { + } + + private static ToolValidator createRpmbuildToolValidator() { + Pattern pattern = Pattern.compile(" (\\d+\\.\\d+)"); + return new ToolValidator(TOOL_RPMBUILD).setMinimalVersion( + TOOL_RPMBUILD_MIN_VERSION).setVersionParser(lines -> { + String versionString = lines.limit(1).collect( + Collectors.toList()).get(0); + Matcher matcher = pattern.matcher(versionString); + if (matcher.find()) { + return matcher.group(1); + } + return null; + }); + } + + @Override + protected List getToolValidators( + Map params) { + return List.of(createRpmbuildToolValidator()); + } + + @Override + protected File buildPackageBundle( + Map replacementData, + Map params, File outputParentDir) throws + PackagerException, IOException { + + Path specFile = specFile(params); + + // prepare spec file + createResource(DEFAULT_SPEC_TEMPLATE, params) + .setCategory(I18N.getString("resource.rpm-spec-file")) + .setSubstitutionData(replacementData) + .saveToFile(specFile); + + return buildRPM(params, outputParentDir.toPath()).toFile(); + } + + @Override + protected Map createReplacementData( + Map params) throws IOException { + Map data = new HashMap<>(); + + data.put("APPLICATION_DIRECTORY", Path.of(LINUX_INSTALL_DIR.fetchFrom( + params), PACKAGE_NAME.fetchFrom(params)).toString()); + data.put("APPLICATION_SUMMARY", APP_NAME.fetchFrom(params)); + data.put("APPLICATION_LICENSE_TYPE", LICENSE_TYPE.fetchFrom(params)); + + String licenseFile = LICENSE_FILE.fetchFrom(params); + if (licenseFile != null) { + licenseFile = Path.of(licenseFile).toAbsolutePath().normalize().toString(); + } + data.put("APPLICATION_LICENSE_FILE", licenseFile); + data.put("APPLICATION_GROUP", GROUP.fetchFrom(params)); + + return data; + } + + @Override + protected void initLibProvidersLookup( + Map params, + LibProvidersLookup libProvidersLookup) { + libProvidersLookup.setPackageLookup(file -> { + return Executor.of(TOOL_RPM, + "-q", "--queryformat", "%{name}\\n", + "-q", "--whatprovides", file.toString()) + .saveOutput(true).executeExpectSuccess().getOutput().stream(); + }); + } + + @Override + protected List verifyOutputBundle( + Map params, Path packageBundle) { + List errors = new ArrayList<>(); + + String specFileName = specFile(params).getFileName().toString(); + + try { + List properties = List.of( + new PackageProperty("Name", PACKAGE_NAME.fetchFrom(params), + "APPLICATION_PACKAGE", specFileName), + new PackageProperty("Version", VERSION.fetchFrom(params), + "APPLICATION_VERSION", specFileName), + new PackageProperty("Release", RELEASE.fetchFrom(params), + "APPLICATION_RELEASE", specFileName), + new PackageProperty("Arch", rpmArch(), null, specFileName)); + + List actualValues = Executor.of(TOOL_RPM, "-qp", "--queryformat", + properties.stream().map(entry -> String.format("%%{%s}", + entry.name)).collect(Collectors.joining("\\n")), + packageBundle.toString()).saveOutput(true).executeExpectSuccess().getOutput(); + + Iterator actualValuesIt = actualValues.iterator(); + properties.forEach(property -> errors.add(property.verifyValue( + actualValuesIt.next()))); + } catch (IOException ex) { + // Ignore error as it is not critical. Just report it. + Log.verbose(ex); + } + + return errors; + } + + /** + * Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is + * mandatory for rpm packaging, try it first. rpm is optional and may not be + * available, use as the last resort. + */ + private enum RpmArchReader { + Rpmbuild(TOOL_RPMBUILD, "--eval=%{_target_cpu}"), + Rpm(TOOL_RPM, "--eval=%{_target_cpu}"); + + RpmArchReader(String... cmdline) { + this.cmdline = cmdline; + } + + String getRpmArch() throws IOException { + Executor exec = Executor.of(cmdline).saveOutput(true); + if (this == values()[values().length - 1]) { + exec.executeExpectSuccess(); + } else if (exec.execute() != 0) { + return null; + } + + return exec.getOutput().get(0); + } + + private final String[] cmdline; + } + + private String rpmArch() throws IOException { + if (rpmArch == null) { + for (var rpmArchReader : RpmArchReader.values()) { + rpmArch = rpmArchReader.getRpmArch(); + if (rpmArch != null) { + break; + } + } + } + return rpmArch; + } + + private Path specFile(Map params) { + return TEMP_ROOT.fetchFrom(params).toPath().resolve(Path.of("SPECS", + PACKAGE_NAME.fetchFrom(params) + ".spec")); + } + + private Path buildRPM(Map params, + Path outdir) throws IOException { + + Path rpmFile = outdir.toAbsolutePath().resolve(String.format( + "%s-%s-%s.%s.rpm", PACKAGE_NAME.fetchFrom(params), + VERSION.fetchFrom(params), RELEASE.fetchFrom(params), rpmArch())); + + Log.verbose(MessageFormat.format(I18N.getString( + "message.outputting-bundle-location"), + rpmFile.getParent())); + + PlatformPackage thePackage = createMetaPackage(params); + + //run rpmbuild + Executor.of( + TOOL_RPMBUILD, + "-bb", specFile(params).toAbsolutePath().toString(), + "--define", String.format("%%_sourcedir %s", + thePackage.sourceRoot()), + // save result to output dir + "--define", String.format("%%_rpmdir %s", rpmFile.getParent()), + // do not use other system directories to build as current user + "--define", String.format("%%_topdir %s", + TEMP_ROOT.fetchFrom(params).toPath().toAbsolutePath()), + "--define", String.format("%%_rpmfilename %s", rpmFile.getFileName()) + ).executeExpectSuccess(); + + Log.verbose(MessageFormat.format( + I18N.getString("message.output-bundle-location"), + rpmFile.getParent())); + + return rpmFile; + } + + @Override + public String getName() { + return I18N.getString("rpm.bundler.name"); + } + + @Override + public String getID() { + return "rpm"; + } + + @Override + public boolean supported(boolean runtimeInstaller) { + return Platform.isLinux() && (createRpmbuildToolValidator().validate() == null); + } + + @Override + public boolean isDefault() { + return !LinuxDebBundler.isDebian(); + } + + private String rpmArch; +} --- /dev/null 2019-11-18 20:36:22.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/PackageProperty.java 2019-11-18 20:36:19.282673700 -0500 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.text.MessageFormat; + +final class PackageProperty { + /** + * Constructor + * + * @param name property name + * @param expectedValue expected property value + * @param substString substitution string to be placed in resource file to + * be replaced with the expected property value by jpackage at package build + * time + * @param customResource name of custom resource from resource directory in + * which this package property can be set + */ + PackageProperty(String name, String expectedValue, String substString, + String customResource) { + this.name = name; + this.expectedValue = expectedValue; + this.substString = substString; + this.customResource = customResource; + } + + ConfigException verifyValue(String actualValue) { + if (expectedValue.equals(actualValue)) { + return null; + } + + final String advice; + if (substString != null) { + advice = MessageFormat.format(I18N.getString( + "error.unexpected-package-property.advice"), substString, + actualValue, name, customResource); + } else { + advice = MessageFormat.format(I18N.getString( + "error.unexpected-default-package-property.advice"), name, + customResource); + } + + return new ConfigException(MessageFormat.format(I18N.getString( + "error.unexpected-package-property"), name, + expectedValue, actualValue, customResource, substString), advice); + } + + final String name; + private final String expectedValue; + private final String substString; + private final String customResource; +} --- /dev/null 2019-11-18 20:36:34.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/LinuxResources.properties 2019-11-18 20:36:30.827528500 -0500 @@ -0,0 +1,69 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# +app.bundler.name=Linux Application Image +deb.bundler.name=DEB Bundle +rpm.bundler.name=RPM Bundle + +param.license-type.default=Unknown +param.menu-group.default=Unknown + +resource.deb-control-file=DEB control file +resource.deb-preinstall-script=DEB preinstall script +resource.deb-prerm-script=DEB prerm script +resource.deb-postinstall-script=DEB postinstall script +resource.deb-postrm-script=DEB postrm script +resource.copyright-file=Copyright file +resource.menu-shortcut-descriptor=Menu shortcut descriptor +resource.menu-icon=menu icon +resource.rpm-spec-file=RPM spec file + +error.tool-not-found.advice=Please install required packages +error.tool-old-version.advice=Please install required packages + +error.invalid-install-dir=Invalid installation directory "{0}" +error.unsupported-install-dir=Installing to system directory "{0}" is currently unsupported + +error.no-content-types-for-file-association=No MIME types were specified for File Association number {0} +error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. +error.invalid-value-for-package-name=Invalid value "{0}" for the bundle name. +error.invalid-value-for-package-name.advice=Set the "linux-bundle-name" option to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character. + +message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place. +message.test-for-tool=Test for [{0}]. Result: {1} +message.outputting-to-location=Generating DEB for installer to: {0}. +message.output-to-location=Package (.deb) saved to: {0}. +message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application. +message.outputting-bundle-location=Generating RPM for installer to: {0}. +message.output-bundle-location=Package (.rpm) saved to: {0}. +message.creating-association-with-null-extension=Creating association with null extension. + +message.ldd-not-available=ldd command not found. Package dependencies will not be generated. +message.deb-ldd-not-available.advice=Install "libc-bin" DEB package to get ldd. +message.rpm-ldd-not-available.advice=Install "glibc-common" RPM package to get ldd. + +error.unexpected-package-property=Expected value of "{0}" property is [{1}]. Actual value in output package is [{2}]. Looks like custom "{3}" file from resource directory contained hard coded value of "{0}" property +error.unexpected-package-property.advice=Use [{0}] pattern string instead of hard coded value [{1}] of {2} property in custom "{3}" file +error.unexpected-default-package-property.advice=Don't explicitly set value of {0} property in custom "{1}" file --- /dev/null 2019-11-18 20:36:45.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/LinuxResources_ja.properties 2019-11-18 20:36:42.083248300 -0500 @@ -0,0 +1,69 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# +app.bundler.name=Linux Application Image +deb.bundler.name=DEB Bundle +rpm.bundler.name=RPM Bundle + +param.license-type.default=Unknown +param.menu-group.default=Unknown + +resource.deb-control-file=DEB control file +resource.deb-preinstall-script=DEB preinstall script +resource.deb-prerm-script=DEB prerm script +resource.deb-postinstall-script=DEB postinstall script +resource.deb-postrm-script=DEB postrm script +resource.copyright-file=Copyright file +resource.menu-shortcut-descriptor=Menu shortcut descriptor +resource.menu-icon=menu icon +resource.rpm-spec-file=RPM spec file + +error.tool-not-found.advice=Please install required packages +error.tool-old-version.advice=Please install required packages + +error.invalid-install-dir=Invalid installation directory "{0}" +error.unsupported-install-dir=Installing to system directory "{0}" is currently unsupported + +error.no-content-types-for-file-association=No MIME types were specified for File Association number {0} +error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. +error.invalid-value-for-package-name=Invalid value "{0}" for the bundle name. +error.invalid-value-for-package-name.advice=Set the "linux-bundle-name" option to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character. + +message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place. +message.test-for-tool=Test for [{0}]. Result: {1} +message.outputting-to-location=Generating DEB for installer to: {0}. +message.output-to-location=Package (.deb) saved to: {0}. +message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application. +message.outputting-bundle-location=Generating RPM for installer to: {0}. +message.output-bundle-location=Package (.rpm) saved to: {0}. +message.creating-association-with-null-extension=Creating association with null extension. + +message.ldd-not-available=ldd command not found. Package dependencies will not be generated. +message.deb-ldd-not-available.advice=Install "libc-bin" DEB package to get ldd. +message.rpm-ldd-not-available.advice=Install "glibc-common" RPM package to get ldd. + +error.unexpected-package-property=Expected value of "{0}" property is [{1}]. Actual value in output package is [{2}]. Looks like custom "{3}" file from resource directory contained hard coded value of "{0}" property +error.unexpected-package-property.advice=Use [{0}] pattern string instead of hard coded value [{1}] of {2} property in custom "{3}" file +error.unexpected-default-package-property.advice=Don't explicitly set value of {0} property in custom "{1}" file --- /dev/null 2019-11-18 20:36:56.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/LinuxResources_zh_CN.properties 2019-11-18 20:36:53.649357100 -0500 @@ -0,0 +1,69 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# +app.bundler.name=Linux Application Image +deb.bundler.name=DEB Bundle +rpm.bundler.name=RPM Bundle + +param.license-type.default=Unknown +param.menu-group.default=Unknown + +resource.deb-control-file=DEB control file +resource.deb-preinstall-script=DEB preinstall script +resource.deb-prerm-script=DEB prerm script +resource.deb-postinstall-script=DEB postinstall script +resource.deb-postrm-script=DEB postrm script +resource.copyright-file=Copyright file +resource.menu-shortcut-descriptor=Menu shortcut descriptor +resource.menu-icon=menu icon +resource.rpm-spec-file=RPM spec file + +error.tool-not-found.advice=Please install required packages +error.tool-old-version.advice=Please install required packages + +error.invalid-install-dir=Invalid installation directory "{0}" +error.unsupported-install-dir=Installing to system directory "{0}" is currently unsupported + +error.no-content-types-for-file-association=No MIME types were specified for File Association number {0} +error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. +error.invalid-value-for-package-name=Invalid value "{0}" for the bundle name. +error.invalid-value-for-package-name.advice=Set the "linux-bundle-name" option to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character. + +message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place. +message.test-for-tool=Test for [{0}]. Result: {1} +message.outputting-to-location=Generating DEB for installer to: {0}. +message.output-to-location=Package (.deb) saved to: {0}. +message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application. +message.outputting-bundle-location=Generating RPM for installer to: {0}. +message.output-bundle-location=Package (.rpm) saved to: {0}. +message.creating-association-with-null-extension=Creating association with null extension. + +message.ldd-not-available=ldd command not found. Package dependencies will not be generated. +message.deb-ldd-not-available.advice=Install "libc-bin" DEB package to get ldd. +message.rpm-ldd-not-available.advice=Install "glibc-common" RPM package to get ldd. + +error.unexpected-package-property=Expected value of "{0}" property is [{1}]. Actual value in output package is [{2}]. Looks like custom "{3}" file from resource directory contained hard coded value of "{0}" property +error.unexpected-package-property.advice=Use [{0}] pattern string instead of hard coded value [{1}] of {2} property in custom "{3}" file +error.unexpected-default-package-property.advice=Don't explicitly set value of {0} property in custom "{1}" file Binary files /dev/null and new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/java32.png differ --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.control 2019-11-18 20:37:19.705136400 -0500 +++ /dev/null 2019-11-18 20:37:21.000000000 -0500 @@ -1,10 +0,0 @@ -Package: APPLICATION_PACKAGE -Version: APPLICATION_VERSION -Section: unknown -Maintainer: APPLICATION_MAINTAINER -Priority: optional -Architecture: APPLICATION_ARCH -Provides: APPLICATION_PACKAGE -Description: APPLICATION_DESCRIPTION -Installed-Size: APPLICATION_INSTALLED_SIZE -PACKAGE_DEPENDENCIES --- /dev/null 2019-11-18 20:37:21.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/template.control 2019-11-18 20:37:15.983529200 -0500 @@ -0,0 +1,10 @@ +Package: APPLICATION_PACKAGE +Version: APPLICATION_VERSION-APPLICATION_RELEASE +Section: APPLICATION_SECTION +Maintainer: APPLICATION_MAINTAINER +Priority: optional +Architecture: APPLICATION_ARCH +Provides: APPLICATION_PACKAGE +Description: APPLICATION_DESCRIPTION +Depends: PACKAGE_DEFAULT_DEPENDENCIES PACKAGE_CUSTOM_DEPENDENCIES +Installed-Size: APPLICATION_INSTALLED_SIZE --- /dev/null 2019-11-18 20:37:41.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/template.copyright 2019-11-18 20:37:38.158245200 -0500 @@ -0,0 +1,5 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ + +Files: * +Copyright: APPLICATION_COPYRIGHT +License: APPLICATION_LICENSE_TEXT --- /dev/null 2019-11-18 20:37:52.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/template.desktop 2019-11-18 20:37:49.025362800 -0500 @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=APPLICATION_NAME +Comment=APPLICATION_DESCRIPTION +Exec=APPLICATION_LAUNCHER +Icon=APPLICATION_ICON +Terminal=false +Type=Application +Categories=DEPLOY_BUNDLE_CATEGORY +DESKTOP_MIMES --- /dev/null 2019-11-18 20:38:04.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/template.postinst 2019-11-18 20:38:00.833344100 -0500 @@ -0,0 +1,34 @@ +#!/bin/sh +# postinst script for APPLICATION_PACKAGE +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) +DESKTOP_COMMANDS_INSTALL + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postrm 2019-11-18 20:38:16.127559900 -0500 +++ /dev/null 2019-11-18 20:38:17.000000000 -0500 @@ -1,44 +0,0 @@ -#!/bin/sh -# postrm script for APPLICATION_NAME -# -# see: dh_installdeb(1) - -set -e - -# summary of how this script can be called: -# * `remove' -# * `purge' -# * `upgrade' -# * `failed-upgrade' -# * `abort-install' -# * `abort-install' -# * `abort-upgrade' -# * `disappear' -# -# for details, see http://www.debian.org/doc/debian-policy/ or -# the debian-policy package - -case "$1" in - purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) - if [ "$1" = "purge" ] ; then - if [ "SERVICE_HINT" = "true" ]; then - echo Uninstalling daemon - rm -f /etc/init.d/APPLICATION_PACKAGE - - update-rc.d APPLICATION_PACKAGE remove - fi - fi - ;; - - *) - echo "postrm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 --- /dev/null 2019-11-18 20:38:17.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/template.postrm 2019-11-18 20:38:12.511018400 -0500 @@ -0,0 +1,31 @@ +#!/bin/sh +# postrm script for APPLICATION_PACKAGE +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst 2019-11-18 20:38:38.568779500 -0500 +++ /dev/null 2019-11-18 20:38:40.000000000 -0500 @@ -1,35 +0,0 @@ -#!/bin/sh -# preinst script for APPLICATION_NAME -# -# see: dh_installdeb(1) - -set -e - -# summary of how this script can be called: -# * `install' -# * `install' -# * `upgrade' -# * `abort-upgrade' -# for details, see http://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - install|upgrade) - ;; - - abort-upgrade) - ;; - - *) - echo "preinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 --- /dev/null 2019-11-18 20:38:40.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/template.preinst 2019-11-18 20:38:34.890444200 -0500 @@ -0,0 +1,30 @@ +#!/bin/sh +# preinst script for APPLICATION_PACKAGE +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `install' +# * `install' +# * `upgrade' +# * `abort-upgrade' +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + install|upgrade) + ;; + + abort-upgrade) + ;; + + *) + echo "preinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm 2019-11-18 20:39:00.971285500 -0500 +++ /dev/null 2019-11-18 20:39:02.000000000 -0500 @@ -1,45 +0,0 @@ -#!/bin/sh -# prerm script for APPLICATION_NAME -# -# see: dh_installdeb(1) - -set -e - -# summary of how this script can be called: -# * `remove' -# * `upgrade' -# * `failed-upgrade' -# * `remove' `in-favour' -# * `deconfigure' `in-favour' -# `removing' -# -# for details, see http://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - remove|upgrade|deconfigure) - if [ "RUNTIME_INSTALLER" != "true" ]; then - echo Removing shortcut -ADD_LAUNCHERS_REMOVE - xdg-desktop-menu uninstall --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop -FILE_ASSOCIATION_REMOVE - fi - ;; - - failed-upgrade) - ;; - - *) - echo "prerm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 - --- /dev/null 2019-11-18 20:39:02.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/template.prerm 2019-11-18 20:38:57.310977800 -0500 @@ -0,0 +1,37 @@ +#!/bin/sh +# prerm script for APPLICATION_PACKAGE +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `upgrade' +# * `failed-upgrade' +# * `remove' `in-favour' +# * `deconfigure' `in-favour' +# `removing' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +UTILITY_SCRIPTS + +case "$1" in + remove|upgrade|deconfigure) +DESKTOP_COMMANDS_UNINSTALL + ;; + + failed-upgrade) + ;; + + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 + --- /dev/null 2019-11-18 20:39:23.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/template.spec 2019-11-18 20:39:19.964717400 -0500 @@ -0,0 +1,58 @@ +Summary: APPLICATION_SUMMARY +Name: APPLICATION_PACKAGE +Version: APPLICATION_VERSION +Release: APPLICATION_RELEASE +License: APPLICATION_LICENSE_TYPE +Vendor: APPLICATION_VENDOR +Prefix: %{dirname:APPLICATION_DIRECTORY} +Provides: APPLICATION_PACKAGE +%if "xAPPLICATION_GROUP" != x +Group: APPLICATION_GROUP +%endif + +Autoprov: 0 +Autoreq: 0 +%if "xPACKAGE_DEFAULT_DEPENDENCIES" != x || "xPACKAGE_CUSTOM_DEPENDENCIES" != x +Requires: PACKAGE_DEFAULT_DEPENDENCIES PACKAGE_CUSTOM_DEPENDENCIES +%endif + +#comment line below to enable effective jar compression +#it could easily get your package size from 40 to 15Mb but +#build time will substantially increase and it may require unpack200/system java to install +%define __jar_repack %{nil} + +%description +APPLICATION_DESCRIPTION + +%prep + +%build + +%install +rm -rf %{buildroot} +install -d -m 755 %{buildroot}APPLICATION_DIRECTORY +cp -r %{_sourcedir}APPLICATION_DIRECTORY/* %{buildroot}APPLICATION_DIRECTORY +%if "xAPPLICATION_LICENSE_FILE" != x + %define license_install_file %{_defaultlicensedir}/%{name}-%{version}/%{basename:APPLICATION_LICENSE_FILE} + install -d -m 755 %{buildroot}%{dirname:%{license_install_file}} + install -m 644 APPLICATION_LICENSE_FILE %{buildroot}%{license_install_file} +%endif + +%files +%if "xAPPLICATION_LICENSE_FILE" != x + %license %{license_install_file} + %{dirname:%{license_install_file}} +%endif +# If installation directory for the application is /a/b/c, we want only root +# component of the path (/a) in the spec file to make sure all subdirectories +# are owned by the package. +%(echo APPLICATION_DIRECTORY | sed -e "s|\(^/[^/]\{1,\}\).*$|\1|") + +%post +DESKTOP_COMMANDS_INSTALL + +%preun +UTILITY_SCRIPTS +DESKTOP_COMMANDS_UNINSTALL + +%clean --- /dev/null 2019-11-18 20:39:34.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/jdk/incubator/jpackage/internal/resources/utils.sh 2019-11-18 20:39:31.374155200 -0500 @@ -0,0 +1,104 @@ +# +# Remove $1 desktop file from the list of default handlers for $2 mime type +# in $3 file dumping output to stdout. +# +_filter_out_default_mime_handler () +{ + local defaults_list="$3" + + local desktop_file="$1" + local mime_type="$2" + + awk -f- "$defaults_list" < "$tmpfile1" + + local v + local update= + for mime in "$@"; do + _filter_out_default_mime_handler "$desktop_file" "$mime" "$tmpfile1" > "$tmpfile2" + v="$tmpfile2" + tmpfile2="$tmpfile1" + tmpfile1="$v" + + if ! diff -q "$tmpfile1" "$tmpfile2" > /dev/null; then + update=yes + trace Remove $desktop_file default handler for $mime mime type from $defaults_list file + fi + done + + if [ -n "$update" ]; then + cat "$tmpfile1" > "$defaults_list" + trace "$defaults_list" file updated + fi + + rm -f "$tmpfile1" "$tmpfile2" +} + + +# +# Remove $1 desktop file from the list of default handlers for $@ mime types +# in all known system defaults lists. +# +uninstall_default_mime_handler () +{ + for f in /usr/share/applications/defaults.list /usr/local/share/applications/defaults.list; do + _uninstall_default_mime_handler "$f" "$@" + done +} + + +trace () +{ + echo "$@" +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java 2019-11-18 20:39:46.289792600 -0500 +++ /dev/null 2019-11-18 20:39:47.000000000 -0500 @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -@FunctionalInterface -interface ArgAction { - void execute(); -} --- /dev/null 2019-11-18 20:39:48.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/classes/module-info.java.extra 2019-11-18 20:39:42.519722000 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +provides jdk.incubator.jpackage.internal.Bundler with + jdk.incubator.jpackage.internal.LinuxAppBundler, + jdk.incubator.jpackage.internal.LinuxDebBundler, + jdk.incubator.jpackage.internal.LinuxRpmBundler; + --- old/src/jdk.jpackage/linux/native/jpackageapplauncher/launcher.cpp 2019-11-18 20:40:08.869472900 -0500 +++ /dev/null 2019-11-18 20:40:10.000000000 -0500 @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include -#include -#include -#include -#include -#include - - -typedef bool (*start_launcher)(int argc, char* argv[]); -typedef void (*stop_launcher)(); - -#define MAX_PATH 1024 - -std::string GetProgramPath() { - ssize_t len = 0; - std::string result; - char buffer[MAX_PATH] = {0}; - - if ((len = readlink("/proc/self/exe", buffer, MAX_PATH - 1)) != -1) { - buffer[len] = '\0'; - result = buffer; - } - - return result; -} - -int main(int argc, char *argv[]) { - int result = 1; - setlocale(LC_ALL, "en_US.utf8"); - void* library = NULL; - - { - std::string programPath = GetProgramPath(); - std::string libraryName = dirname((char*)programPath.c_str()); - libraryName += "/libapplauncher.so"; - library = dlopen(libraryName.c_str(), RTLD_LAZY); - - if (library == NULL) { - fprintf(stderr, "dlopen failed: %s\n", dlerror()); - fprintf(stderr, "%s not found.\n", libraryName.c_str()); - } - } - - if (library != NULL) { - start_launcher start = (start_launcher)dlsym(library, "start_launcher"); - stop_launcher stop = (stop_launcher)dlsym(library, "stop_launcher"); - - if (start != NULL && stop != NULL) { - if (start(argc, argv) == true) { - result = 0; - stop(); - } - } else { - fprintf(stderr, "cannot find start_launcher and stop_launcher in libapplauncher.so"); - } - - dlclose(library); - } - - - return result; -} --- /dev/null 2019-11-18 20:40:10.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/native/jpackageapplauncher/launcher.cpp 2019-11-18 20:40:05.319421600 -0500 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include +#include +#include + + +typedef bool (*start_launcher)(int argc, char* argv[]); +typedef void (*stop_launcher)(); + +#define MAX_PATH 1024 + +std::string GetProgramPath() { + ssize_t len = 0; + std::string result; + char buffer[MAX_PATH] = {0}; + + if ((len = readlink("/proc/self/exe", buffer, MAX_PATH - 1)) != -1) { + buffer[len] = '\0'; + result = buffer; + } + + return result; +} + +int main(int argc, char *argv[]) { + int result = 1; + setlocale(LC_ALL, "en_US.utf8"); + void* library = NULL; + + { + std::string programPath = GetProgramPath(); + std::string libraryName = dirname((char*)programPath.c_str()); + libraryName += "/../lib/libapplauncher.so"; + library = dlopen(libraryName.c_str(), RTLD_LAZY); + + if (library == NULL) { + fprintf(stderr, "dlopen failed: %s\n", dlerror()); + fprintf(stderr, "%s not found.\n", libraryName.c_str()); + } + } + + if (library != NULL) { + start_launcher start = (start_launcher)dlsym(library, "start_launcher"); + stop_launcher stop = (stop_launcher)dlsym(library, "stop_launcher"); + + if (start != NULL && stop != NULL) { + if (start(argc, argv) == true) { + result = 0; + stop(); + } + } else { + fprintf(stderr, "cannot find start_launcher and stop_launcher in libapplauncher.so"); + } + + dlclose(library); + } + + + return result; +} --- old/src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.cpp 2019-11-18 20:40:31.228062400 -0500 +++ /dev/null 2019-11-18 20:40:32.000000000 -0500 @@ -1,1083 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Platform.h" - -#include "JavaVirtualMachine.h" -#include "LinuxPlatform.h" -#include "PlatformString.h" -#include "IniFile.h" -#include "Helpers.h" -#include "FilePath.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LINUX_JPACKAGE_TMP_DIR "/.java/jpackage/tmp" - -TString GetEnv(const TString &name) { - TString result; - - char *value = ::getenv((TCHAR*) name.c_str()); - - if (value != NULL) { - result = value; - } - - return result; -} - -LinuxPlatform::LinuxPlatform(void) : Platform(), -PosixPlatform() { - FMainThread = pthread_self(); -} - -LinuxPlatform::~LinuxPlatform(void) { -} - -TString LinuxPlatform::GetPackageAppDirectory() { - return FilePath::IncludeTrailingSeparator( - GetPackageRootDirectory()) + _T("app"); -} - -TString LinuxPlatform::GetAppName() { - TString result = GetModuleFileName(); - result = FilePath::ExtractFileName(result); - return result; -} - -TString LinuxPlatform::GetPackageLauncherDirectory() { - return GetPackageRootDirectory(); -} - -TString LinuxPlatform::GetPackageRuntimeBinDirectory() { - return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime/bin"); -} - -void LinuxPlatform::ShowMessage(TString title, TString description) { - printf("%s %s\n", PlatformString(title).toPlatformString(), - PlatformString(description).toPlatformString()); - fflush(stdout); -} - -void LinuxPlatform::ShowMessage(TString description) { - TString appname = GetModuleFileName(); - appname = FilePath::ExtractFileName(appname); - ShowMessage(PlatformString(appname).toPlatformString(), - PlatformString(description).toPlatformString()); -} - -TCHAR* LinuxPlatform::ConvertStringToFileSystemString(TCHAR* Source, - bool &release) { - // Not Implemented. - return NULL; -} - -TCHAR* LinuxPlatform::ConvertFileSystemStringToString(TCHAR* Source, - bool &release) { - // Not Implemented. - return NULL; -} - -TString LinuxPlatform::GetModuleFileName() { - ssize_t len = 0; - TString result; - DynamicBuffer buffer(MAX_PATH); - if (buffer.GetData() == NULL) { - return result; - } - - if ((len = readlink("/proc/self/exe", buffer.GetData(), - MAX_PATH - 1)) != -1) { - buffer[len] = '\0'; - result = buffer.GetData(); - } - - return result; -} - -void LinuxPlatform::SetCurrentDirectory(TString Value) { - chdir(PlatformString(Value).toPlatformString()); -} - -TString LinuxPlatform::GetPackageRootDirectory() { - TString filename = GetModuleFileName(); - return FilePath::ExtractFilePath(filename); -} - -TString LinuxPlatform::GetAppDataDirectory() { - TString result; - TString home = GetEnv(_T("HOME")); - - if (home.empty() == false) { - result += FilePath::IncludeTrailingSeparator(home) + _T(".local"); - } - - return result; -} - -ISectionalPropertyContainer* LinuxPlatform::GetConfigFile(TString FileName) { - IniFile *result = new IniFile(); - if (result == NULL) { - return NULL; - } - - result->LoadFromFile(FileName); - - return result; -} - -TString LinuxPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) { - TString result = FilePath::IncludeTrailingSeparator(RuntimePath) + - "lib/libjli.so"; - - if (FilePath::FileExists(result) == false) { - result = FilePath::IncludeTrailingSeparator(RuntimePath) + - "lib/jli/libjli.so"; - if (FilePath::FileExists(result) == false) { - printf("Cannot find libjli.so!"); - } - } - - return result; -} - -bool LinuxPlatform::IsMainThread() { - bool result = (FMainThread == pthread_self()); - return result; -} - -TString LinuxPlatform::getTmpDirString() { - return TString(LINUX_JPACKAGE_TMP_DIR); -} - -TPlatformNumber LinuxPlatform::GetMemorySize() { - long pages = sysconf(_SC_PHYS_PAGES); - long page_size = sysconf(_SC_PAGE_SIZE); - TPlatformNumber result = pages * page_size; - result = result / 1048576; // Convert from bytes to megabytes. - return result; -} - -void PosixProcess::Cleanup() { - if (FOutputHandle != 0) { - close(FOutputHandle); - FOutputHandle = 0; - } - - if (FInputHandle != 0) { - close(FInputHandle); - FInputHandle = 0; - } -} - -#define PIPE_READ 0 -#define PIPE_WRITE 1 - -bool PosixProcess::Execute(const TString Application, - const std::vector Arguments, bool AWait) { - bool result = false; - - if (FRunning == false) { - FRunning = true; - - int handles[2]; - - if (pipe(handles) == -1) { - return false; - } - - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - - FChildPID = fork(); - - // PID returned by vfork is 0 for the child process and the - // PID of the child process for the parent. - if (FChildPID == -1) { - // Error - TString message = PlatformString::Format( - _T("Error: Unable to create process %s"), - Application.data()); - throw Exception(message); - } else if (FChildPID == 0) { - Cleanup(); - TString command = Application; - - for (std::vector::const_iterator iterator = - Arguments.begin(); iterator != Arguments.end(); - iterator++) { - command += TString(_T(" ")) + *iterator; - } -#ifdef DEBUG - printf("%s\n", command.data()); -#endif // DEBUG - - dup2(handles[PIPE_READ], STDIN_FILENO); - dup2(handles[PIPE_WRITE], STDOUT_FILENO); - - close(handles[PIPE_READ]); - close(handles[PIPE_WRITE]); - - execl("/bin/sh", "sh", "-c", command.data(), (char *) 0); - - _exit(127); - } else { - FOutputHandle = handles[PIPE_READ]; - FInputHandle = handles[PIPE_WRITE]; - - if (AWait == true) { - ReadOutput(); - Wait(); - Cleanup(); - FRunning = false; - result = true; - } else { - result = true; - } - } - } - - return result; -} - - -//---------------------------------------------------------------------------- - -#ifndef __UNIX_JPACKAGE_PLATFORM__ -#define __UNIX_JPACKAGE_PLATFORM__ - -/** Provide an abstraction for difference in the platform APIs, - e.g. string manipulation functions, etc. */ -#include -#include -#include -#include - -#define TCHAR char - -#define _T(x) x - -#define JPACKAGE_MULTIBYTE_SNPRINTF snprintf - -#define JPACKAGE_SNPRINTF(buffer, sizeOfBuffer, count, format, ...) \ - snprintf((buffer), (count), (format), __VA_ARGS__) - -#define JPACKAGE_PRINTF(format, ...) \ - printf((format), ##__VA_ARGS__) - -#define JPACKAGE_FPRINTF(dest, format, ...) \ - fprintf((dest), (format), __VA_ARGS__) - -#define JPACKAGE_SSCANF(buf, format, ...) \ - sscanf((buf), (format), __VA_ARGS__) - -#define JPACKAGE_STRDUP(strSource) \ - strdup((strSource)) - -//return "error code" (like on Windows) - -static int JPACKAGE_STRNCPY(char *strDest, size_t numberOfElements, - const char *strSource, size_t count) { - char *s = strncpy(strDest, strSource, count); - // Duplicate behavior of the Windows' _tcsncpy_s() by adding a NULL - // terminator at the end of the string. - if (count < numberOfElements) { - s[count] = '\0'; - } else { - s[numberOfElements - 1] = '\0'; - } - return (s == strDest) ? 0 : 1; -} - -#define JPACKAGE_STRICMP(x, y) \ - strcasecmp((x), (y)) - -#define JPACKAGE_STRNICMP(x, y, cnt) \ - strncasecmp((x), (y), (cnt)) - -#define JPACKAGE_STRNCMP(x, y, cnt) \ - strncmp((x), (y), (cnt)) - -#define JPACKAGE_STRLEN(x) \ - strlen((x)) - -#define JPACKAGE_STRSTR(x, y) \ - strstr((x), (y)) - -#define JPACKAGE_STRCHR(x, y) \ - strchr((x), (y)) - -#define JPACKAGE_STRRCHR(x, y) \ - strrchr((x), (y)) - -#define JPACKAGE_STRPBRK(x, y) \ - strpbrk((x), (y)) - -#define JPACKAGE_GETENV(x) \ - getenv((x)) - -#define JPACKAGE_PUTENV(x) \ - putenv((x)) - -#define JPACKAGE_STRCMP(x, y) \ - strcmp((x), (y)) - -#define JPACKAGE_STRCPY(x, y) \ - strcpy((x), (y)) - -#define JPACKAGE_STRCAT(x, y) \ - strcat((x), (y)) - -#define JPACKAGE_ATOI(x) \ - atoi((x)) - -#define JPACKAGE_FOPEN(x, y) \ - fopen((x), (y)) - -#define JPACKAGE_FGETS(x, y, z) \ - fgets((x), (y), (z)) - -#define JPACKAGE_REMOVE(x) \ - remove((x)) - -#define JPACKAGE_SPAWNV(mode, cmd, args) \ - spawnv((mode), (cmd), (args)) - -#define JPACKAGE_ISDIGIT(ch) isdigit(ch) - -// for non-unicode, just return the input string for -// the following 2 conversions -#define JPACKAGE_NEW_MULTIBYTE(message) message - -#define JPACKAGE_NEW_FROM_MULTIBYTE(message) message - -// for non-unicode, no-op for the relase operation -// since there is no memory allocated for the -// string conversions -#define JPACKAGE_RELEASE_MULTIBYTE(tmpMBCS) - -#define JPACKAGE_RELEASE_FROM_MULTIBYTE(tmpMBCS) - -// The size will be used for converting from 1 byte to 1 byte encoding. -// Ensure have space for zero-terminator. -#define JPACKAGE_GET_SIZE_FOR_ENCODING(message, theLength) (theLength + 1) - -#endif -#define xmlTagType 0 -#define xmlPCDataType 1 - -typedef struct _xmlNode XMLNode; -typedef struct _xmlAttribute XMLAttribute; - -struct _xmlNode { - int _type; // Type of node: tag, pcdata, cdate - TCHAR* _name; // Contents of node - XMLNode* _next; // Next node at same level - XMLNode* _sub; // First sub-node - XMLAttribute* _attributes; // List of attributes -}; - -struct _xmlAttribute { - TCHAR* _name; // Name of attribute - TCHAR* _value; // Value of attribute - XMLAttribute* _next; // Next attribute for this tag -}; - -// Public interface -static void RemoveNonAsciiUTF8FromBuffer(char *buf); -XMLNode* ParseXMLDocument(TCHAR* buf); -void FreeXMLDocument(XMLNode* root); - -// Utility methods for parsing document -XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name); -TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name); - -// Debugging -void PrintXMLDocument(XMLNode* node, int indt); - -#include -#include -#include -#include -#include - -#define JWS_assert(s, msg) \ - if (!(s)) { Abort(msg); } - - -// Internal declarations -static XMLNode* ParseXMLElement(void); -static XMLAttribute* ParseXMLAttribute(void); -static TCHAR* SkipWhiteSpace(TCHAR *p); -static TCHAR* SkipXMLName(TCHAR *p); -static TCHAR* SkipXMLComment(TCHAR *p); -static TCHAR* SkipXMLDocType(TCHAR *p); -static TCHAR* SkipXMLProlog(TCHAR *p); -static TCHAR* SkipPCData(TCHAR *p); -static int IsPCData(TCHAR *p); -static void ConvertBuiltInEntities(TCHAR* p); -static void SetToken(int type, TCHAR* start, TCHAR* end); -static void GetNextToken(void); -static XMLNode* CreateXMLNode(int type, TCHAR* name); -static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value); -static XMLNode* ParseXMLElement(void); -static XMLAttribute* ParseXMLAttribute(void); -static void FreeXMLAttribute(XMLAttribute* attr); -static void PrintXMLAttributes(XMLAttribute* attr); -static void indent(int indt); - -static jmp_buf jmpbuf; -static XMLNode* root_node = NULL; - -/** definition of error codes for setjmp/longjmp, - * that can be handled in ParseXMLDocument() - */ -#define JMP_NO_ERROR 0 -#define JMP_OUT_OF_RANGE 1 - -#define NEXT_CHAR(p) { \ - if (*p != 0) { \ - p++; \ - } else { \ - longjmp(jmpbuf, JMP_OUT_OF_RANGE); \ - } \ -} -#define NEXT_CHAR_OR_BREAK(p) { \ - if (*p != 0) { \ - p++; \ - } else { \ - break; \ - } \ -} -#define NEXT_CHAR_OR_RETURN(p) { \ - if (*p != 0) { \ - p++; \ - } else { \ - return; \ - } \ -} -#define SKIP_CHARS(p,n) { \ - int i; \ - for (i = 0; i < (n); i++) { \ - if (*p != 0) { \ - p++; \ - } else { \ - longjmp(jmpbuf, JMP_OUT_OF_RANGE); \ - } \ - } \ -} -#define SKIP_CHARS_OR_BREAK(p,n) { \ - int i; \ - for (i = 0; i < (n); i++) { \ - if (*p != 0) { \ - p++; \ - } else { \ - break; \ - } \ - } \ - if (i < (n)) { \ - break; \ - } \ -} - -/** Iterates through the null-terminated buffer (i.e., C string) and - * replaces all UTF-8 encoded character >255 with 255 - * - * UTF-8 encoding: - * - * Range A: 0x0000 - 0x007F - * 0 | bits 0 - 7 - * Range B : 0x0080 - 0x07FF : - * 110 | bits 6 - 10 - * 10 | bits 0 - 5 - * Range C : 0x0800 - 0xFFFF : - * 1110 | bits 12-15 - * 10 | bits 6-11 - * 10 | bits 0-5 - */ -static void RemoveNonAsciiUTF8FromBuffer(char *buf) { - char* p; - char* q; - char c; - p = q = buf; - // We are not using NEXT_CHAR() to check if *q is NULL, as q is output - // location and offset for q is smaller than for p. - while (*p != '\0') { - c = *p; - if ((c & 0x80) == 0) { - /* Range A */ - *q++ = *p; - NEXT_CHAR(p); - } else if ((c & 0xE0) == 0xC0) { - /* Range B */ - *q++ = (char) 0xFF; - NEXT_CHAR(p); - NEXT_CHAR_OR_BREAK(p); - } else { - /* Range C */ - *q++ = (char) 0xFF; - NEXT_CHAR(p); - SKIP_CHARS_OR_BREAK(p, 2); - } - } - /* Null terminate string */ - *q = '\0'; -} - -static TCHAR* SkipWhiteSpace(TCHAR *p) { - if (p != NULL) { - while (iswspace(*p)) - NEXT_CHAR_OR_BREAK(p); - } - return p; -} - -static TCHAR* SkipXMLName(TCHAR *p) { - TCHAR c = *p; - /* Check if start of token */ - if (('a' <= c && c <= 'z') || - ('A' <= c && c <= 'Z') || - c == '_' || c == ':') { - - while (('a' <= c && c <= 'z') || - ('A' <= c && c <= 'Z') || - ('0' <= c && c <= '9') || - c == '_' || c == ':' || c == '.' || c == '-') { - NEXT_CHAR(p); - c = *p; - if (c == '\0') break; - } - } - return p; -} - -static TCHAR* SkipXMLComment(TCHAR *p) { - if (p != NULL) { - if (JPACKAGE_STRNCMP(p, _T(""), 3) == 0) { - SKIP_CHARS(p, 3); - return p; - } - NEXT_CHAR(p); - } while (*p != '\0'); - } - } - return p; -} - -static TCHAR* SkipXMLDocType(TCHAR *p) { - if (p != NULL) { - if (JPACKAGE_STRNCMP(p, _T("') { - NEXT_CHAR(p); - return p; - } - NEXT_CHAR(p); - } - } - } - return p; -} - -static TCHAR* SkipXMLProlog(TCHAR *p) { - if (p != NULL) { - if (JPACKAGE_STRNCMP(p, _T(""), 2) == 0) { - SKIP_CHARS(p, 2); - return p; - } - NEXT_CHAR(p); - } while (*p != '\0'); - } - } - return p; -} - -/* Search for the built-in XML entities: - * & (&), < (<), > (>), ' ('), and "e(") - * and convert them to a real TCHARacter - */ -static void ConvertBuiltInEntities(TCHAR* p) { - TCHAR* q; - q = p; - // We are not using NEXT_CHAR() to check if *q is NULL, - // as q is output location and offset for q is smaller than for p. - while (*p) { - if (IsPCData(p)) { - /* dont convert &xxx values within PData */ - TCHAR *end; - end = SkipPCData(p); - while (p < end) { - *q++ = *p; - NEXT_CHAR(p); - } - } else { - if (JPACKAGE_STRNCMP(p, _T("&"), 5) == 0) { - *q++ = '&'; - SKIP_CHARS(p, 5); - } else if (JPACKAGE_STRNCMP(p, _T("<"), 4) == 0) { - *q = '<'; - SKIP_CHARS(p, 4); - } else if (JPACKAGE_STRNCMP(p, _T(">"), 4) == 0) { - *q = '>'; - SKIP_CHARS(p, 4); - } else if (JPACKAGE_STRNCMP(p, _T("'"), 6) == 0) { - *q = '\''; - SKIP_CHARS(p, 6); - } else if (JPACKAGE_STRNCMP(p, _T(""e;"), 7) == 0) { - *q = '\"'; - SKIP_CHARS(p, 7); - } else { - *q++ = *p; - NEXT_CHAR(p); - } - } - } - *q = '\0'; -} - -/* ------------------------------------------------------------- */ -/* XML tokenizer */ - -#define TOKEN_UNKNOWN 0 -#define TOKEN_BEGIN_TAG 1 /* */ -#define TOKEN_EMPTY_CLOSE_BRACKET 4 /* /> */ -#define TOKEN_PCDATA 5 /* pcdata */ -#define TOKEN_CDATA 6 /* cdata */ -#define TOKEN_EOF 7 - -static TCHAR* CurPos = NULL; -static TCHAR* CurTokenName = NULL; -static int CurTokenType; -static int MaxTokenSize = -1; - -/* Copy token from buffer to Token variable */ -static void SetToken(int type, TCHAR* start, TCHAR* end) { - int len = end - start; - if (len > MaxTokenSize) { - if (CurTokenName != NULL) free(CurTokenName); - CurTokenName = (TCHAR *) malloc((len + 1) * sizeof (TCHAR)); - if (CurTokenName == NULL) { - return; - } - MaxTokenSize = len; - } - - CurTokenType = type; - JPACKAGE_STRNCPY(CurTokenName, len + 1, start, len); - CurTokenName[len] = '\0'; -} - -/* Skip XML comments, doctypes, and prolog tags */ -static TCHAR* SkipFilling(void) { - TCHAR *q = CurPos; - - /* Skip white space and comment sections */ - do { - q = CurPos; - CurPos = SkipWhiteSpace(CurPos); - CurPos = SkipXMLComment(CurPos); /* Must be called befor DocTypes */ - CurPos = SkipXMLDocType(CurPos); /* directives */ - CurPos = SkipXMLProlog(CurPos); /* directives */ - } while (CurPos != q); - - return CurPos; -} - -/* Parses next token and initializes the global token variables above - The tokennizer automatically skips comments () and - directives. - */ -static void GetNextToken(void) { - TCHAR *p, *q; - - /* Skip white space and comment sections */ - p = SkipFilling(); - - if (p == NULL || *p == '\0') { - CurTokenType = TOKEN_EOF; - return; - } else if (p[0] == '<' && p[1] == '/') { - /* TOKEN_END_TAG */ - q = SkipXMLName(p + 2); - SetToken(TOKEN_END_TAG, p + 2, q); - p = q; - } else if (*p == '<') { - /* TOKEN_BEGIN_TAG */ - q = SkipXMLName(p + 1); - SetToken(TOKEN_BEGIN_TAG, p + 1, q); - p = q; - } else if (p[0] == '>') { - CurTokenType = TOKEN_CLOSE_BRACKET; - NEXT_CHAR(p); - } else if (p[0] == '/' && p[1] == '>') { - CurTokenType = TOKEN_EMPTY_CLOSE_BRACKET; - SKIP_CHARS(p, 2); - } else { - /* Search for end of data */ - q = p + 1; - while (*q && *q != '<') { - if (IsPCData(q)) { - q = SkipPCData(q); - } else { - NEXT_CHAR(q); - } - } - SetToken(TOKEN_PCDATA, p, q); - /* Convert all entities inside token */ - ConvertBuiltInEntities(CurTokenName); - p = q; - } - /* Advance pointer to beginning of next token */ - CurPos = p; -} - -static XMLNode* CreateXMLNode(int type, TCHAR* name) { - XMLNode* node; - node = (XMLNode*) malloc(sizeof (XMLNode)); - if (node == NULL) { - return NULL; - } - node->_type = type; - node->_name = name; - node->_next = NULL; - node->_sub = NULL; - node->_attributes = NULL; - return node; -} - -static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value) { - XMLAttribute* attr; - attr = (XMLAttribute*) malloc(sizeof (XMLAttribute)); - if (attr == NULL) { - return NULL; - } - attr->_name = name; - attr->_value = value; - attr->_next = NULL; - return attr; -} - -XMLNode* ParseXMLDocument(TCHAR* buf) { - XMLNode* root; - int err_code = setjmp(jmpbuf); - switch (err_code) { - case JMP_NO_ERROR: -#ifndef _UNICODE - /* Remove UTF-8 encoding from buffer */ - RemoveNonAsciiUTF8FromBuffer(buf); -#endif - - /* Get first Token */ - CurPos = buf; - GetNextToken(); - - /* Parse document*/ - root = ParseXMLElement(); - break; - case JMP_OUT_OF_RANGE: - /* cleanup: */ - if (root_node != NULL) { - FreeXMLDocument(root_node); - root_node = NULL; - } - if (CurTokenName != NULL) free(CurTokenName); - fprintf(stderr, "Error during parsing jnlp file...\n"); - exit(-1); - break; - default: - root = NULL; - break; - } - - return root; -} - -static XMLNode* ParseXMLElement(void) { - XMLNode* node = NULL; - XMLNode* subnode = NULL; - XMLNode* nextnode = NULL; - XMLAttribute* attr = NULL; - - if (CurTokenType == TOKEN_BEGIN_TAG) { - - /* Create node for new element tag */ - node = CreateXMLNode(xmlTagType, JPACKAGE_STRDUP(CurTokenName)); - /* We need to save root node pointer to be able to cleanup - if an error happens during parsing */ - if (!root_node) { - root_node = node; - } - /* Parse attributes. This section eats a all input until - EOF, a > or a /> */ - attr = ParseXMLAttribute(); - while (attr != NULL) { - attr->_next = node->_attributes; - node->_attributes = attr; - attr = ParseXMLAttribute(); - } - - /* This will eihter be a TOKEN_EOF, TOKEN_CLOSE_BRACKET, or a - * TOKEN_EMPTY_CLOSE_BRACKET */ - GetNextToken(); - - /* Skip until '>', '/>' or EOF. This should really be an error, */ - /* but we are loose */ - // if(CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET || - // CurTokenType == TOKEN_CLOSE_BRACKET || - // CurTokenType == TOKEN_EOF) { - // println("XML Parsing error: wrong kind of token found"); - // return NULL; - // } - - if (CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET) { - GetNextToken(); - /* We are done with the sublevel - fall through to continue */ - /* parsing tags at the same level */ - } else if (CurTokenType == TOKEN_CLOSE_BRACKET) { - GetNextToken(); - - /* Parse until end tag if found */ - node->_sub = ParseXMLElement(); - - if (CurTokenType == TOKEN_END_TAG) { - /* Find closing bracket '>' for end tag */ - do { - GetNextToken(); - } while (CurTokenType != TOKEN_EOF && - CurTokenType != TOKEN_CLOSE_BRACKET); - GetNextToken(); - } - } - - /* Continue parsing rest on same level */ - if (CurTokenType != TOKEN_EOF) { - /* Parse rest of stream at same level */ - node->_next = ParseXMLElement(); - } - return node; - - } else if (CurTokenType == TOKEN_PCDATA) { - /* Create node for pcdata */ - node = CreateXMLNode(xmlPCDataType, JPACKAGE_STRDUP(CurTokenName)); - /* We need to save root node pointer to be able to cleanup - if an error happens during parsing */ - if (!root_node) { - root_node = node; - } - GetNextToken(); - return node; - } - - /* Something went wrong. */ - return NULL; -} - -/* Parses an XML attribute. */ -static XMLAttribute* ParseXMLAttribute(void) { - TCHAR* q = NULL; - TCHAR* name = NULL; - TCHAR* PrevPos = NULL; - - do { - /* We need to check this condition to avoid endless loop - in case if an error happend during parsing. */ - if (PrevPos == CurPos) { - if (name != NULL) { - free(name); - name = NULL; - } - - return NULL; - } - - PrevPos = CurPos; - - /* Skip whitespace etc. */ - SkipFilling(); - - /* Check if we are done witht this attribute section */ - if (CurPos[0] == '\0' || - CurPos[0] == '>' || - (CurPos[0] == '/' && CurPos[1] == '>')) { - - if (name != NULL) { - free(name); - name = NULL; - } - - return NULL; - } - - /* Find end of name */ - q = CurPos; - while (*q && !iswspace(*q) && *q != '=') NEXT_CHAR(q); - - SetToken(TOKEN_UNKNOWN, CurPos, q); - if (name) { - free(name); - name = NULL; - } - name = JPACKAGE_STRDUP(CurTokenName); - - /* Skip any whitespace */ - CurPos = q; - CurPos = SkipFilling(); - - /* Next TCHARacter must be '=' for a valid attribute. - If it is not, this is really an error. - We ignore this, and just try to parse an attribute - out of the rest of the string. - */ - } while (*CurPos != '='); - - NEXT_CHAR(CurPos); - CurPos = SkipWhiteSpace(CurPos); - /* Parse CDATA part of attribute */ - if ((*CurPos == '\"') || (*CurPos == '\'')) { - TCHAR quoteChar = *CurPos; - q = ++CurPos; - while (*q != '\0' && *q != quoteChar) NEXT_CHAR(q); - SetToken(TOKEN_CDATA, CurPos, q); - CurPos = q + 1; - } else { - q = CurPos; - while (*q != '\0' && !iswspace(*q)) NEXT_CHAR(q); - SetToken(TOKEN_CDATA, CurPos, q); - CurPos = q; - } - - //Note: no need to free name and CurTokenName duplicate; they're assigned - // to an XMLAttribute structure in CreateXMLAttribute - - return CreateXMLAttribute(name, JPACKAGE_STRDUP(CurTokenName)); -} - -void FreeXMLDocument(XMLNode* root) { - if (root == NULL) return; - FreeXMLDocument(root->_sub); - FreeXMLDocument(root->_next); - FreeXMLAttribute(root->_attributes); - free(root->_name); - free(root); -} - -static void FreeXMLAttribute(XMLAttribute* attr) { - if (attr == NULL) return; - free(attr->_name); - free(attr->_value); - FreeXMLAttribute(attr->_next); - free(attr); -} - -/* Find element at current level with a given name */ -XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name) { - if (root == NULL) return NULL; - - if (root->_type == xmlTagType && JPACKAGE_STRCMP(root->_name, name) == 0) { - return root; - } - - return FindXMLChild(root->_next, name); -} - -/* Search for an attribute with the given name and returns the contents. Returns NULL if - * attribute is not found - */ -TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name) { - if (attr == NULL) return NULL; - if (JPACKAGE_STRCMP(attr->_name, name) == 0) return attr->_value; - return FindXMLAttribute(attr->_next, name); -} - -void PrintXMLDocument(XMLNode* node, int indt) { - if (node == NULL) return; - - if (node->_type == xmlTagType) { - JPACKAGE_PRINTF(_T("\n")); - indent(indt); - JPACKAGE_PRINTF(_T("<%s"), node->_name); - PrintXMLAttributes(node->_attributes); - if (node->_sub == NULL) { - JPACKAGE_PRINTF(_T("/>\n")); - } else { - JPACKAGE_PRINTF(_T(">")); - PrintXMLDocument(node->_sub, indt + 1); - indent(indt); - JPACKAGE_PRINTF(_T(""), node->_name); - } - } else { - JPACKAGE_PRINTF(_T("%s"), node->_name); - } - PrintXMLDocument(node->_next, indt); -} - -static void PrintXMLAttributes(XMLAttribute* attr) { - if (attr == NULL) return; - - JPACKAGE_PRINTF(_T(" %s=\"%s\""), attr->_name, attr->_value); - PrintXMLAttributes(attr->_next); -} - -static void indent(int indt) { - int i; - for (i = 0; i < indt; i++) { - JPACKAGE_PRINTF(_T(" ")); - } -} - -const TCHAR *CDStart = _T(""); - -static TCHAR* SkipPCData(TCHAR *p) { - TCHAR *end = JPACKAGE_STRSTR(p, CDEnd); - if (end != NULL) { - return end + sizeof (CDEnd); - } - return (++p); -} - -static int IsPCData(TCHAR *p) { - const int size = sizeof (CDStart); - return (JPACKAGE_STRNCMP(CDStart, p, size) == 0); -} --- /dev/null 2019-11-18 20:40:32.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/native/libapplauncher/LinuxPlatform.cpp 2019-11-18 20:40:27.442886500 -0500 @@ -0,0 +1,1080 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Platform.h" + +#include "JavaVirtualMachine.h" +#include "LinuxPlatform.h" +#include "PlatformString.h" +#include "IniFile.h" +#include "Helpers.h" +#include "FilePath.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LINUX_JPACKAGE_TMP_DIR "/.java/jpackage/tmp" + +TString GetEnv(const TString &name) { + TString result; + + char *value = ::getenv((TCHAR*) name.c_str()); + + if (value != NULL) { + result = value; + } + + return result; +} + +LinuxPlatform::LinuxPlatform(void) : Platform(), +PosixPlatform() { + FMainThread = pthread_self(); +} + +LinuxPlatform::~LinuxPlatform(void) { +} + +TString LinuxPlatform::GetPackageAppDirectory() { + return FilePath::IncludeTrailingSeparator( + GetPackageRootDirectory()) + _T("lib/app"); +} + +TString LinuxPlatform::GetAppName() { + TString result = GetModuleFileName(); + result = FilePath::ExtractFileName(result); + return result; +} + +TString LinuxPlatform::GetPackageLauncherDirectory() { + return FilePath::IncludeTrailingSeparator( + GetPackageRootDirectory()) + _T("bin"); +} + +TString LinuxPlatform::GetPackageRuntimeBinDirectory() { + return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + + _T("runtime/bin"); +} + +void LinuxPlatform::ShowMessage(TString title, TString description) { + printf("%s %s\n", PlatformString(title).toPlatformString(), + PlatformString(description).toPlatformString()); + fflush(stdout); +} + +void LinuxPlatform::ShowMessage(TString description) { + TString appname = GetModuleFileName(); + appname = FilePath::ExtractFileName(appname); + ShowMessage(PlatformString(appname).toPlatformString(), + PlatformString(description).toPlatformString()); +} + +TCHAR* LinuxPlatform::ConvertStringToFileSystemString(TCHAR* Source, + bool &release) { + // Not Implemented. + return NULL; +} + +TCHAR* LinuxPlatform::ConvertFileSystemStringToString(TCHAR* Source, + bool &release) { + // Not Implemented. + return NULL; +} + +TString LinuxPlatform::GetModuleFileName() { + ssize_t len = 0; + TString result; + DynamicBuffer buffer(MAX_PATH); + if (buffer.GetData() == NULL) { + return result; + } + + if ((len = readlink("/proc/self/exe", buffer.GetData(), + MAX_PATH - 1)) != -1) { + buffer[len] = '\0'; + result = buffer.GetData(); + } + + return result; +} + +TString LinuxPlatform::GetPackageRootDirectory() { + TString result; + TString filename = GetModuleFileName(); + TString binPath = FilePath::ExtractFilePath(filename); + + size_t slash = binPath.find_last_of(TRAILING_PATHSEPARATOR); + if (slash != TString::npos) { + result = binPath.substr(0, slash); + } + + return result; +} + +TString LinuxPlatform::GetAppDataDirectory() { + TString result; + TString home = GetEnv(_T("HOME")); + + if (home.empty() == false) { + result += FilePath::IncludeTrailingSeparator(home) + _T(".local"); + } + + return result; +} + +ISectionalPropertyContainer* LinuxPlatform::GetConfigFile(TString FileName) { + IniFile *result = new IniFile(); + if (result == NULL) { + return NULL; + } + + result->LoadFromFile(FileName); + + return result; +} + +TString LinuxPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) { + TString result = FilePath::IncludeTrailingSeparator(RuntimePath) + + "lib/libjli.so"; + + if (FilePath::FileExists(result) == false) { + result = FilePath::IncludeTrailingSeparator(RuntimePath) + + "lib/jli/libjli.so"; + if (FilePath::FileExists(result) == false) { + printf("Cannot find libjli.so!"); + } + } + + return result; +} + +bool LinuxPlatform::IsMainThread() { + bool result = (FMainThread == pthread_self()); + return result; +} + +TString LinuxPlatform::getTmpDirString() { + return TString(LINUX_JPACKAGE_TMP_DIR); +} + +TPlatformNumber LinuxPlatform::GetMemorySize() { + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + TPlatformNumber result = pages * page_size; + result = result / 1048576; // Convert from bytes to megabytes. + return result; +} + +void PosixProcess::Cleanup() { + if (FOutputHandle != 0) { + close(FOutputHandle); + FOutputHandle = 0; + } + + if (FInputHandle != 0) { + close(FInputHandle); + FInputHandle = 0; + } +} + +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +bool PosixProcess::Execute(const TString Application, + const std::vector Arguments, bool AWait) { + bool result = false; + + if (FRunning == false) { + FRunning = true; + + int handles[2]; + + if (pipe(handles) == -1) { + return false; + } + + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + FChildPID = fork(); + + // PID returned by vfork is 0 for the child process and the + // PID of the child process for the parent. + if (FChildPID == -1) { + // Error + TString message = PlatformString::Format( + _T("Error: Unable to create process %s"), + Application.data()); + throw Exception(message); + } else if (FChildPID == 0) { + Cleanup(); + TString command = Application; + + for (std::vector::const_iterator iterator = + Arguments.begin(); iterator != Arguments.end(); + iterator++) { + command += TString(_T(" ")) + *iterator; + } +#ifdef DEBUG + printf("%s\n", command.data()); +#endif // DEBUG + + dup2(handles[PIPE_READ], STDIN_FILENO); + dup2(handles[PIPE_WRITE], STDOUT_FILENO); + + close(handles[PIPE_READ]); + close(handles[PIPE_WRITE]); + + execl("/bin/sh", "sh", "-c", command.data(), (char *) 0); + + _exit(127); + } else { + FOutputHandle = handles[PIPE_READ]; + FInputHandle = handles[PIPE_WRITE]; + + if (AWait == true) { + ReadOutput(); + Wait(); + Cleanup(); + FRunning = false; + result = true; + } else { + result = true; + } + } + } + + return result; +} + + +//---------------------------------------------------------------------------- + +#ifndef __UNIX_JPACKAGE_PLATFORM__ +#define __UNIX_JPACKAGE_PLATFORM__ + +/** Provide an abstraction for difference in the platform APIs, + e.g. string manipulation functions, etc. */ +#include +#include +#include +#include + +#define TCHAR char + +#define _T(x) x + +#define JPACKAGE_MULTIBYTE_SNPRINTF snprintf + +#define JPACKAGE_SNPRINTF(buffer, sizeOfBuffer, count, format, ...) \ + snprintf((buffer), (count), (format), __VA_ARGS__) + +#define JPACKAGE_PRINTF(format, ...) \ + printf((format), ##__VA_ARGS__) + +#define JPACKAGE_FPRINTF(dest, format, ...) \ + fprintf((dest), (format), __VA_ARGS__) + +#define JPACKAGE_SSCANF(buf, format, ...) \ + sscanf((buf), (format), __VA_ARGS__) + +#define JPACKAGE_STRDUP(strSource) \ + strdup((strSource)) + +//return "error code" (like on Windows) + +static int JPACKAGE_STRNCPY(char *strDest, size_t numberOfElements, + const char *strSource, size_t count) { + char *s = strncpy(strDest, strSource, count); + // Duplicate behavior of the Windows' _tcsncpy_s() by adding a NULL + // terminator at the end of the string. + if (count < numberOfElements) { + s[count] = '\0'; + } else { + s[numberOfElements - 1] = '\0'; + } + return (s == strDest) ? 0 : 1; +} + +#define JPACKAGE_STRICMP(x, y) \ + strcasecmp((x), (y)) + +#define JPACKAGE_STRNICMP(x, y, cnt) \ + strncasecmp((x), (y), (cnt)) + +#define JPACKAGE_STRNCMP(x, y, cnt) \ + strncmp((x), (y), (cnt)) + +#define JPACKAGE_STRLEN(x) \ + strlen((x)) + +#define JPACKAGE_STRSTR(x, y) \ + strstr((x), (y)) + +#define JPACKAGE_STRCHR(x, y) \ + strchr((x), (y)) + +#define JPACKAGE_STRRCHR(x, y) \ + strrchr((x), (y)) + +#define JPACKAGE_STRPBRK(x, y) \ + strpbrk((x), (y)) + +#define JPACKAGE_GETENV(x) \ + getenv((x)) + +#define JPACKAGE_PUTENV(x) \ + putenv((x)) + +#define JPACKAGE_STRCMP(x, y) \ + strcmp((x), (y)) + +#define JPACKAGE_STRCPY(x, y) \ + strcpy((x), (y)) + +#define JPACKAGE_STRCAT(x, y) \ + strcat((x), (y)) + +#define JPACKAGE_ATOI(x) \ + atoi((x)) + +#define JPACKAGE_FOPEN(x, y) \ + fopen((x), (y)) + +#define JPACKAGE_FGETS(x, y, z) \ + fgets((x), (y), (z)) + +#define JPACKAGE_REMOVE(x) \ + remove((x)) + +#define JPACKAGE_SPAWNV(mode, cmd, args) \ + spawnv((mode), (cmd), (args)) + +#define JPACKAGE_ISDIGIT(ch) isdigit(ch) + +// for non-unicode, just return the input string for +// the following 2 conversions +#define JPACKAGE_NEW_MULTIBYTE(message) message + +#define JPACKAGE_NEW_FROM_MULTIBYTE(message) message + +// for non-unicode, no-op for the relase operation +// since there is no memory allocated for the +// string conversions +#define JPACKAGE_RELEASE_MULTIBYTE(tmpMBCS) + +#define JPACKAGE_RELEASE_FROM_MULTIBYTE(tmpMBCS) + +// The size will be used for converting from 1 byte to 1 byte encoding. +// Ensure have space for zero-terminator. +#define JPACKAGE_GET_SIZE_FOR_ENCODING(message, theLength) (theLength + 1) + +#endif +#define xmlTagType 0 +#define xmlPCDataType 1 + +typedef struct _xmlNode XMLNode; +typedef struct _xmlAttribute XMLAttribute; + +struct _xmlNode { + int _type; // Type of node: tag, pcdata, cdate + TCHAR* _name; // Contents of node + XMLNode* _next; // Next node at same level + XMLNode* _sub; // First sub-node + XMLAttribute* _attributes; // List of attributes +}; + +struct _xmlAttribute { + TCHAR* _name; // Name of attribute + TCHAR* _value; // Value of attribute + XMLAttribute* _next; // Next attribute for this tag +}; + +// Public interface +static void RemoveNonAsciiUTF8FromBuffer(char *buf); +XMLNode* ParseXMLDocument(TCHAR* buf); +void FreeXMLDocument(XMLNode* root); + +// Utility methods for parsing document +XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name); +TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name); + +// Debugging +void PrintXMLDocument(XMLNode* node, int indt); + +#include +#include +#include +#include +#include + +#define JWS_assert(s, msg) \ + if (!(s)) { Abort(msg); } + + +// Internal declarations +static XMLNode* ParseXMLElement(void); +static XMLAttribute* ParseXMLAttribute(void); +static TCHAR* SkipWhiteSpace(TCHAR *p); +static TCHAR* SkipXMLName(TCHAR *p); +static TCHAR* SkipXMLComment(TCHAR *p); +static TCHAR* SkipXMLDocType(TCHAR *p); +static TCHAR* SkipXMLProlog(TCHAR *p); +static TCHAR* SkipPCData(TCHAR *p); +static int IsPCData(TCHAR *p); +static void ConvertBuiltInEntities(TCHAR* p); +static void SetToken(int type, TCHAR* start, TCHAR* end); +static void GetNextToken(void); +static XMLNode* CreateXMLNode(int type, TCHAR* name); +static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value); +static XMLNode* ParseXMLElement(void); +static XMLAttribute* ParseXMLAttribute(void); +static void FreeXMLAttribute(XMLAttribute* attr); +static void PrintXMLAttributes(XMLAttribute* attr); +static void indent(int indt); + +static jmp_buf jmpbuf; +static XMLNode* root_node = NULL; + +/** definition of error codes for setjmp/longjmp, + * that can be handled in ParseXMLDocument() + */ +#define JMP_NO_ERROR 0 +#define JMP_OUT_OF_RANGE 1 + +#define NEXT_CHAR(p) { \ + if (*p != 0) { \ + p++; \ + } else { \ + longjmp(jmpbuf, JMP_OUT_OF_RANGE); \ + } \ +} +#define NEXT_CHAR_OR_BREAK(p) { \ + if (*p != 0) { \ + p++; \ + } else { \ + break; \ + } \ +} +#define NEXT_CHAR_OR_RETURN(p) { \ + if (*p != 0) { \ + p++; \ + } else { \ + return; \ + } \ +} +#define SKIP_CHARS(p,n) { \ + int i; \ + for (i = 0; i < (n); i++) { \ + if (*p != 0) { \ + p++; \ + } else { \ + longjmp(jmpbuf, JMP_OUT_OF_RANGE); \ + } \ + } \ +} +#define SKIP_CHARS_OR_BREAK(p,n) { \ + int i; \ + for (i = 0; i < (n); i++) { \ + if (*p != 0) { \ + p++; \ + } else { \ + break; \ + } \ + } \ + if (i < (n)) { \ + break; \ + } \ +} + +/** Iterates through the null-terminated buffer (i.e., C string) and + * replaces all UTF-8 encoded character >255 with 255 + * + * UTF-8 encoding: + * + * Range A: 0x0000 - 0x007F + * 0 | bits 0 - 7 + * Range B : 0x0080 - 0x07FF : + * 110 | bits 6 - 10 + * 10 | bits 0 - 5 + * Range C : 0x0800 - 0xFFFF : + * 1110 | bits 12-15 + * 10 | bits 6-11 + * 10 | bits 0-5 + */ +static void RemoveNonAsciiUTF8FromBuffer(char *buf) { + char* p; + char* q; + char c; + p = q = buf; + // We are not using NEXT_CHAR() to check if *q is NULL, as q is output + // location and offset for q is smaller than for p. + while (*p != '\0') { + c = *p; + if ((c & 0x80) == 0) { + /* Range A */ + *q++ = *p; + NEXT_CHAR(p); + } else if ((c & 0xE0) == 0xC0) { + /* Range B */ + *q++ = (char) 0xFF; + NEXT_CHAR(p); + NEXT_CHAR_OR_BREAK(p); + } else { + /* Range C */ + *q++ = (char) 0xFF; + NEXT_CHAR(p); + SKIP_CHARS_OR_BREAK(p, 2); + } + } + /* Null terminate string */ + *q = '\0'; +} + +static TCHAR* SkipWhiteSpace(TCHAR *p) { + if (p != NULL) { + while (iswspace(*p)) + NEXT_CHAR_OR_BREAK(p); + } + return p; +} + +static TCHAR* SkipXMLName(TCHAR *p) { + TCHAR c = *p; + /* Check if start of token */ + if (('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + c == '_' || c == ':') { + + while (('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9') || + c == '_' || c == ':' || c == '.' || c == '-') { + NEXT_CHAR(p); + c = *p; + if (c == '\0') break; + } + } + return p; +} + +static TCHAR* SkipXMLComment(TCHAR *p) { + if (p != NULL) { + if (JPACKAGE_STRNCMP(p, _T(""), 3) == 0) { + SKIP_CHARS(p, 3); + return p; + } + NEXT_CHAR(p); + } while (*p != '\0'); + } + } + return p; +} + +static TCHAR* SkipXMLDocType(TCHAR *p) { + if (p != NULL) { + if (JPACKAGE_STRNCMP(p, _T("') { + NEXT_CHAR(p); + return p; + } + NEXT_CHAR(p); + } + } + } + return p; +} + +static TCHAR* SkipXMLProlog(TCHAR *p) { + if (p != NULL) { + if (JPACKAGE_STRNCMP(p, _T(""), 2) == 0) { + SKIP_CHARS(p, 2); + return p; + } + NEXT_CHAR(p); + } while (*p != '\0'); + } + } + return p; +} + +/* Search for the built-in XML entities: + * & (&), < (<), > (>), ' ('), and "e(") + * and convert them to a real TCHARacter + */ +static void ConvertBuiltInEntities(TCHAR* p) { + TCHAR* q; + q = p; + // We are not using NEXT_CHAR() to check if *q is NULL, + // as q is output location and offset for q is smaller than for p. + while (*p) { + if (IsPCData(p)) { + /* dont convert &xxx values within PData */ + TCHAR *end; + end = SkipPCData(p); + while (p < end) { + *q++ = *p; + NEXT_CHAR(p); + } + } else { + if (JPACKAGE_STRNCMP(p, _T("&"), 5) == 0) { + *q++ = '&'; + SKIP_CHARS(p, 5); + } else if (JPACKAGE_STRNCMP(p, _T("<"), 4) == 0) { + *q = '<'; + SKIP_CHARS(p, 4); + } else if (JPACKAGE_STRNCMP(p, _T(">"), 4) == 0) { + *q = '>'; + SKIP_CHARS(p, 4); + } else if (JPACKAGE_STRNCMP(p, _T("'"), 6) == 0) { + *q = '\''; + SKIP_CHARS(p, 6); + } else if (JPACKAGE_STRNCMP(p, _T(""e;"), 7) == 0) { + *q = '\"'; + SKIP_CHARS(p, 7); + } else { + *q++ = *p; + NEXT_CHAR(p); + } + } + } + *q = '\0'; +} + +/* ------------------------------------------------------------- */ +/* XML tokenizer */ + +#define TOKEN_UNKNOWN 0 +#define TOKEN_BEGIN_TAG 1 /* */ +#define TOKEN_EMPTY_CLOSE_BRACKET 4 /* /> */ +#define TOKEN_PCDATA 5 /* pcdata */ +#define TOKEN_CDATA 6 /* cdata */ +#define TOKEN_EOF 7 + +static TCHAR* CurPos = NULL; +static TCHAR* CurTokenName = NULL; +static int CurTokenType; +static int MaxTokenSize = -1; + +/* Copy token from buffer to Token variable */ +static void SetToken(int type, TCHAR* start, TCHAR* end) { + int len = end - start; + if (len > MaxTokenSize) { + if (CurTokenName != NULL) free(CurTokenName); + CurTokenName = (TCHAR *) malloc((len + 1) * sizeof (TCHAR)); + if (CurTokenName == NULL) { + return; + } + MaxTokenSize = len; + } + + CurTokenType = type; + JPACKAGE_STRNCPY(CurTokenName, len + 1, start, len); + CurTokenName[len] = '\0'; +} + +/* Skip XML comments, doctypes, and prolog tags */ +static TCHAR* SkipFilling(void) { + TCHAR *q = CurPos; + + /* Skip white space and comment sections */ + do { + q = CurPos; + CurPos = SkipWhiteSpace(CurPos); + CurPos = SkipXMLComment(CurPos); /* Must be called befor DocTypes */ + CurPos = SkipXMLDocType(CurPos); /* directives */ + CurPos = SkipXMLProlog(CurPos); /* directives */ + } while (CurPos != q); + + return CurPos; +} + +/* Parses next token and initializes the global token variables above + The tokennizer automatically skips comments () and + directives. + */ +static void GetNextToken(void) { + TCHAR *p, *q; + + /* Skip white space and comment sections */ + p = SkipFilling(); + + if (p == NULL || *p == '\0') { + CurTokenType = TOKEN_EOF; + return; + } else if (p[0] == '<' && p[1] == '/') { + /* TOKEN_END_TAG */ + q = SkipXMLName(p + 2); + SetToken(TOKEN_END_TAG, p + 2, q); + p = q; + } else if (*p == '<') { + /* TOKEN_BEGIN_TAG */ + q = SkipXMLName(p + 1); + SetToken(TOKEN_BEGIN_TAG, p + 1, q); + p = q; + } else if (p[0] == '>') { + CurTokenType = TOKEN_CLOSE_BRACKET; + NEXT_CHAR(p); + } else if (p[0] == '/' && p[1] == '>') { + CurTokenType = TOKEN_EMPTY_CLOSE_BRACKET; + SKIP_CHARS(p, 2); + } else { + /* Search for end of data */ + q = p + 1; + while (*q && *q != '<') { + if (IsPCData(q)) { + q = SkipPCData(q); + } else { + NEXT_CHAR(q); + } + } + SetToken(TOKEN_PCDATA, p, q); + /* Convert all entities inside token */ + ConvertBuiltInEntities(CurTokenName); + p = q; + } + /* Advance pointer to beginning of next token */ + CurPos = p; +} + +static XMLNode* CreateXMLNode(int type, TCHAR* name) { + XMLNode* node; + node = (XMLNode*) malloc(sizeof (XMLNode)); + if (node == NULL) { + return NULL; + } + node->_type = type; + node->_name = name; + node->_next = NULL; + node->_sub = NULL; + node->_attributes = NULL; + return node; +} + +static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value) { + XMLAttribute* attr; + attr = (XMLAttribute*) malloc(sizeof (XMLAttribute)); + if (attr == NULL) { + return NULL; + } + attr->_name = name; + attr->_value = value; + attr->_next = NULL; + return attr; +} + +XMLNode* ParseXMLDocument(TCHAR* buf) { + XMLNode* root; + int err_code = setjmp(jmpbuf); + switch (err_code) { + case JMP_NO_ERROR: +#ifndef _UNICODE + /* Remove UTF-8 encoding from buffer */ + RemoveNonAsciiUTF8FromBuffer(buf); +#endif + + /* Get first Token */ + CurPos = buf; + GetNextToken(); + + /* Parse document*/ + root = ParseXMLElement(); + break; + case JMP_OUT_OF_RANGE: + /* cleanup: */ + if (root_node != NULL) { + FreeXMLDocument(root_node); + root_node = NULL; + } + if (CurTokenName != NULL) free(CurTokenName); + fprintf(stderr, "Error during parsing jnlp file...\n"); + exit(-1); + break; + default: + root = NULL; + break; + } + + return root; +} + +static XMLNode* ParseXMLElement(void) { + XMLNode* node = NULL; + XMLNode* subnode = NULL; + XMLNode* nextnode = NULL; + XMLAttribute* attr = NULL; + + if (CurTokenType == TOKEN_BEGIN_TAG) { + + /* Create node for new element tag */ + node = CreateXMLNode(xmlTagType, JPACKAGE_STRDUP(CurTokenName)); + /* We need to save root node pointer to be able to cleanup + if an error happens during parsing */ + if (!root_node) { + root_node = node; + } + /* Parse attributes. This section eats a all input until + EOF, a > or a /> */ + attr = ParseXMLAttribute(); + while (attr != NULL) { + attr->_next = node->_attributes; + node->_attributes = attr; + attr = ParseXMLAttribute(); + } + + /* This will eihter be a TOKEN_EOF, TOKEN_CLOSE_BRACKET, or a + * TOKEN_EMPTY_CLOSE_BRACKET */ + GetNextToken(); + + if (CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET) { + GetNextToken(); + /* We are done with the sublevel - fall through to continue */ + /* parsing tags at the same level */ + } else if (CurTokenType == TOKEN_CLOSE_BRACKET) { + GetNextToken(); + + /* Parse until end tag if found */ + node->_sub = ParseXMLElement(); + + if (CurTokenType == TOKEN_END_TAG) { + /* Find closing bracket '>' for end tag */ + do { + GetNextToken(); + } while (CurTokenType != TOKEN_EOF && + CurTokenType != TOKEN_CLOSE_BRACKET); + GetNextToken(); + } + } + + /* Continue parsing rest on same level */ + if (CurTokenType != TOKEN_EOF) { + /* Parse rest of stream at same level */ + node->_next = ParseXMLElement(); + } + return node; + + } else if (CurTokenType == TOKEN_PCDATA) { + /* Create node for pcdata */ + node = CreateXMLNode(xmlPCDataType, JPACKAGE_STRDUP(CurTokenName)); + /* We need to save root node pointer to be able to cleanup + if an error happens during parsing */ + if (!root_node) { + root_node = node; + } + GetNextToken(); + return node; + } + + /* Something went wrong. */ + return NULL; +} + +/* Parses an XML attribute. */ +static XMLAttribute* ParseXMLAttribute(void) { + TCHAR* q = NULL; + TCHAR* name = NULL; + TCHAR* PrevPos = NULL; + + do { + /* We need to check this condition to avoid endless loop + in case if an error happend during parsing. */ + if (PrevPos == CurPos) { + if (name != NULL) { + free(name); + name = NULL; + } + + return NULL; + } + + PrevPos = CurPos; + + /* Skip whitespace etc. */ + SkipFilling(); + + /* Check if we are done witht this attribute section */ + if (CurPos[0] == '\0' || + CurPos[0] == '>' || + (CurPos[0] == '/' && CurPos[1] == '>')) { + + if (name != NULL) { + free(name); + name = NULL; + } + + return NULL; + } + + /* Find end of name */ + q = CurPos; + while (*q && !iswspace(*q) && *q != '=') NEXT_CHAR(q); + + SetToken(TOKEN_UNKNOWN, CurPos, q); + if (name) { + free(name); + name = NULL; + } + name = JPACKAGE_STRDUP(CurTokenName); + + /* Skip any whitespace */ + CurPos = q; + CurPos = SkipFilling(); + + /* Next TCHARacter must be '=' for a valid attribute. + If it is not, this is really an error. + We ignore this, and just try to parse an attribute + out of the rest of the string. + */ + } while (*CurPos != '='); + + NEXT_CHAR(CurPos); + CurPos = SkipWhiteSpace(CurPos); + /* Parse CDATA part of attribute */ + if ((*CurPos == '\"') || (*CurPos == '\'')) { + TCHAR quoteChar = *CurPos; + q = ++CurPos; + while (*q != '\0' && *q != quoteChar) NEXT_CHAR(q); + SetToken(TOKEN_CDATA, CurPos, q); + CurPos = q + 1; + } else { + q = CurPos; + while (*q != '\0' && !iswspace(*q)) NEXT_CHAR(q); + SetToken(TOKEN_CDATA, CurPos, q); + CurPos = q; + } + + //Note: no need to free name and CurTokenName duplicate; they're assigned + // to an XMLAttribute structure in CreateXMLAttribute + + return CreateXMLAttribute(name, JPACKAGE_STRDUP(CurTokenName)); +} + +void FreeXMLDocument(XMLNode* root) { + if (root == NULL) return; + FreeXMLDocument(root->_sub); + FreeXMLDocument(root->_next); + FreeXMLAttribute(root->_attributes); + free(root->_name); + free(root); +} + +static void FreeXMLAttribute(XMLAttribute* attr) { + if (attr == NULL) return; + free(attr->_name); + free(attr->_value); + FreeXMLAttribute(attr->_next); + free(attr); +} + +/* Find element at current level with a given name */ +XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name) { + if (root == NULL) return NULL; + + if (root->_type == xmlTagType && JPACKAGE_STRCMP(root->_name, name) == 0) { + return root; + } + + return FindXMLChild(root->_next, name); +} + +/* Search for an attribute with the given name and returns the contents. + * Returns NULL if attribute is not found + */ +TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name) { + if (attr == NULL) return NULL; + if (JPACKAGE_STRCMP(attr->_name, name) == 0) return attr->_value; + return FindXMLAttribute(attr->_next, name); +} + +void PrintXMLDocument(XMLNode* node, int indt) { + if (node == NULL) return; + + if (node->_type == xmlTagType) { + JPACKAGE_PRINTF(_T("\n")); + indent(indt); + JPACKAGE_PRINTF(_T("<%s"), node->_name); + PrintXMLAttributes(node->_attributes); + if (node->_sub == NULL) { + JPACKAGE_PRINTF(_T("/>\n")); + } else { + JPACKAGE_PRINTF(_T(">")); + PrintXMLDocument(node->_sub, indt + 1); + indent(indt); + JPACKAGE_PRINTF(_T(""), node->_name); + } + } else { + JPACKAGE_PRINTF(_T("%s"), node->_name); + } + PrintXMLDocument(node->_next, indt); +} + +static void PrintXMLAttributes(XMLAttribute* attr) { + if (attr == NULL) return; + + JPACKAGE_PRINTF(_T(" %s=\"%s\""), attr->_name, attr->_value); + PrintXMLAttributes(attr->_next); +} + +static void indent(int indt) { + int i; + for (i = 0; i < indt; i++) { + JPACKAGE_PRINTF(_T(" ")); + } +} + +const TCHAR *CDStart = _T(""); + +static TCHAR* SkipPCData(TCHAR *p) { + TCHAR *end = JPACKAGE_STRSTR(p, CDEnd); + if (end != NULL) { + return end + sizeof (CDEnd); + } + return (++p); +} + +static int IsPCData(TCHAR *p) { + const int size = sizeof (CDStart); + return (JPACKAGE_STRNCMP(CDStart, p, size) == 0); +} --- old/src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.h 2019-11-18 20:40:54.220504600 -0500 +++ /dev/null 2019-11-18 20:40:55.000000000 -0500 @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef LINUXPLATFORM_H -#define LINUXPLATFORM_H - -#include "Platform.h" -#include "PosixPlatform.h" -#include -#include -#include -#include - -class LinuxPlatform : virtual public Platform, PosixPlatform { -private: - pthread_t FMainThread; - -protected: - virtual TString getTmpDirString(); - -public: - LinuxPlatform(void); - virtual ~LinuxPlatform(void); - - TString GetPackageAppDirectory(); - TString GetPackageLauncherDirectory(); - TString GetPackageRuntimeBinDirectory(); - - virtual void ShowMessage(TString title, TString description); - virtual void ShowMessage(TString description); - - virtual TCHAR* ConvertStringToFileSystemString( - TCHAR* Source, bool &release); - virtual TCHAR* ConvertFileSystemStringToString( - TCHAR* Source, bool &release); - - virtual void SetCurrentDirectory(TString Value); - virtual TString GetPackageRootDirectory(); - virtual TString GetAppDataDirectory(); - virtual TString GetAppName(); - - virtual TString GetModuleFileName(); - - virtual TString GetBundledJavaLibraryFileName(TString RuntimePath); - - virtual ISectionalPropertyContainer* GetConfigFile(TString FileName); - - virtual bool IsMainThread(); - virtual TPlatformNumber GetMemorySize(); -}; - -#endif //LINUXPLATFORM_H --- /dev/null 2019-11-18 20:40:55.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/native/libapplauncher/LinuxPlatform.h 2019-11-18 20:40:50.587142000 -0500 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef LINUXPLATFORM_H +#define LINUXPLATFORM_H + +#include "Platform.h" +#include "PosixPlatform.h" +#include +#include +#include +#include + +class LinuxPlatform : virtual public Platform, PosixPlatform { +private: + pthread_t FMainThread; + +protected: + virtual TString getTmpDirString(); + +public: + LinuxPlatform(void); + virtual ~LinuxPlatform(void); + + TString GetPackageAppDirectory(); + TString GetPackageLauncherDirectory(); + TString GetPackageRuntimeBinDirectory(); + + virtual void ShowMessage(TString title, TString description); + virtual void ShowMessage(TString description); + + virtual TCHAR* ConvertStringToFileSystemString( + TCHAR* Source, bool &release); + virtual TCHAR* ConvertFileSystemStringToString( + TCHAR* Source, bool &release); + + virtual TString GetPackageRootDirectory(); + virtual TString GetAppDataDirectory(); + virtual TString GetAppName(); + + virtual TString GetModuleFileName(); + + virtual TString GetBundledJavaLibraryFileName(TString RuntimePath); + + virtual ISectionalPropertyContainer* GetConfigFile(TString FileName); + + virtual bool IsMainThread(); + virtual TPlatformNumber GetMemorySize(); +}; + +#endif //LINUXPLATFORM_H --- old/src/jdk.jpackage/linux/native/libapplauncher/PlatformDefs.h 2019-11-18 20:41:16.131382600 -0500 +++ /dev/null 2019-11-18 20:41:17.000000000 -0500 @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef PLATFORM_DEFS_H -#define PLATFORM_DEFS_H - -#include -#include -#include -#include -#include -#include - -using namespace std; - -#ifndef LINUX -#define LINUX -#endif - -#define _T(x) x - -typedef char TCHAR; -typedef std::string TString; -#define StringLength strlen - -typedef unsigned long DWORD; - -#define TRAILING_PATHSEPARATOR '/' -#define BAD_TRAILING_PATHSEPARATOR '\\' -#define PATH_SEPARATOR ':' -#define BAD_PATH_SEPARATOR ';' -#define MAX_PATH 1000 - -typedef long TPlatformNumber; -typedef pid_t TProcessID; - -#define HMODULE void* - -typedef void* Module; -typedef void* Procedure; - -#define StringToFileSystemString PlatformString -#define FileSystemStringToString PlatformString - -#endif // PLATFORM_DEFS_H --- /dev/null 2019-11-18 20:41:17.000000000 -0500 +++ new/src/jdk.incubator.jpackage/linux/native/libapplauncher/PlatformDefs.h 2019-11-18 20:41:12.601176500 -0500 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef PLATFORM_DEFS_H +#define PLATFORM_DEFS_H + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#ifndef LINUX +#define LINUX +#endif + +#define _T(x) x + +typedef char TCHAR; +typedef std::string TString; +#define StringLength strlen + +typedef unsigned long DWORD; + +#define TRAILING_PATHSEPARATOR '/' +#define BAD_TRAILING_PATHSEPARATOR '\\' +#define PATH_SEPARATOR ':' +#define BAD_PATH_SEPARATOR ';' +#define MAX_PATH 1000 + +typedef long TPlatformNumber; +typedef pid_t TProcessID; + +#define HMODULE void* + +typedef void* Module; +typedef void* Procedure; + +#define StringToFileSystemString PlatformString +#define FileSystemStringToString PlatformString + +#endif // PLATFORM_DEFS_H --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/EnumeratedBundlerParam.java 2019-11-18 20:41:27.205637000 -0500 +++ /dev/null 2019-11-18 20:41:28.000000000 -0500 @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.*; -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * EnumeratedBundlerParams - * - * Contains key-value pairs (elements) where keys are "displayable" - * keys which the IDE can display/choose and values are "identifier" values - * which can be stored in parameters' map. - * - * For instance the Mac has a predefined set of categories which can be applied - * to LSApplicationCategoryType which is required for the mac app store. - * - * The following example illustrates a simple usage of - * the MAC_CATEGORY parameter: - * - *
{@code
- *     Set keys = MAC_CATEGORY.getDisplayableKeys();
- *
- *     String key = getLastValue(keys); // get last value for example
- *
- *     String value = MAC_CATEGORY.getValueForDisplayableKey(key);
- *     params.put(MAC_CATEGORY.getID(), value);
- * }
- * - */ -class EnumeratedBundlerParam extends BundlerParamInfo { - // Not sure if this is the correct order, my idea is that from IDE - // perspective the string to display to the user is the key and then the - // value is some type of object (although probably a String in most cases) - private final Map elements; - private final boolean strict; - - EnumeratedBundlerParam(String id, Class valueType, - Function, T> defaultValueFunction, - BiFunction, T> stringConverter, - Map elements, boolean strict) { - this.id = id; - this.valueType = valueType; - this.defaultValueFunction = defaultValueFunction; - this.stringConverter = stringConverter; - this.elements = elements; - this.strict = strict; - } - - boolean isInPossibleValues(T value) { - return elements.values().contains(value); - } - - // Having the displayable values as the keys seems a bit wacky - Set getDisplayableKeys() { - return Collections.unmodifiableSet(elements.keySet()); - } - - // mapping from a "displayable" key to an "identifier" value. - T getValueForDisplayableKey(String displayableKey) { - return elements.get(displayableKey); - } - - boolean isStrict() { - return strict; - } - - boolean isLoose() { - return !isStrict(); - } - -} --- /dev/null 2019-11-18 20:41:28.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/EnumeratedBundlerParam.java 2019-11-18 20:41:23.522645200 -0500 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * EnumeratedBundlerParams + * + * Contains key-value pairs (elements) where keys are "displayable" + * keys which the IDE can display/choose and values are "identifier" values + * which can be stored in parameters' map. + * + * For instance the Mac has a predefined set of categories which can be applied + * to LSApplicationCategoryType which is required for the mac app store. + * + * The following example illustrates a simple usage of + * the MAC_CATEGORY parameter: + * + *
{@code
+ *     Set keys = MAC_CATEGORY.getDisplayableKeys();
+ *
+ *     String key = getLastValue(keys); // get last value for example
+ *
+ *     String value = MAC_CATEGORY.getValueForDisplayableKey(key);
+ *     params.put(MAC_CATEGORY.getID(), value);
+ * }
+ * + */ +class EnumeratedBundlerParam extends BundlerParamInfo { + // Not sure if this is the correct order, my idea is that from IDE + // perspective the string to display to the user is the key and then the + // value is some type of object (although probably a String in most cases) + private final Map elements; + private final boolean strict; + + EnumeratedBundlerParam(String id, Class valueType, + Function, T> defaultValueFunction, + BiFunction, T> stringConverter, + Map elements, boolean strict) { + this.id = id; + this.valueType = valueType; + this.defaultValueFunction = defaultValueFunction; + this.stringConverter = stringConverter; + this.elements = elements; + this.strict = strict; + } + + boolean isInPossibleValues(T value) { + return elements.values().contains(value); + } + + // Having the displayable values as the keys seems a bit wacky + Set getDisplayableKeys() { + return Collections.unmodifiableSet(elements.keySet()); + } + + // mapping from a "displayable" key to an "identifier" value. + T getValueForDisplayableKey(String displayableKey) { + return elements.get(displayableKey); + } + + boolean isStrict() { + return strict; + } + + boolean isLoose() { + return !isStrict(); + } + +} --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java 2019-11-18 20:41:48.581807900 -0500 +++ /dev/null 2019-11-18 20:41:50.000000000 -0500 @@ -1,382 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.io.IOException; -import java.math.BigInteger; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; - -import static jdk.jpackage.internal.StandardBundlerParam.*; -import static jdk.jpackage.internal.MacBaseInstallerBundler.*; -import jdk.jpackage.internal.AbstractAppImageBuilder; - -public class MacAppBundler extends AbstractImageBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MacResources"); - - private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns"; - - public static Map getMacCategories() { - Map map = new HashMap<>(); - map.put("Business", "public.app-category.business"); - map.put("Developer Tools", "public.app-category.developer-tools"); - map.put("Education", "public.app-category.education"); - map.put("Entertainment", "public.app-category.entertainment"); - map.put("Finance", "public.app-category.finance"); - map.put("Games", "public.app-category.games"); - map.put("Graphics & Design", "public.app-category.graphics-design"); - map.put("Healthcare & Fitness", - "public.app-category.healthcare-fitness"); - map.put("Lifestyle", "public.app-category.lifestyle"); - map.put("Medical", "public.app-category.medical"); - map.put("Music", "public.app-category.music"); - map.put("News", "public.app-category.news"); - map.put("Photography", "public.app-category.photography"); - map.put("Productivity", "public.app-category.productivity"); - map.put("Reference", "public.app-category.reference"); - map.put("Social Networking", "public.app-category.social-networking"); - map.put("Sports", "public.app-category.sports"); - map.put("Travel", "public.app-category.travel"); - map.put("Utilities", "public.app-category.utilities"); - map.put("Video", "public.app-category.video"); - map.put("Weather", "public.app-category.weather"); - - map.put("Action Games", "public.app-category.action-games"); - map.put("Adventure Games", "public.app-category.adventure-games"); - map.put("Arcade Games", "public.app-category.arcade-games"); - map.put("Board Games", "public.app-category.board-games"); - map.put("Card Games", "public.app-category.card-games"); - map.put("Casino Games", "public.app-category.casino-games"); - map.put("Dice Games", "public.app-category.dice-games"); - map.put("Educational Games", "public.app-category.educational-games"); - map.put("Family Games", "public.app-category.family-games"); - map.put("Kids Games", "public.app-category.kids-games"); - map.put("Music Games", "public.app-category.music-games"); - map.put("Puzzle Games", "public.app-category.puzzle-games"); - map.put("Racing Games", "public.app-category.racing-games"); - map.put("Role Playing Games", "public.app-category.role-playing-games"); - map.put("Simulation Games", "public.app-category.simulation-games"); - map.put("Sports Games", "public.app-category.sports-games"); - map.put("Strategy Games", "public.app-category.strategy-games"); - map.put("Trivia Games", "public.app-category.trivia-games"); - map.put("Word Games", "public.app-category.word-games"); - - return map; - } - - public static final EnumeratedBundlerParam MAC_CATEGORY = - new EnumeratedBundlerParam<>( - Arguments.CLIOptions.MAC_APP_STORE_CATEGORY.getId(), - String.class, - params -> "Unknown", - (s, p) -> s, - getMacCategories(), - false //strict - for MacStoreBundler this should be strict - ); - - public static final BundlerParamInfo MAC_CF_BUNDLE_NAME = - new StandardBundlerParam<>( - Arguments.CLIOptions.MAC_BUNDLE_NAME.getId(), - String.class, - params -> null, - (s, p) -> s); - - public static final BundlerParamInfo MAC_CF_BUNDLE_IDENTIFIER = - new StandardBundlerParam<>( - Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), - String.class, - IDENTIFIER::fetchFrom, - (s, p) -> s); - - public static final BundlerParamInfo MAC_CF_BUNDLE_VERSION = - new StandardBundlerParam<>( - "mac.CFBundleVersion", - String.class, - p -> { - String s = VERSION.fetchFrom(p); - if (validCFBundleVersion(s)) { - return s; - } else { - return "100"; - } - }, - (s, p) -> s); - - public static final BundlerParamInfo DEFAULT_ICNS_ICON = - new StandardBundlerParam<>( - ".mac.default.icns", - String.class, - params -> TEMPLATE_BUNDLE_ICON, - (s, p) -> s); - - public static final BundlerParamInfo DEVELOPER_ID_APP_SIGNING_KEY = - new StandardBundlerParam<>( - "mac.signing-key-developer-id-app", - String.class, - params -> { - String result = MacBaseInstallerBundler.findKey( - "Developer ID Application: " - + SIGNING_KEY_USER.fetchFrom(params), - SIGNING_KEYCHAIN.fetchFrom(params), - VERBOSE.fetchFrom(params)); - if (result != null) { - MacCertificate certificate = new MacCertificate(result, - VERBOSE.fetchFrom(params)); - - if (!certificate.isValid()) { - Log.error(MessageFormat.format(I18N.getString( - "error.certificate.expired"), result)); - } - } - - return result; - }, - (s, p) -> s); - - public static final BundlerParamInfo BUNDLE_ID_SIGNING_PREFIX = - new StandardBundlerParam<>( - Arguments.CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(), - String.class, - params -> IDENTIFIER.fetchFrom(params) + ".", - (s, p) -> s); - - public static final BundlerParamInfo ICON_ICNS = - new StandardBundlerParam<>( - "icon.icns", - File.class, - params -> { - File f = ICON.fetchFrom(params); - if (f != null && !f.getName().toLowerCase().endsWith(".icns")) { - Log.error(MessageFormat.format( - I18N.getString("message.icon-not-icns"), f)); - return null; - } - return f; - }, - (s, p) -> new File(s)); - - public static boolean validCFBundleVersion(String v) { - // CFBundleVersion (String - iOS, OS X) specifies the build version - // number of the bundle, which identifies an iteration (released or - // unreleased) of the bundle. The build version number should be a - // string comprised of three non-negative, period-separated integers - // with the first integer being greater than zero. The string should - // only contain numeric (0-9) and period (.) characters. Leading zeros - // are truncated from each integer and will be ignored (that is, - // 1.02.3 is equivalent to 1.2.3). This key is not localizable. - - if (v == null) { - return false; - } - - String p[] = v.split("\\."); - if (p.length > 3 || p.length < 1) { - Log.verbose(I18N.getString( - "message.version-string-too-many-components")); - return false; - } - - try { - BigInteger n = new BigInteger(p[0]); - if (BigInteger.ONE.compareTo(n) > 0) { - Log.verbose(I18N.getString( - "message.version-string-first-number-not-zero")); - return false; - } - if (p.length > 1) { - n = new BigInteger(p[1]); - if (BigInteger.ZERO.compareTo(n) > 0) { - Log.verbose(I18N.getString( - "message.version-string-no-negative-numbers")); - return false; - } - } - if (p.length > 2) { - n = new BigInteger(p[2]); - if (BigInteger.ZERO.compareTo(n) > 0) { - Log.verbose(I18N.getString( - "message.version-string-no-negative-numbers")); - return false; - } - } - } catch (NumberFormatException ne) { - Log.verbose(I18N.getString("message.version-string-numbers-only")); - Log.verbose(ne); - return false; - } - - return true; - } - - @Override - public boolean validate(Map params) - throws UnsupportedPlatformException, ConfigException { - try { - return doValidate(params); - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - private boolean doValidate(Map p) - throws UnsupportedPlatformException, ConfigException { - if (Platform.getPlatform() != Platform.MAC) { - throw new UnsupportedPlatformException(); - } - - imageBundleValidation(p); - - if (StandardBundlerParam.getPredefinedAppImage(p) != null) { - return true; - } - - // validate short version - if (!validCFBundleVersion(MAC_CF_BUNDLE_VERSION.fetchFrom(p))) { - throw new ConfigException( - I18N.getString("error.invalid-cfbundle-version"), - I18N.getString("error.invalid-cfbundle-version.advice")); - } - - // reject explicitly set sign to true and no valid signature key - if (Optional.ofNullable(MacAppImageBuilder. - SIGN_BUNDLE.fetchFrom(p)).orElse(Boolean.FALSE)) { - String signingIdentity = DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(p); - if (signingIdentity == null) { - throw new ConfigException( - I18N.getString("error.explicit-sign-no-cert"), - I18N.getString("error.explicit-sign-no-cert.advice")); - } - } - - return true; - } - - File doBundle(Map p, File outputDirectory, - boolean dependentTask) throws PackagerException { - if (StandardBundlerParam.isRuntimeInstaller(p)) { - return PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); - } else { - return doAppBundle(p, outputDirectory, dependentTask); - } - } - - File doAppBundle(Map p, File outputDirectory, - boolean dependentTask) throws PackagerException { - try { - File rootDirectory = createRoot(p, outputDirectory, dependentTask, - APP_NAME.fetchFrom(p) + ".app"); - AbstractAppImageBuilder appBuilder = - new MacAppImageBuilder(p, outputDirectory.toPath()); - if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) { - JLinkBundlerHelper.execute(p, appBuilder); - } else { - StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder); - } - return rootDirectory; - } catch (PackagerException pe) { - throw pe; - } catch (Exception ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - ///////////////////////////////////////////////////////////////////////// - // Implement Bundler - ///////////////////////////////////////////////////////////////////////// - - @Override - public String getName() { - return I18N.getString("app.bundler.name"); - } - - @Override - public String getDescription() { - return I18N.getString("app.bundler.description"); - } - - @Override - public String getID() { - return "mac.app"; - } - - @Override - public String getBundleType() { - return "IMAGE"; - } - - @Override - public Collection> getBundleParameters() { - return getAppBundleParameters(); - } - - public static Collection> getAppBundleParameters() { - return Arrays.asList( - APP_NAME, - APP_RESOURCES, - ARGUMENTS, - BUNDLE_ID_SIGNING_PREFIX, - CLASSPATH, - DEVELOPER_ID_APP_SIGNING_KEY, - ICON_ICNS, - JAVA_OPTIONS, - MAC_CATEGORY, - MAC_CF_BUNDLE_IDENTIFIER, - MAC_CF_BUNDLE_NAME, - MAC_CF_BUNDLE_VERSION, - MAIN_CLASS, - MAIN_JAR, - SIGNING_KEYCHAIN, - VERSION, - VERBOSE - ); - } - - - @Override - public File execute(Map params, - File outputParentDir) throws PackagerException { - return doBundle(params, outputParentDir, false); - } - - @Override - public boolean supported(boolean runtimeInstaller) { - return Platform.getPlatform() == Platform.MAC; - } - -} --- /dev/null 2019-11-18 20:41:50.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/MacAppBundler.java 2019-11-18 20:41:44.957427000 -0500 @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.IOException; +import java.math.BigInteger; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; +import static jdk.incubator.jpackage.internal.MacBaseInstallerBundler.*; + +public class MacAppBundler extends AbstractImageBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MacResources"); + + private static final String TEMPLATE_BUNDLE_ICON = "java.icns"; + + public static final BundlerParamInfo MAC_CF_BUNDLE_NAME = + new StandardBundlerParam<>( + Arguments.CLIOptions.MAC_BUNDLE_NAME.getId(), + String.class, + params -> null, + (s, p) -> s); + + public static final BundlerParamInfo MAC_CF_BUNDLE_VERSION = + new StandardBundlerParam<>( + "mac.CFBundleVersion", + String.class, + p -> { + String s = VERSION.fetchFrom(p); + if (validCFBundleVersion(s)) { + return s; + } else { + return "100"; + } + }, + (s, p) -> s); + + public static final BundlerParamInfo DEFAULT_ICNS_ICON = + new StandardBundlerParam<>( + ".mac.default.icns", + String.class, + params -> TEMPLATE_BUNDLE_ICON, + (s, p) -> s); + + public static final BundlerParamInfo DEVELOPER_ID_APP_SIGNING_KEY = + new StandardBundlerParam<>( + "mac.signing-key-developer-id-app", + String.class, + params -> { + String result = MacBaseInstallerBundler.findKey( + "Developer ID Application: " + + SIGNING_KEY_USER.fetchFrom(params), + SIGNING_KEYCHAIN.fetchFrom(params), + VERBOSE.fetchFrom(params)); + if (result != null) { + MacCertificate certificate = new MacCertificate(result); + + if (!certificate.isValid()) { + Log.error(MessageFormat.format(I18N.getString( + "error.certificate.expired"), result)); + } + } + + return result; + }, + (s, p) -> s); + + public static final BundlerParamInfo BUNDLE_ID_SIGNING_PREFIX = + new StandardBundlerParam<>( + Arguments.CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(), + String.class, + params -> IDENTIFIER.fetchFrom(params) + ".", + (s, p) -> s); + + public static final BundlerParamInfo ICON_ICNS = + new StandardBundlerParam<>( + "icon.icns", + File.class, + params -> { + File f = ICON.fetchFrom(params); + if (f != null && !f.getName().toLowerCase().endsWith(".icns")) { + Log.error(MessageFormat.format( + I18N.getString("message.icon-not-icns"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + + public static boolean validCFBundleVersion(String v) { + // CFBundleVersion (String - iOS, OS X) specifies the build version + // number of the bundle, which identifies an iteration (released or + // unreleased) of the bundle. The build version number should be a + // string comprised of three non-negative, period-separated integers + // with the first integer being greater than zero. The string should + // only contain numeric (0-9) and period (.) characters. Leading zeros + // are truncated from each integer and will be ignored (that is, + // 1.02.3 is equivalent to 1.2.3). This key is not localizable. + + if (v == null) { + return false; + } + + String p[] = v.split("\\."); + if (p.length > 3 || p.length < 1) { + Log.verbose(I18N.getString( + "message.version-string-too-many-components")); + return false; + } + + try { + BigInteger n = new BigInteger(p[0]); + if (BigInteger.ONE.compareTo(n) > 0) { + Log.verbose(I18N.getString( + "message.version-string-first-number-not-zero")); + return false; + } + if (p.length > 1) { + n = new BigInteger(p[1]); + if (BigInteger.ZERO.compareTo(n) > 0) { + Log.verbose(I18N.getString( + "message.version-string-no-negative-numbers")); + return false; + } + } + if (p.length > 2) { + n = new BigInteger(p[2]); + if (BigInteger.ZERO.compareTo(n) > 0) { + Log.verbose(I18N.getString( + "message.version-string-no-negative-numbers")); + return false; + } + } + } catch (NumberFormatException ne) { + Log.verbose(I18N.getString("message.version-string-numbers-only")); + Log.verbose(ne); + return false; + } + + return true; + } + + @Override + public boolean validate(Map params) + throws ConfigException { + try { + return doValidate(params); + } catch (RuntimeException re) { + if (re.getCause() instanceof ConfigException) { + throw (ConfigException) re.getCause(); + } else { + throw new ConfigException(re); + } + } + } + + private boolean doValidate(Map params) + throws ConfigException { + + imageBundleValidation(params); + + if (StandardBundlerParam.getPredefinedAppImage(params) != null) { + return true; + } + + // validate short version + if (!validCFBundleVersion(MAC_CF_BUNDLE_VERSION.fetchFrom(params))) { + throw new ConfigException( + I18N.getString("error.invalid-cfbundle-version"), + I18N.getString("error.invalid-cfbundle-version.advice")); + } + + // reject explicitly set sign to true and no valid signature key + if (Optional.ofNullable(MacAppImageBuilder. + SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.FALSE)) { + String signingIdentity = + DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(params); + if (signingIdentity == null) { + throw new ConfigException( + I18N.getString("error.explicit-sign-no-cert"), + I18N.getString("error.explicit-sign-no-cert.advice")); + } + + // Signing will not work without Xcode with command line developer tools + try { + ProcessBuilder pb = new ProcessBuilder("xcrun", "--help"); + Process p = pb.start(); + int code = p.waitFor(); + if (code != 0) { + throw new ConfigException( + I18N.getString("error.no.xcode.signing"), + I18N.getString("error.no.xcode.signing.advice")); + } + } catch (IOException | InterruptedException ex) { + throw new ConfigException(ex); + } + } + + return true; + } + + File doBundle(Map params, File outputDirectory, + boolean dependentTask) throws PackagerException { + if (StandardBundlerParam.isRuntimeInstaller(params)) { + return PREDEFINED_RUNTIME_IMAGE.fetchFrom(params); + } else { + return doAppBundle(params, outputDirectory, dependentTask); + } + } + + File doAppBundle(Map params, File outputDirectory, + boolean dependentTask) throws PackagerException { + try { + File rootDirectory = createRoot(params, outputDirectory, + dependentTask, APP_NAME.fetchFrom(params) + ".app"); + AbstractAppImageBuilder appBuilder = + new MacAppImageBuilder(params, outputDirectory.toPath()); + if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(params) == null ) { + JLinkBundlerHelper.execute(params, appBuilder); + } else { + StandardBundlerParam.copyPredefinedRuntimeImage( + params, appBuilder); + } + return rootDirectory; + } catch (PackagerException pe) { + throw pe; + } catch (Exception ex) { + Log.verbose(ex); + throw new PackagerException(ex); + } + } + + ///////////////////////////////////////////////////////////////////////// + // Implement Bundler + ///////////////////////////////////////////////////////////////////////// + + @Override + public String getName() { + return I18N.getString("app.bundler.name"); + } + + @Override + public String getID() { + return "mac.app"; + } + + @Override + public String getBundleType() { + return "IMAGE"; + } + + @Override + public File execute(Map params, + File outputParentDir) throws PackagerException { + return doBundle(params, outputParentDir, false); + } + + @Override + public boolean supported(boolean runtimeInstaller) { + return true; + } + + @Override + public boolean isDefault() { + return false; + } + +} --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java 2019-11-18 20:42:10.581026200 -0500 +++ /dev/null 2019-11-18 20:42:12.000000000 -0500 @@ -1,957 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.UncheckedIOException; -import java.io.Writer; -import java.math.BigInteger; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.nio.file.attribute.PosixFilePermission; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.ResourceBundle; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -import static jdk.jpackage.internal.StandardBundlerParam.*; -import static jdk.jpackage.internal.MacBaseInstallerBundler.*; -import static jdk.jpackage.internal.MacAppBundler.*; - -public class MacAppImageBuilder extends AbstractAppImageBuilder { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MacResources"); - - private static final String LIBRARY_NAME = "libapplauncher.dylib"; - private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns"; - private static final String OS_TYPE_CODE = "APPL"; - private static final String TEMPLATE_INFO_PLIST_LITE = - "Info-lite.plist.template"; - private static final String TEMPLATE_RUNTIME_INFO_PLIST = - "Runtime-Info.plist.template"; - - private final Path root; - private final Path contentsDir; - private final Path javaDir; - private final Path javaModsDir; - private final Path resourcesDir; - private final Path macOSDir; - private final Path runtimeDir; - private final Path runtimeRoot; - private final Path mdir; - - private final Map params; - - private static List keyChains; - - public static final BundlerParamInfo - MAC_CONFIGURE_LAUNCHER_IN_PLIST = new StandardBundlerParam<>( - "mac.configure-launcher-in-plist", - Boolean.class, - params -> Boolean.FALSE, - (s, p) -> Boolean.valueOf(s)); - - public static final EnumeratedBundlerParam MAC_CATEGORY = - new EnumeratedBundlerParam<>( - Arguments.CLIOptions.MAC_APP_STORE_CATEGORY.getId(), - String.class, - params -> "Unknown", - (s, p) -> s, - MacAppBundler.getMacCategories(), - false //strict - for MacStoreBundler this should be strict - ); - - public static final BundlerParamInfo MAC_CF_BUNDLE_NAME = - new StandardBundlerParam<>( - "mac.CFBundleName", - String.class, - params -> null, - (s, p) -> s); - - public static final BundlerParamInfo MAC_CF_BUNDLE_IDENTIFIER = - new StandardBundlerParam<>( - Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), - String.class, - IDENTIFIER::fetchFrom, - (s, p) -> s); - - public static final BundlerParamInfo MAC_CF_BUNDLE_VERSION = - new StandardBundlerParam<>( - "mac.CFBundleVersion", - String.class, - p -> { - String s = VERSION.fetchFrom(p); - if (validCFBundleVersion(s)) { - return s; - } else { - return "100"; - } - }, - (s, p) -> s); - - public static final BundlerParamInfo DEFAULT_ICNS_ICON = - new StandardBundlerParam<>( - ".mac.default.icns", - String.class, - params -> TEMPLATE_BUNDLE_ICON, - (s, p) -> s); - - public static final BundlerParamInfo ICON_ICNS = - new StandardBundlerParam<>( - "icon.icns", - File.class, - params -> { - File f = ICON.fetchFrom(params); - if (f != null && !f.getName().toLowerCase().endsWith(".icns")) { - Log.error(MessageFormat.format( - I18N.getString("message.icon-not-icns"), f)); - return null; - } - return f; - }, - (s, p) -> new File(s)); - - public static final StandardBundlerParam SIGN_BUNDLE = - new StandardBundlerParam<>( - Arguments.CLIOptions.MAC_SIGN.getId(), - Boolean.class, - params -> false, - // valueOf(null) is false, we actually do want null in some cases - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? - null : Boolean.valueOf(s) - ); - - public MacAppImageBuilder(Map config, Path imageOutDir) - throws IOException { - super(config, imageOutDir.resolve(APP_NAME.fetchFrom(config) - + ".app/Contents/runtime/Contents/Home")); - - Objects.requireNonNull(imageOutDir); - - this.params = config; - this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params) + ".app"); - this.contentsDir = root.resolve("Contents"); - this.javaDir = contentsDir.resolve("Java"); - this.javaModsDir = javaDir.resolve("mods"); - this.resourcesDir = contentsDir.resolve("Resources"); - this.macOSDir = contentsDir.resolve("MacOS"); - this.runtimeDir = contentsDir.resolve("runtime"); - this.runtimeRoot = runtimeDir.resolve("Contents/Home"); - this.mdir = runtimeRoot.resolve("lib"); - Files.createDirectories(javaDir); - Files.createDirectories(resourcesDir); - Files.createDirectories(macOSDir); - Files.createDirectories(runtimeDir); - } - - public MacAppImageBuilder(Map config, String jreName, - Path imageOutDir) throws IOException { - super(null, imageOutDir.resolve(jreName + "/Contents/Home")); - - Objects.requireNonNull(imageOutDir); - - this.params = config; - this.root = imageOutDir.resolve(jreName ); - this.contentsDir = root.resolve("Contents"); - this.javaDir = null; - this.javaModsDir = null; - this.resourcesDir = null; - this.macOSDir = null; - this.runtimeDir = this.root; - this.runtimeRoot = runtimeDir.resolve("Contents/Home"); - this.mdir = runtimeRoot.resolve("lib"); - - Files.createDirectories(runtimeDir); - } - - private void writeEntry(InputStream in, Path dstFile) throws IOException { - Files.createDirectories(dstFile.getParent()); - Files.copy(in, dstFile); - } - - // chmod ugo+x file - private void setExecutable(Path file) { - try { - Set perms = - Files.getPosixFilePermissions(file); - perms.add(PosixFilePermission.OWNER_EXECUTE); - perms.add(PosixFilePermission.GROUP_EXECUTE); - perms.add(PosixFilePermission.OTHERS_EXECUTE); - Files.setPosixFilePermissions(file, perms); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - } - - private static void createUtf8File(File file, String content) - throws IOException { - try (OutputStream fout = new FileOutputStream(file); - Writer output = new OutputStreamWriter(fout, "UTF-8")) { - output.write(content); - } - } - - public static boolean validCFBundleVersion(String v) { - // CFBundleVersion (String - iOS, OS X) specifies the build version - // number of the bundle, which identifies an iteration (released or - // unreleased) of the bundle. The build version number should be a - // string comprised of three non-negative, period-separated integers - // with the first integer being greater than zero. The string should - // only contain numeric (0-9) and period (.) characters. Leading zeros - // are truncated from each integer and will be ignored (that is, - // 1.02.3 is equivalent to 1.2.3). This key is not localizable. - - if (v == null) { - return false; - } - - String p[] = v.split("\\."); - if (p.length > 3 || p.length < 1) { - Log.verbose(I18N.getString( - "message.version-string-too-many-components")); - return false; - } - - try { - BigInteger n = new BigInteger(p[0]); - if (BigInteger.ONE.compareTo(n) > 0) { - Log.verbose(I18N.getString( - "message.version-string-first-number-not-zero")); - return false; - } - if (p.length > 1) { - n = new BigInteger(p[1]); - if (BigInteger.ZERO.compareTo(n) > 0) { - Log.verbose(I18N.getString( - "message.version-string-no-negative-numbers")); - return false; - } - } - if (p.length > 2) { - n = new BigInteger(p[2]); - if (BigInteger.ZERO.compareTo(n) > 0) { - Log.verbose(I18N.getString( - "message.version-string-no-negative-numbers")); - return false; - } - } - } catch (NumberFormatException ne) { - Log.verbose(I18N.getString("message.version-string-numbers-only")); - Log.verbose(ne); - return false; - } - - return true; - } - - @Override - public Path getAppDir() { - return javaDir; - } - - @Override - public Path getAppModsDir() { - return javaModsDir; - } - - @Override - public void prepareApplicationFiles() throws IOException { - Map originalParams = new HashMap<>(params); - // Generate PkgInfo - File pkgInfoFile = new File(contentsDir.toFile(), "PkgInfo"); - pkgInfoFile.createNewFile(); - writePkgInfo(pkgInfoFile); - - Path executable = macOSDir.resolve(getLauncherName(params)); - - // create the main app launcher - try (InputStream is_launcher = - getResourceAsStream("jpackageapplauncher"); - InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { - // Copy executable and library to MacOS folder - writeEntry(is_launcher, executable); - writeEntry(is_lib, macOSDir.resolve(LIBRARY_NAME)); - } - executable.toFile().setExecutable(true, false); - // generate main app launcher config file - File cfg = new File(root.toFile(), getLauncherCfgName(params)); - writeCfgFile(params, cfg, "$APPDIR/runtime"); - - // create additional app launcher(s) and config file(s) - List> entryPoints = - StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); - for (Map entryPoint : entryPoints) { - Map tmp = - AddLauncherArguments.merge(originalParams, entryPoint); - - // add executable for add launcher - Path addExecutable = macOSDir.resolve(getLauncherName(tmp)); - try (InputStream is = getResourceAsStream("jpackageapplauncher");) { - writeEntry(is, addExecutable); - } - addExecutable.toFile().setExecutable(true, false); - - // add config file for add launcher - cfg = new File(root.toFile(), getLauncherCfgName(tmp)); - writeCfgFile(tmp, cfg, "$APPDIR/runtime"); - } - - // Copy class path entries to Java folder - copyClassPathEntries(javaDir); - - /*********** Take care of "config" files *******/ - File icon = ICON_ICNS.fetchFrom(params); - - InputStream in = locateResource( - APP_NAME.fetchFrom(params) + ".icns", - "icon", - DEFAULT_ICNS_ICON.fetchFrom(params), - icon, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - Files.copy(in, - resourcesDir.resolve(APP_NAME.fetchFrom(params) + ".icns"), - StandardCopyOption.REPLACE_EXISTING); - - // copy file association icons - for (Map fa : FILE_ASSOCIATIONS.fetchFrom(params)) { - File f = FA_ICON.fetchFrom(fa); - if (f != null && f.exists()) { - try (InputStream in2 = new FileInputStream(f)) { - Files.copy(in2, resourcesDir.resolve(f.getName())); - } - - } - } - - copyRuntimeFiles(); - sign(); - } - - @Override - public void prepareJreFiles() throws IOException { - copyRuntimeFiles(); - sign(); - } - - private void copyRuntimeFiles() throws IOException { - // Generate Info.plist - writeInfoPlist(contentsDir.resolve("Info.plist").toFile()); - - // generate java runtime info.plist - writeRuntimeInfoPlist( - runtimeDir.resolve("Contents/Info.plist").toFile()); - - // copy library - Path runtimeMacOSDir = Files.createDirectories( - runtimeDir.resolve("Contents/MacOS")); - - // JDK 9, 10, and 11 have extra '/jli/' subdir - Path jli = runtimeRoot.resolve("lib/libjli.dylib"); - if (!Files.exists(jli)) { - jli = runtimeRoot.resolve("lib/jli/libjli.dylib"); - } - - Files.copy(jli, runtimeMacOSDir.resolve("libjli.dylib")); - } - - private void sign() throws IOException { - if (Optional.ofNullable( - SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) { - try { - addNewKeychain(params); - } catch (InterruptedException e) { - Log.error(e.getMessage()); - } - String signingIdentity = - DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(params); - if (signingIdentity != null) { - signAppBundle(params, root, signingIdentity, - BUNDLE_ID_SIGNING_PREFIX.fetchFrom(params), null, null); - } - restoreKeychainList(params); - } - } - - private String getLauncherName(Map params) { - if (APP_NAME.fetchFrom(params) != null) { - return APP_NAME.fetchFrom(params); - } else { - return MAIN_CLASS.fetchFrom(params); - } - } - - public static String getLauncherCfgName(Map p) { - return "Contents/Java/" + APP_NAME.fetchFrom(p) + ".cfg"; - } - - private void copyClassPathEntries(Path javaDirectory) throws IOException { - List resourcesList = - APP_RESOURCES_LIST.fetchFrom(params); - if (resourcesList == null) { - throw new RuntimeException( - I18N.getString("message.null-classpath")); - } - - for (RelativeFileSet classPath : resourcesList) { - File srcdir = classPath.getBaseDirectory(); - for (String fname : classPath.getIncludedFiles()) { - copyEntry(javaDirectory, srcdir, fname); - } - } - } - - private String getBundleName(Map params) { - if (MAC_CF_BUNDLE_NAME.fetchFrom(params) != null) { - String bn = MAC_CF_BUNDLE_NAME.fetchFrom(params); - if (bn.length() > 16) { - Log.error(MessageFormat.format(I18N.getString( - "message.bundle-name-too-long-warning"), - MAC_CF_BUNDLE_NAME.getID(), bn)); - } - return MAC_CF_BUNDLE_NAME.fetchFrom(params); - } else if (APP_NAME.fetchFrom(params) != null) { - return APP_NAME.fetchFrom(params); - } else { - String nm = MAIN_CLASS.fetchFrom(params); - if (nm.length() > 16) { - nm = nm.substring(0, 16); - } - return nm; - } - } - - private void writeRuntimeInfoPlist(File file) throws IOException { - Map data = new HashMap<>(); - String identifier = StandardBundlerParam.isRuntimeInstaller(params) ? - MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) : - "com.oracle.java." + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params); - data.put("CF_BUNDLE_IDENTIFIER", identifier); - String name = StandardBundlerParam.isRuntimeInstaller(params) ? - getBundleName(params): "Java Runtime Image"; - data.put("CF_BUNDLE_NAME", name); - data.put("CF_BUNDLE_VERSION", VERSION.fetchFrom(params)); - data.put("CF_BUNDLE_SHORT_VERSION_STRING", VERSION.fetchFrom(params)); - - Writer w = new BufferedWriter(new FileWriter(file)); - w.write(preprocessTextResource("Runtime-Info.plist", - I18N.getString("resource.runtime-info-plist"), - TEMPLATE_RUNTIME_INFO_PLIST, - data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params))); - w.close(); - } - - private void writeInfoPlist(File file) throws IOException { - Log.verbose(MessageFormat.format(I18N.getString( - "message.preparing-info-plist"), file.getAbsolutePath())); - - //prepare config for exe - //Note: do not need CFBundleDisplayName if we don't support localization - Map data = new HashMap<>(); - data.put("DEPLOY_ICON_FILE", APP_NAME.fetchFrom(params) + ".icns"); - data.put("DEPLOY_BUNDLE_IDENTIFIER", - MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params)); - data.put("DEPLOY_BUNDLE_NAME", - getBundleName(params)); - data.put("DEPLOY_BUNDLE_COPYRIGHT", - COPYRIGHT.fetchFrom(params) != null ? - COPYRIGHT.fetchFrom(params) : "Unknown"); - data.put("DEPLOY_LAUNCHER_NAME", getLauncherName(params)); - data.put("DEPLOY_JAVA_RUNTIME_NAME", "$APPDIR/runtime"); - data.put("DEPLOY_BUNDLE_SHORT_VERSION", - VERSION.fetchFrom(params) != null ? - VERSION.fetchFrom(params) : "1.0.0"); - data.put("DEPLOY_BUNDLE_CFBUNDLE_VERSION", - MAC_CF_BUNDLE_VERSION.fetchFrom(params) != null ? - MAC_CF_BUNDLE_VERSION.fetchFrom(params) : "100"); - data.put("DEPLOY_BUNDLE_CATEGORY", MAC_CATEGORY.fetchFrom(params)); - - boolean hasMainJar = MAIN_JAR.fetchFrom(params) != null; - boolean hasMainModule = - StandardBundlerParam.MODULE.fetchFrom(params) != null; - - if (hasMainJar) { - data.put("DEPLOY_MAIN_JAR_NAME", MAIN_JAR.fetchFrom(params). - getIncludedFiles().iterator().next()); - } - else if (hasMainModule) { - data.put("DEPLOY_MODULE_NAME", - StandardBundlerParam.MODULE.fetchFrom(params)); - } - - StringBuilder sb = new StringBuilder(); - List jvmOptions = JAVA_OPTIONS.fetchFrom(params); - - String newline = ""; //So we don't add extra line after last append - for (String o : jvmOptions) { - sb.append(newline).append( - " ").append(o).append(""); - newline = "\n"; - } - - data.put("DEPLOY_JAVA_OPTIONS", sb.toString()); - - sb = new StringBuilder(); - List args = ARGUMENTS.fetchFrom(params); - newline = ""; - // So we don't add unneccessary extra line after last append - - for (String o : args) { - sb.append(newline).append(" ").append(o).append( - ""); - newline = "\n"; - } - data.put("DEPLOY_ARGUMENTS", sb.toString()); - - newline = ""; - - data.put("DEPLOY_LAUNCHER_CLASS", MAIN_CLASS.fetchFrom(params)); - - StringBuilder macroedPath = new StringBuilder(); - for (String s : CLASSPATH.fetchFrom(params).split("[ ;:]+")) { - macroedPath.append(s); - macroedPath.append(":"); - } - macroedPath.deleteCharAt(macroedPath.length() - 1); - - data.put("DEPLOY_APP_CLASSPATH", macroedPath.toString()); - - StringBuilder bundleDocumentTypes = new StringBuilder(); - StringBuilder exportedTypes = new StringBuilder(); - for (Map - fileAssociation : FILE_ASSOCIATIONS.fetchFrom(params)) { - - List extensions = FA_EXTENSIONS.fetchFrom(fileAssociation); - - if (extensions == null) { - Log.verbose(I18N.getString( - "message.creating-association-with-null-extension")); - } - - List mimeTypes = FA_CONTENT_TYPE.fetchFrom(fileAssociation); - String itemContentType = MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) - + "." + ((extensions == null || extensions.isEmpty()) - ? "mime" : extensions.get(0)); - String description = FA_DESCRIPTION.fetchFrom(fileAssociation); - File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICNS - - bundleDocumentTypes.append(" \n") - .append(" LSItemContentTypes\n") - .append(" \n") - .append(" ") - .append(itemContentType) - .append("\n") - .append(" \n") - .append("\n") - .append(" CFBundleTypeName\n") - .append(" ") - .append(description) - .append("\n") - .append("\n") - .append(" LSHandlerRank\n") - .append(" Owner\n") - // TODO make a bundler arg - .append("\n") - .append(" CFBundleTypeRole\n") - .append(" Editor\n") - // TODO make a bundler arg - .append("\n") - .append(" LSIsAppleDefaultForType\n") - .append(" \n") - // TODO make a bundler arg - .append("\n"); - - if (icon != null && icon.exists()) { - bundleDocumentTypes - .append(" CFBundleTypeIconFile\n") - .append(" ") - .append(icon.getName()) - .append("\n"); - } - bundleDocumentTypes.append(" \n"); - - exportedTypes.append(" \n") - .append(" UTTypeIdentifier\n") - .append(" ") - .append(itemContentType) - .append("\n") - .append("\n") - .append(" UTTypeDescription\n") - .append(" ") - .append(description) - .append("\n") - .append(" UTTypeConformsTo\n") - .append(" \n") - .append(" public.data\n") - //TODO expose this? - .append(" \n") - .append("\n"); - - if (icon != null && icon.exists()) { - exportedTypes.append(" UTTypeIconFile\n") - .append(" ") - .append(icon.getName()) - .append("\n") - .append("\n"); - } - - exportedTypes.append("\n") - .append(" UTTypeTagSpecification\n") - .append(" \n") - // TODO expose via param? .append( - // " com.apple.ostype\n"); - // TODO expose via param? .append( - // " ABCD\n") - .append("\n"); - - if (extensions != null && !extensions.isEmpty()) { - exportedTypes.append( - " public.filename-extension\n") - .append(" \n"); - - for (String ext : extensions) { - exportedTypes.append(" ") - .append(ext) - .append("\n"); - } - exportedTypes.append(" \n"); - } - if (mimeTypes != null && !mimeTypes.isEmpty()) { - exportedTypes.append(" public.mime-type\n") - .append(" \n"); - - for (String mime : mimeTypes) { - exportedTypes.append(" ") - .append(mime) - .append("\n"); - } - exportedTypes.append(" \n"); - } - exportedTypes.append(" \n") - .append(" \n"); - } - String associationData; - if (bundleDocumentTypes.length() > 0) { - associationData = - "\n CFBundleDocumentTypes\n \n" - + bundleDocumentTypes.toString() - + " \n\n" - + " UTExportedTypeDeclarations\n \n" - + exportedTypes.toString() - + " \n"; - } else { - associationData = ""; - } - data.put("DEPLOY_FILE_ASSOCIATIONS", associationData); - - - Writer w = new BufferedWriter(new FileWriter(file)); - w.write(preprocessTextResource( - // getConfig_InfoPlist(params).getName(), - "Info.plist", - I18N.getString("resource.app-info-plist"), - TEMPLATE_INFO_PLIST_LITE, - data, VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params))); - w.close(); - } - - private void writePkgInfo(File file) throws IOException { - //hardcoded as it does not seem we need to change it ever - String signature = "????"; - - try (Writer out = new BufferedWriter(new FileWriter(file))) { - out.write(OS_TYPE_CODE + signature); - out.flush(); - } - } - - public static void addNewKeychain(Map params) - throws IOException, InterruptedException { - if (Platform.getMajorVersion() < 10 || - (Platform.getMajorVersion() == 10 && - Platform.getMinorVersion() < 12)) { - // we need this for OS X 10.12+ - return; - } - - String keyChain = SIGNING_KEYCHAIN.fetchFrom(params); - if (keyChain == null || keyChain.isEmpty()) { - return; - } - - // get current keychain list - String keyChainPath = new File (keyChain).getAbsolutePath().toString(); - List keychainList = new ArrayList<>(); - int ret = IOUtils.getProcessOutput( - keychainList, "security", "list-keychains"); - if (ret != 0) { - Log.error(I18N.getString("message.keychain.error")); - return; - } - - boolean contains = keychainList.stream().anyMatch( - str -> str.trim().equals("\""+keyChainPath.trim()+"\"")); - if (contains) { - // keychain is already added in the search list - return; - } - - keyChains = new ArrayList<>(); - // remove " - keychainList.forEach((String s) -> { - String path = s.trim(); - if (path.startsWith("\"") && path.endsWith("\"")) { - path = path.substring(1, path.length()-1); - } - keyChains.add(path); - }); - - List args = new ArrayList<>(); - args.add("security"); - args.add("list-keychains"); - args.add("-s"); - - args.addAll(keyChains); - args.add(keyChain); - - ProcessBuilder pb = new ProcessBuilder(args); - IOUtils.exec(pb, false); - } - - public static void restoreKeychainList(Map params) - throws IOException{ - if (Platform.getMajorVersion() < 10 || - (Platform.getMajorVersion() == 10 && - Platform.getMinorVersion() < 12)) { - // we need this for OS X 10.12+ - return; - } - - if (keyChains == null || keyChains.isEmpty()) { - return; - } - - List args = new ArrayList<>(); - args.add("security"); - args.add("list-keychains"); - args.add("-s"); - - args.addAll(keyChains); - - ProcessBuilder pb = new ProcessBuilder(args); - IOUtils.exec(pb, false); - } - - public static void signAppBundle( - Map params, Path appLocation, - String signingIdentity, String identifierPrefix, - String entitlementsFile, String inheritedEntitlements) - throws IOException { - AtomicReference toThrow = new AtomicReference<>(); - String appExecutable = "/Contents/MacOS/" + APP_NAME.fetchFrom(params); - String keyChain = SIGNING_KEYCHAIN.fetchFrom(params); - - // sign all dylibs and jars - Files.walk(appLocation) - // fix permissions - .peek(path -> { - try { - Set pfp = - Files.getPosixFilePermissions(path); - if (!pfp.contains(PosixFilePermission.OWNER_WRITE)) { - pfp = EnumSet.copyOf(pfp); - pfp.add(PosixFilePermission.OWNER_WRITE); - Files.setPosixFilePermissions(path, pfp); - } - } catch (IOException e) { - Log.debug(e); - } - }) - .filter(p -> Files.isRegularFile(p) && - !(p.toString().contains("/Contents/MacOS/libjli.dylib") - || p.toString().contains( - "/Contents/MacOS/JavaAppletPlugin") - || p.toString().endsWith(appExecutable)) - ).forEach(p -> { - //noinspection ThrowableResultOfMethodCallIgnored - if (toThrow.get() != null) return; - - // If p is a symlink then skip the signing process. - if (Files.isSymbolicLink(p)) { - if (VERBOSE.fetchFrom(params)) { - Log.verbose(MessageFormat.format(I18N.getString( - "message.ignoring.symlink"), p.toString())); - } - } - else { - List args = new ArrayList<>(); - args.addAll(Arrays.asList("codesign", - "-s", signingIdentity, // sign with this key - "--prefix", identifierPrefix, - // use the identifier as a prefix - "-vvvv")); - if (entitlementsFile != null && - (p.toString().endsWith(".jar") - || p.toString().endsWith(".dylib"))) { - args.add("--entitlements"); - args.add(entitlementsFile); // entitlements - } else if (inheritedEntitlements != null && - Files.isExecutable(p)) { - args.add("--entitlements"); - args.add(inheritedEntitlements); - // inherited entitlements for executable processes - } - if (keyChain != null && !keyChain.isEmpty()) { - args.add("--keychain"); - args.add(keyChain); - } - args.add(p.toString()); - - try { - Set oldPermissions = - Files.getPosixFilePermissions(p); - File f = p.toFile(); - f.setWritable(true, true); - - ProcessBuilder pb = new ProcessBuilder(args); - IOUtils.exec(pb, false); - - Files.setPosixFilePermissions(p, oldPermissions); - } catch (IOException ioe) { - toThrow.set(ioe); - } - } - }); - - IOException ioe = toThrow.get(); - if (ioe != null) { - throw ioe; - } - - // sign all runtime and frameworks - Consumer signIdentifiedByPList = path -> { - //noinspection ThrowableResultOfMethodCallIgnored - if (toThrow.get() != null) return; - - try { - List args = new ArrayList<>(); - args.addAll(Arrays.asList("codesign", - "-s", signingIdentity, // sign with this key - "--prefix", identifierPrefix, - // use the identifier as a prefix - "-vvvv")); - if (keyChain != null && !keyChain.isEmpty()) { - args.add("--keychain"); - args.add(keyChain); - } - args.add(path.toString()); - ProcessBuilder pb = new ProcessBuilder(args); - IOUtils.exec(pb, false); - - args = new ArrayList<>(); - args.addAll(Arrays.asList("codesign", - "-s", signingIdentity, // sign with this key - "--prefix", identifierPrefix, - // use the identifier as a prefix - "-vvvv")); - if (keyChain != null && !keyChain.isEmpty()) { - args.add("--keychain"); - args.add(keyChain); - } - args.add(path.toString() - + "/Contents/_CodeSignature/CodeResources"); - pb = new ProcessBuilder(args); - IOUtils.exec(pb, false); - } catch (IOException e) { - toThrow.set(e); - } - }; - - Path javaPath = appLocation.resolve("Contents/runtime"); - if (Files.isDirectory(javaPath)) { - Files.list(javaPath) - .forEach(signIdentifiedByPList); - - ioe = toThrow.get(); - if (ioe != null) { - throw ioe; - } - } - Path frameworkPath = appLocation.resolve("Contents/Frameworks"); - if (Files.isDirectory(frameworkPath)) { - Files.list(frameworkPath) - .forEach(signIdentifiedByPList); - - ioe = toThrow.get(); - if (ioe != null) { - throw ioe; - } - } - - // sign the app itself - List args = new ArrayList<>(); - args.addAll(Arrays.asList("codesign", - "-s", signingIdentity, // sign with this key - "-vvvv")); // super verbose output - if (entitlementsFile != null) { - args.add("--entitlements"); - args.add(entitlementsFile); // entitlements - } - if (keyChain != null && !keyChain.isEmpty()) { - args.add("--keychain"); - args.add(keyChain); - } - args.add(appLocation.toString()); - - ProcessBuilder pb = - new ProcessBuilder(args.toArray(new String[args.size()])); - IOUtils.exec(pb, false); - } - -} --- /dev/null 2019-11-18 20:42:12.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/MacAppImageBuilder.java 2019-11-18 20:42:07.137057300 -0500 @@ -0,0 +1,945 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Writer; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.PosixFilePermission; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.stream.Stream; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; +import static jdk.incubator.jpackage.internal.MacBaseInstallerBundler.*; +import static jdk.incubator.jpackage.internal.MacAppBundler.*; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; + +public class MacAppImageBuilder extends AbstractAppImageBuilder { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MacResources"); + + private static final String LIBRARY_NAME = "libapplauncher.dylib"; + private static final String TEMPLATE_BUNDLE_ICON = "java.icns"; + private static final String OS_TYPE_CODE = "APPL"; + private static final String TEMPLATE_INFO_PLIST_LITE = + "Info-lite.plist.template"; + private static final String TEMPLATE_RUNTIME_INFO_PLIST = + "Runtime-Info.plist.template"; + + private final Path root; + private final Path contentsDir; + private final Path appDir; + private final Path javaModsDir; + private final Path resourcesDir; + private final Path macOSDir; + private final Path runtimeDir; + private final Path runtimeRoot; + private final Path mdir; + + private static List keyChains; + + public static final BundlerParamInfo + MAC_CONFIGURE_LAUNCHER_IN_PLIST = new StandardBundlerParam<>( + "mac.configure-launcher-in-plist", + Boolean.class, + params -> Boolean.FALSE, + (s, p) -> Boolean.valueOf(s)); + + public static final BundlerParamInfo MAC_CF_BUNDLE_NAME = + new StandardBundlerParam<>( + Arguments.CLIOptions.MAC_BUNDLE_NAME.getId(), + String.class, + params -> null, + (s, p) -> s); + + public static final BundlerParamInfo MAC_CF_BUNDLE_IDENTIFIER = + new StandardBundlerParam<>( + Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), + String.class, + params -> { + // Get identifier from app image if user provided + // app image and did not provide the identifier via CLI. + String identifier = extractBundleIdentifier(params); + if (identifier != null) { + return identifier; + } + + return IDENTIFIER.fetchFrom(params); + }, + (s, p) -> s); + + public static final BundlerParamInfo MAC_CF_BUNDLE_VERSION = + new StandardBundlerParam<>( + "mac.CFBundleVersion", + String.class, + p -> { + String s = VERSION.fetchFrom(p); + if (validCFBundleVersion(s)) { + return s; + } else { + return "100"; + } + }, + (s, p) -> s); + + public static final BundlerParamInfo ICON_ICNS = + new StandardBundlerParam<>( + "icon.icns", + File.class, + params -> { + File f = ICON.fetchFrom(params); + if (f != null && !f.getName().toLowerCase().endsWith(".icns")) { + Log.error(MessageFormat.format( + I18N.getString("message.icon-not-icns"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + + public static final StandardBundlerParam SIGN_BUNDLE = + new StandardBundlerParam<>( + Arguments.CLIOptions.MAC_SIGN.getId(), + Boolean.class, + params -> false, + // valueOf(null) is false, we actually do want null in some cases + (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? + null : Boolean.valueOf(s) + ); + + public MacAppImageBuilder(Map params, Path imageOutDir) + throws IOException { + super(params, imageOutDir.resolve(APP_NAME.fetchFrom(params) + + ".app/Contents/runtime/Contents/Home")); + + Objects.requireNonNull(imageOutDir); + + this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params) + ".app"); + this.contentsDir = root.resolve("Contents"); + this.appDir = contentsDir.resolve("app"); + this.javaModsDir = appDir.resolve("mods"); + this.resourcesDir = contentsDir.resolve("Resources"); + this.macOSDir = contentsDir.resolve("MacOS"); + this.runtimeDir = contentsDir.resolve("runtime"); + this.runtimeRoot = runtimeDir.resolve("Contents/Home"); + this.mdir = runtimeRoot.resolve("lib"); + Files.createDirectories(appDir); + Files.createDirectories(resourcesDir); + Files.createDirectories(macOSDir); + Files.createDirectories(runtimeDir); + } + + private void writeEntry(InputStream in, Path dstFile) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.copy(in, dstFile); + } + + public static boolean validCFBundleVersion(String v) { + // CFBundleVersion (String - iOS, OS X) specifies the build version + // number of the bundle, which identifies an iteration (released or + // unreleased) of the bundle. The build version number should be a + // string comprised of three non-negative, period-separated integers + // with the first integer being greater than zero. The string should + // only contain numeric (0-9) and period (.) characters. Leading zeros + // are truncated from each integer and will be ignored (that is, + // 1.02.3 is equivalent to 1.2.3). This key is not localizable. + + if (v == null) { + return false; + } + + String p[] = v.split("\\."); + if (p.length > 3 || p.length < 1) { + Log.verbose(I18N.getString( + "message.version-string-too-many-components")); + return false; + } + + try { + BigInteger n = new BigInteger(p[0]); + if (BigInteger.ONE.compareTo(n) > 0) { + Log.verbose(I18N.getString( + "message.version-string-first-number-not-zero")); + return false; + } + if (p.length > 1) { + n = new BigInteger(p[1]); + if (BigInteger.ZERO.compareTo(n) > 0) { + Log.verbose(I18N.getString( + "message.version-string-no-negative-numbers")); + return false; + } + } + if (p.length > 2) { + n = new BigInteger(p[2]); + if (BigInteger.ZERO.compareTo(n) > 0) { + Log.verbose(I18N.getString( + "message.version-string-no-negative-numbers")); + return false; + } + } + } catch (NumberFormatException ne) { + Log.verbose(I18N.getString("message.version-string-numbers-only")); + Log.verbose(ne); + return false; + } + + return true; + } + + @Override + public Path getAppDir() { + return appDir; + } + + @Override + public Path getAppModsDir() { + return javaModsDir; + } + + @Override + public void prepareApplicationFiles(Map params) + throws IOException { + Map originalParams = new HashMap<>(params); + // Generate PkgInfo + File pkgInfoFile = new File(contentsDir.toFile(), "PkgInfo"); + pkgInfoFile.createNewFile(); + writePkgInfo(pkgInfoFile); + + Path executable = macOSDir.resolve(getLauncherName(params)); + + // create the main app launcher + try (InputStream is_launcher = + getResourceAsStream("jpackageapplauncher"); + InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { + // Copy executable and library to MacOS folder + writeEntry(is_launcher, executable); + writeEntry(is_lib, macOSDir.resolve(LIBRARY_NAME)); + } + executable.toFile().setExecutable(true, false); + // generate main app launcher config file + File cfg = new File(root.toFile(), getLauncherCfgName(params)); + writeCfgFile(params, cfg); + + // create additional app launcher(s) and config file(s) + List> entryPoints = + StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); + for (Map entryPoint : entryPoints) { + Map tmp = + AddLauncherArguments.merge(originalParams, entryPoint); + + // add executable for add launcher + Path addExecutable = macOSDir.resolve(getLauncherName(tmp)); + try (InputStream is = getResourceAsStream("jpackageapplauncher");) { + writeEntry(is, addExecutable); + } + addExecutable.toFile().setExecutable(true, false); + + // add config file for add launcher + cfg = new File(root.toFile(), getLauncherCfgName(tmp)); + writeCfgFile(tmp, cfg); + } + + // Copy class path entries to Java folder + copyClassPathEntries(appDir, params); + + /*********** Take care of "config" files *******/ + + createResource(TEMPLATE_BUNDLE_ICON, params) + .setCategory("icon") + .setExternal(ICON_ICNS.fetchFrom(params)) + .saveToFile(resourcesDir.resolve(APP_NAME.fetchFrom(params) + + ".icns")); + + // copy file association icons + for (Map fa : FILE_ASSOCIATIONS.fetchFrom(params)) { + File f = FA_ICON.fetchFrom(fa); + if (f != null && f.exists()) { + try (InputStream in2 = new FileInputStream(f)) { + Files.copy(in2, resourcesDir.resolve(f.getName())); + } + + } + } + + copyRuntimeFiles(params); + sign(params); + } + + @Override + public void prepareJreFiles(Map params) + throws IOException { + copyRuntimeFiles(params); + sign(params); + } + + @Override + File getRuntimeImageDir(File runtimeImageTop) { + File home = new File(runtimeImageTop, "Contents/Home"); + return (home.exists() ? home : runtimeImageTop); + } + + private void copyRuntimeFiles(Map params) + throws IOException { + // Generate Info.plist + writeInfoPlist(contentsDir.resolve("Info.plist").toFile(), params); + + // generate java runtime info.plist + writeRuntimeInfoPlist( + runtimeDir.resolve("Contents/Info.plist").toFile(), params); + + // copy library + Path runtimeMacOSDir = Files.createDirectories( + runtimeDir.resolve("Contents/MacOS")); + + // JDK 9, 10, and 11 have extra '/jli/' subdir + Path jli = runtimeRoot.resolve("lib/libjli.dylib"); + if (!Files.exists(jli)) { + jli = runtimeRoot.resolve("lib/jli/libjli.dylib"); + } + + Files.copy(jli, runtimeMacOSDir.resolve("libjli.dylib")); + } + + private void sign(Map params) throws IOException { + if (Optional.ofNullable( + SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) { + try { + addNewKeychain(params); + } catch (InterruptedException e) { + Log.error(e.getMessage()); + } + String signingIdentity = + DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(params); + if (signingIdentity != null) { + signAppBundle(params, root, signingIdentity, + BUNDLE_ID_SIGNING_PREFIX.fetchFrom(params), null, null); + } + restoreKeychainList(params); + } + } + + private String getLauncherName(Map params) { + if (APP_NAME.fetchFrom(params) != null) { + return APP_NAME.fetchFrom(params); + } else { + return MAIN_CLASS.fetchFrom(params); + } + } + + public static String getLauncherCfgName( + Map params) { + return "Contents/app/" + APP_NAME.fetchFrom(params) + ".cfg"; + } + + private void copyClassPathEntries(Path javaDirectory, + Map params) throws IOException { + List resourcesList = + APP_RESOURCES_LIST.fetchFrom(params); + if (resourcesList == null) { + throw new RuntimeException( + I18N.getString("message.null-classpath")); + } + + for (RelativeFileSet classPath : resourcesList) { + File srcdir = classPath.getBaseDirectory(); + for (String fname : classPath.getIncludedFiles()) { + copyEntry(javaDirectory, srcdir, fname); + } + } + } + + private String getBundleName(Map params) { + if (MAC_CF_BUNDLE_NAME.fetchFrom(params) != null) { + String bn = MAC_CF_BUNDLE_NAME.fetchFrom(params); + if (bn.length() > 16) { + Log.error(MessageFormat.format(I18N.getString( + "message.bundle-name-too-long-warning"), + MAC_CF_BUNDLE_NAME.getID(), bn)); + } + return MAC_CF_BUNDLE_NAME.fetchFrom(params); + } else if (APP_NAME.fetchFrom(params) != null) { + return APP_NAME.fetchFrom(params); + } else { + String nm = MAIN_CLASS.fetchFrom(params); + if (nm.length() > 16) { + nm = nm.substring(0, 16); + } + return nm; + } + } + + private void writeRuntimeInfoPlist(File file, + Map params) throws IOException { + Map data = new HashMap<>(); + String identifier = StandardBundlerParam.isRuntimeInstaller(params) ? + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) : + "com.oracle.java." + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params); + data.put("CF_BUNDLE_IDENTIFIER", identifier); + String name = StandardBundlerParam.isRuntimeInstaller(params) ? + getBundleName(params): "Java Runtime Image"; + data.put("CF_BUNDLE_NAME", name); + data.put("CF_BUNDLE_VERSION", VERSION.fetchFrom(params)); + data.put("CF_BUNDLE_SHORT_VERSION_STRING", VERSION.fetchFrom(params)); + + createResource(TEMPLATE_RUNTIME_INFO_PLIST, params) + .setPublicName("Runtime-Info.plist") + .setCategory(I18N.getString("resource.runtime-info-plist")) + .setSubstitutionData(data) + .saveToFile(file); + } + + private void writeInfoPlist(File file, Map params) + throws IOException { + Log.verbose(MessageFormat.format(I18N.getString( + "message.preparing-info-plist"), file.getAbsolutePath())); + + //prepare config for exe + //Note: do not need CFBundleDisplayName if we don't support localization + Map data = new HashMap<>(); + data.put("DEPLOY_ICON_FILE", APP_NAME.fetchFrom(params) + ".icns"); + data.put("DEPLOY_BUNDLE_IDENTIFIER", + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params)); + data.put("DEPLOY_BUNDLE_NAME", + getBundleName(params)); + data.put("DEPLOY_BUNDLE_COPYRIGHT", + COPYRIGHT.fetchFrom(params) != null ? + COPYRIGHT.fetchFrom(params) : "Unknown"); + data.put("DEPLOY_LAUNCHER_NAME", getLauncherName(params)); + data.put("DEPLOY_BUNDLE_SHORT_VERSION", + VERSION.fetchFrom(params) != null ? + VERSION.fetchFrom(params) : "1.0.0"); + data.put("DEPLOY_BUNDLE_CFBUNDLE_VERSION", + MAC_CF_BUNDLE_VERSION.fetchFrom(params) != null ? + MAC_CF_BUNDLE_VERSION.fetchFrom(params) : "100"); + + boolean hasMainJar = MAIN_JAR.fetchFrom(params) != null; + boolean hasMainModule = + StandardBundlerParam.MODULE.fetchFrom(params) != null; + + if (hasMainJar) { + data.put("DEPLOY_MAIN_JAR_NAME", MAIN_JAR.fetchFrom(params). + getIncludedFiles().iterator().next()); + } + else if (hasMainModule) { + data.put("DEPLOY_MODULE_NAME", + StandardBundlerParam.MODULE.fetchFrom(params)); + } + + StringBuilder sb = new StringBuilder(); + List jvmOptions = JAVA_OPTIONS.fetchFrom(params); + + String newline = ""; //So we don't add extra line after last append + for (String o : jvmOptions) { + sb.append(newline).append( + " ").append(o).append(""); + newline = "\n"; + } + + data.put("DEPLOY_JAVA_OPTIONS", sb.toString()); + + sb = new StringBuilder(); + List args = ARGUMENTS.fetchFrom(params); + newline = ""; + // So we don't add unneccessary extra line after last append + + for (String o : args) { + sb.append(newline).append(" ").append(o).append( + ""); + newline = "\n"; + } + data.put("DEPLOY_ARGUMENTS", sb.toString()); + + newline = ""; + + data.put("DEPLOY_LAUNCHER_CLASS", MAIN_CLASS.fetchFrom(params)); + + data.put("DEPLOY_APP_CLASSPATH", + getCfgClassPath(CLASSPATH.fetchFrom(params))); + + StringBuilder bundleDocumentTypes = new StringBuilder(); + StringBuilder exportedTypes = new StringBuilder(); + for (Map + fileAssociation : FILE_ASSOCIATIONS.fetchFrom(params)) { + + List extensions = FA_EXTENSIONS.fetchFrom(fileAssociation); + + if (extensions == null) { + Log.verbose(I18N.getString( + "message.creating-association-with-null-extension")); + } + + List mimeTypes = FA_CONTENT_TYPE.fetchFrom(fileAssociation); + String itemContentType = MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) + + "." + ((extensions == null || extensions.isEmpty()) + ? "mime" : extensions.get(0)); + String description = FA_DESCRIPTION.fetchFrom(fileAssociation); + File icon = FA_ICON.fetchFrom(fileAssociation); + + bundleDocumentTypes.append(" \n") + .append(" LSItemContentTypes\n") + .append(" \n") + .append(" ") + .append(itemContentType) + .append("\n") + .append(" \n") + .append("\n") + .append(" CFBundleTypeName\n") + .append(" ") + .append(description) + .append("\n") + .append("\n") + .append(" LSHandlerRank\n") + .append(" Owner\n") + // TODO make a bundler arg + .append("\n") + .append(" CFBundleTypeRole\n") + .append(" Editor\n") + // TODO make a bundler arg + .append("\n") + .append(" LSIsAppleDefaultForType\n") + .append(" \n") + // TODO make a bundler arg + .append("\n"); + + if (icon != null && icon.exists()) { + bundleDocumentTypes + .append(" CFBundleTypeIconFile\n") + .append(" ") + .append(icon.getName()) + .append("\n"); + } + bundleDocumentTypes.append(" \n"); + + exportedTypes.append(" \n") + .append(" UTTypeIdentifier\n") + .append(" ") + .append(itemContentType) + .append("\n") + .append("\n") + .append(" UTTypeDescription\n") + .append(" ") + .append(description) + .append("\n") + .append(" UTTypeConformsTo\n") + .append(" \n") + .append(" public.data\n") + //TODO expose this? + .append(" \n") + .append("\n"); + + if (icon != null && icon.exists()) { + exportedTypes.append(" UTTypeIconFile\n") + .append(" ") + .append(icon.getName()) + .append("\n") + .append("\n"); + } + + exportedTypes.append("\n") + .append(" UTTypeTagSpecification\n") + .append(" \n") + // TODO expose via param? .append( + // " com.apple.ostype\n"); + // TODO expose via param? .append( + // " ABCD\n") + .append("\n"); + + if (extensions != null && !extensions.isEmpty()) { + exportedTypes.append( + " public.filename-extension\n") + .append(" \n"); + + for (String ext : extensions) { + exportedTypes.append(" ") + .append(ext) + .append("\n"); + } + exportedTypes.append(" \n"); + } + if (mimeTypes != null && !mimeTypes.isEmpty()) { + exportedTypes.append(" public.mime-type\n") + .append(" \n"); + + for (String mime : mimeTypes) { + exportedTypes.append(" ") + .append(mime) + .append("\n"); + } + exportedTypes.append(" \n"); + } + exportedTypes.append(" \n") + .append(" \n"); + } + String associationData; + if (bundleDocumentTypes.length() > 0) { + associationData = + "\n CFBundleDocumentTypes\n \n" + + bundleDocumentTypes.toString() + + " \n\n" + + " UTExportedTypeDeclarations\n \n" + + exportedTypes.toString() + + " \n"; + } else { + associationData = ""; + } + data.put("DEPLOY_FILE_ASSOCIATIONS", associationData); + + createResource(TEMPLATE_INFO_PLIST_LITE, params) + .setCategory(I18N.getString("resource.app-info-plist")) + .setSubstitutionData(data) + .setPublicName("Info.plist") + .saveToFile(file); + } + + private void writePkgInfo(File file) throws IOException { + //hardcoded as it does not seem we need to change it ever + String signature = "????"; + + try (Writer out = Files.newBufferedWriter(file.toPath())) { + out.write(OS_TYPE_CODE + signature); + out.flush(); + } + } + + public static void addNewKeychain(Map params) + throws IOException, InterruptedException { + if (Platform.getMajorVersion() < 10 || + (Platform.getMajorVersion() == 10 && + Platform.getMinorVersion() < 12)) { + // we need this for OS X 10.12+ + return; + } + + String keyChain = SIGNING_KEYCHAIN.fetchFrom(params); + if (keyChain == null || keyChain.isEmpty()) { + return; + } + + // get current keychain list + String keyChainPath = new File (keyChain).getAbsolutePath().toString(); + List keychainList = new ArrayList<>(); + int ret = IOUtils.getProcessOutput( + keychainList, "security", "list-keychains"); + if (ret != 0) { + Log.error(I18N.getString("message.keychain.error")); + return; + } + + boolean contains = keychainList.stream().anyMatch( + str -> str.trim().equals("\""+keyChainPath.trim()+"\"")); + if (contains) { + // keychain is already added in the search list + return; + } + + keyChains = new ArrayList<>(); + // remove " + keychainList.forEach((String s) -> { + String path = s.trim(); + if (path.startsWith("\"") && path.endsWith("\"")) { + path = path.substring(1, path.length()-1); + } + keyChains.add(path); + }); + + List args = new ArrayList<>(); + args.add("security"); + args.add("list-keychains"); + args.add("-s"); + + args.addAll(keyChains); + args.add(keyChain); + + ProcessBuilder pb = new ProcessBuilder(args); + IOUtils.exec(pb); + } + + public static void restoreKeychainList(Map params) + throws IOException{ + if (Platform.getMajorVersion() < 10 || + (Platform.getMajorVersion() == 10 && + Platform.getMinorVersion() < 12)) { + // we need this for OS X 10.12+ + return; + } + + if (keyChains == null || keyChains.isEmpty()) { + return; + } + + List args = new ArrayList<>(); + args.add("security"); + args.add("list-keychains"); + args.add("-s"); + + args.addAll(keyChains); + + ProcessBuilder pb = new ProcessBuilder(args); + IOUtils.exec(pb); + } + + public static void signAppBundle( + Map params, Path appLocation, + String signingIdentity, String identifierPrefix, + String entitlementsFile, String inheritedEntitlements) + throws IOException { + AtomicReference toThrow = new AtomicReference<>(); + String appExecutable = "/Contents/MacOS/" + APP_NAME.fetchFrom(params); + String keyChain = SIGNING_KEYCHAIN.fetchFrom(params); + + // sign all dylibs and jars + try (Stream stream = Files.walk(appLocation)) { + stream.peek(path -> { // fix permissions + try { + Set pfp = + Files.getPosixFilePermissions(path); + if (!pfp.contains(PosixFilePermission.OWNER_WRITE)) { + pfp = EnumSet.copyOf(pfp); + pfp.add(PosixFilePermission.OWNER_WRITE); + Files.setPosixFilePermissions(path, pfp); + } + } catch (IOException e) { + Log.verbose(e); + } + }).filter(p -> Files.isRegularFile(p) + && !(p.toString().contains("/Contents/MacOS/libjli.dylib") + || p.toString().endsWith(appExecutable) + || p.toString().contains("/Contents/runtime") + || p.toString().contains("/Contents/Frameworks"))).forEach(p -> { + //noinspection ThrowableResultOfMethodCallIgnored + if (toThrow.get() != null) return; + + // If p is a symlink then skip the signing process. + if (Files.isSymbolicLink(p)) { + if (VERBOSE.fetchFrom(params)) { + Log.verbose(MessageFormat.format(I18N.getString( + "message.ignoring.symlink"), p.toString())); + } + } else { + if (p.toString().endsWith(LIBRARY_NAME)) { + if (isFileSigned(p)) { + return; + } + } + + List args = new ArrayList<>(); + args.addAll(Arrays.asList("codesign", + "-s", signingIdentity, // sign with this key + "--prefix", identifierPrefix, + // use the identifier as a prefix + "-vvvv")); + if (entitlementsFile != null && + (p.toString().endsWith(".jar") + || p.toString().endsWith(".dylib"))) { + args.add("--entitlements"); + args.add(entitlementsFile); // entitlements + } else if (inheritedEntitlements != null && + Files.isExecutable(p)) { + args.add("--entitlements"); + args.add(inheritedEntitlements); + // inherited entitlements for executable processes + } + if (keyChain != null && !keyChain.isEmpty()) { + args.add("--keychain"); + args.add(keyChain); + } + args.add(p.toString()); + + try { + Set oldPermissions = + Files.getPosixFilePermissions(p); + File f = p.toFile(); + f.setWritable(true, true); + + ProcessBuilder pb = new ProcessBuilder(args); + IOUtils.exec(pb); + + Files.setPosixFilePermissions(p, oldPermissions); + } catch (IOException ioe) { + toThrow.set(ioe); + } + } + }); + } + IOException ioe = toThrow.get(); + if (ioe != null) { + throw ioe; + } + + // sign all runtime and frameworks + Consumer signIdentifiedByPList = path -> { + //noinspection ThrowableResultOfMethodCallIgnored + if (toThrow.get() != null) return; + + try { + List args = new ArrayList<>(); + args.addAll(Arrays.asList("codesign", + "-s", signingIdentity, // sign with this key + "--prefix", identifierPrefix, + // use the identifier as a prefix + "-vvvv")); + if (keyChain != null && !keyChain.isEmpty()) { + args.add("--keychain"); + args.add(keyChain); + } + args.add(path.toString()); + ProcessBuilder pb = new ProcessBuilder(args); + IOUtils.exec(pb); + + args = new ArrayList<>(); + args.addAll(Arrays.asList("codesign", + "-s", signingIdentity, // sign with this key + "--prefix", identifierPrefix, + // use the identifier as a prefix + "-vvvv")); + if (keyChain != null && !keyChain.isEmpty()) { + args.add("--keychain"); + args.add(keyChain); + } + args.add(path.toString() + + "/Contents/_CodeSignature/CodeResources"); + pb = new ProcessBuilder(args); + IOUtils.exec(pb); + } catch (IOException e) { + toThrow.set(e); + } + }; + + Path javaPath = appLocation.resolve("Contents/runtime"); + if (Files.isDirectory(javaPath)) { + signIdentifiedByPList.accept(javaPath); + + ioe = toThrow.get(); + if (ioe != null) { + throw ioe; + } + } + Path frameworkPath = appLocation.resolve("Contents/Frameworks"); + if (Files.isDirectory(frameworkPath)) { + Files.list(frameworkPath) + .forEach(signIdentifiedByPList); + + ioe = toThrow.get(); + if (ioe != null) { + throw ioe; + } + } + + // sign the app itself + List args = new ArrayList<>(); + args.addAll(Arrays.asList("codesign", + "-s", signingIdentity, // sign with this key + "-vvvv")); // super verbose output + if (entitlementsFile != null) { + args.add("--entitlements"); + args.add(entitlementsFile); // entitlements + } + if (keyChain != null && !keyChain.isEmpty()) { + args.add("--keychain"); + args.add(keyChain); + } + args.add(appLocation.toString()); + + ProcessBuilder pb = + new ProcessBuilder(args.toArray(new String[args.size()])); + IOUtils.exec(pb); + } + + private static boolean isFileSigned(Path file) { + ProcessBuilder pb = + new ProcessBuilder("codesign", "--verify", file.toString()); + + try { + IOUtils.exec(pb); + } catch (IOException ex) { + return false; + } + + return true; + } + + private static String extractBundleIdentifier(Map params) { + if (PREDEFINED_APP_IMAGE.fetchFrom(params) == null) { + return null; + } + + try { + File infoPList = new File(PREDEFINED_APP_IMAGE.fetchFrom(params) + + File.separator + "Contents" + + File.separator + "Info.plist"); + + DocumentBuilderFactory dbf + = DocumentBuilderFactory.newDefaultInstance(); + dbf.setFeature("http://apache.org/xml/features/" + + "nonvalidating/load-external-dtd", false); + DocumentBuilder b = dbf.newDocumentBuilder(); + org.w3c.dom.Document doc = b.parse(new FileInputStream( + infoPList.getAbsolutePath())); + + XPath xPath = XPathFactory.newInstance().newXPath(); + // Query for the value of element preceding + // element with value equal to CFBundleIdentifier + String v = (String) xPath.evaluate( + "//string[preceding-sibling::key = \"CFBundleIdentifier\"][1]", + doc, XPathConstants.STRING); + + if (v != null && !v.isEmpty()) { + return v; + } + } catch (Exception ex) { + Log.verbose(ex); + } + + return null; + } + +} --- /dev/null 2019-11-18 20:42:32.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/MacAppStoreBundler.java 2019-11-18 20:42:29.077821200 -0500 @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.*; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; +import static jdk.incubator.jpackage.internal.MacAppBundler.*; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; + +public class MacAppStoreBundler extends MacBaseInstallerBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MacResources"); + + private static final String TEMPLATE_BUNDLE_ICON_HIDPI = "java.icns"; + private final static String DEFAULT_ENTITLEMENTS = + "MacAppStore.entitlements"; + private final static String DEFAULT_INHERIT_ENTITLEMENTS = + "MacAppStore_Inherit.entitlements"; + + public static final BundlerParamInfo MAC_APP_STORE_APP_SIGNING_KEY = + new StandardBundlerParam<>( + "mac.signing-key-app", + String.class, + params -> { + String result = MacBaseInstallerBundler.findKey( + "3rd Party Mac Developer Application: " + + SIGNING_KEY_USER.fetchFrom(params), + SIGNING_KEYCHAIN.fetchFrom(params), + VERBOSE.fetchFrom(params)); + if (result != null) { + MacCertificate certificate = new MacCertificate(result); + + if (!certificate.isValid()) { + Log.error(MessageFormat.format( + I18N.getString("error.certificate.expired"), + result)); + } + } + + return result; + }, + (s, p) -> s); + + public static final BundlerParamInfo MAC_APP_STORE_PKG_SIGNING_KEY = + new StandardBundlerParam<>( + "mac.signing-key-pkg", + String.class, + params -> { + String result = MacBaseInstallerBundler.findKey( + "3rd Party Mac Developer Installer: " + + SIGNING_KEY_USER.fetchFrom(params), + SIGNING_KEYCHAIN.fetchFrom(params), + VERBOSE.fetchFrom(params)); + + if (result != null) { + MacCertificate certificate = new MacCertificate(result); + + if (!certificate.isValid()) { + Log.error(MessageFormat.format( + I18N.getString("error.certificate.expired"), + result)); + } + } + + return result; + }, + (s, p) -> s); + + public static final StandardBundlerParam MAC_APP_STORE_ENTITLEMENTS = + new StandardBundlerParam<>( + Arguments.CLIOptions.MAC_APP_STORE_ENTITLEMENTS.getId(), + File.class, + params -> null, + (s, p) -> new File(s)); + + public static final BundlerParamInfo INSTALLER_SUFFIX = + new StandardBundlerParam<> ( + "mac.app-store.installerName.suffix", + String.class, + params -> "-MacAppStore", + (s, p) -> s); + + public File bundle(Map params, + File outdir) throws PackagerException { + Log.verbose(MessageFormat.format(I18N.getString( + "message.building-bundle"), APP_NAME.fetchFrom(params))); + + IOUtils.writableOutputDir(outdir.toPath()); + + // first, load in some overrides + // icns needs @2 versions, so load in the @2 default + params.put(DEFAULT_ICNS_ICON.getID(), TEMPLATE_BUNDLE_ICON_HIDPI); + + // now we create the app + File appImageDir = APP_IMAGE_TEMP_ROOT.fetchFrom(params); + try { + appImageDir.mkdirs(); + + try { + MacAppImageBuilder.addNewKeychain(params); + } catch (InterruptedException e) { + Log.error(e.getMessage()); + } + // first, make sure we don't use the local signing key + params.put(DEVELOPER_ID_APP_SIGNING_KEY.getID(), null); + File appLocation = prepareAppBundle(params); + + prepareEntitlements(params); + + String signingIdentity = + MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(params); + String identifierPrefix = + BUNDLE_ID_SIGNING_PREFIX.fetchFrom(params); + String entitlementsFile = + getConfig_Entitlements(params).toString(); + String inheritEntitlements = + getConfig_Inherit_Entitlements(params).toString(); + + MacAppImageBuilder.signAppBundle(params, appLocation.toPath(), + signingIdentity, identifierPrefix, + entitlementsFile, inheritEntitlements); + MacAppImageBuilder.restoreKeychainList(params); + + ProcessBuilder pb; + + // create the final pkg file + File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(params) + + INSTALLER_SUFFIX.fetchFrom(params) + + ".pkg"); + outdir.mkdirs(); + + String installIdentify = + MAC_APP_STORE_PKG_SIGNING_KEY.fetchFrom(params); + + List buildOptions = new ArrayList<>(); + buildOptions.add("productbuild"); + buildOptions.add("--component"); + buildOptions.add(appLocation.toString()); + buildOptions.add("/Applications"); + buildOptions.add("--sign"); + buildOptions.add(installIdentify); + buildOptions.add("--product"); + buildOptions.add(appLocation + "/Contents/Info.plist"); + String keychainName = SIGNING_KEYCHAIN.fetchFrom(params); + if (keychainName != null && !keychainName.isEmpty()) { + buildOptions.add("--keychain"); + buildOptions.add(keychainName); + } + buildOptions.add(finalPKG.getAbsolutePath()); + + pb = new ProcessBuilder(buildOptions); + + IOUtils.exec(pb); + return finalPKG; + } catch (PackagerException pe) { + throw pe; + } catch (Exception ex) { + Log.verbose(ex); + throw new PackagerException(ex); + } + } + + private File getConfig_Entitlements(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + ".entitlements"); + } + + private File getConfig_Inherit_Entitlements( + Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "_Inherit.entitlements"); + } + + private void prepareEntitlements(Map params) + throws IOException { + createResource(DEFAULT_ENTITLEMENTS, params) + .setCategory( + I18N.getString("resource.mac-app-store-entitlements")) + .setExternal(MAC_APP_STORE_ENTITLEMENTS.fetchFrom(params)) + .saveToFile(getConfig_Entitlements(params)); + + createResource(DEFAULT_INHERIT_ENTITLEMENTS, params) + .setCategory(I18N.getString( + "resource.mac-app-store-inherit-entitlements")) + .saveToFile(getConfig_Entitlements(params)); + } + + /////////////////////////////////////////////////////////////////////// + // Implement Bundler + /////////////////////////////////////////////////////////////////////// + + @Override + public String getName() { + return I18N.getString("store.bundler.name"); + } + + @Override + public String getID() { + return "mac.appStore"; + } + + @Override + public boolean validate(Map params) + throws ConfigException { + try { + Objects.requireNonNull(params); + + // hdiutil is always available so there's no need to test for + // availability. + // run basic validation to ensure requirements are met + + // we are not interested in return code, only possible exception + validateAppImageAndBundeler(params); + + // reject explicitly set to not sign + if (!Optional.ofNullable(MacAppImageBuilder. + SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) { + throw new ConfigException( + I18N.getString("error.must-sign-app-store"), + I18N.getString("error.must-sign-app-store.advice")); + } + + // make sure we have settings for signatures + if (MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(params) == null) { + throw new ConfigException( + I18N.getString("error.no-app-signing-key"), + I18N.getString("error.no-app-signing-key.advice")); + } + if (MAC_APP_STORE_PKG_SIGNING_KEY.fetchFrom(params) == null) { + throw new ConfigException( + I18N.getString("error.no-pkg-signing-key"), + I18N.getString("error.no-pkg-signing-key.advice")); + } + + // things we could check... + // check the icons, make sure it has hidpi icons + // check the category, + // make sure it fits in the list apple has provided + // validate bundle identifier is reverse dns + // check for \a+\.\a+\.. + + return true; + } catch (RuntimeException re) { + if (re.getCause() instanceof ConfigException) { + throw (ConfigException) re.getCause(); + } else { + throw new ConfigException(re); + } + } + } + + @Override + public File execute(Map params, + File outputParentDir) throws PackagerException { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported(boolean runtimeInstaller) { + // return (!runtimeInstaller && + // Platform.getPlatform() == Platform.MAC); + return false; // mac-app-store not yet supported + } + + @Override + public boolean isDefault() { + return false; + } + +} --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java 2019-11-18 20:42:43.763988400 -0500 +++ /dev/null 2019-11-18 20:42:45.000000000 -0500 @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.PrintStream; -import java.nio.file.Files; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static jdk.jpackage.internal.StandardBundlerParam.*; - -public abstract class MacBaseInstallerBundler extends AbstractBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MacResources"); - - // This could be generalized more to be for any type of Image Bundler - public static final BundlerParamInfo APP_BUNDLER = - new StandardBundlerParam<>( - "mac.app.bundler", - MacAppBundler.class, - params -> new MacAppBundler(), - (s, p) -> null); - - public final BundlerParamInfo APP_IMAGE_TEMP_ROOT = - new StandardBundlerParam<>( - "mac.app.imageRoot", - File.class, - params -> { - File imageDir = IMAGES_ROOT.fetchFrom(params); - if (!imageDir.exists()) imageDir.mkdirs(); - try { - return Files.createTempDirectory( - imageDir.toPath(), "image-").toFile(); - } catch (IOException e) { - return new File(imageDir, getID()+ ".image"); - } - }, - (s, p) -> new File(s)); - - public static final BundlerParamInfo SIGNING_KEY_USER = - new StandardBundlerParam<>( - Arguments.CLIOptions.MAC_SIGNING_KEY_NAME.getId(), - String.class, - params -> "", - null); - - public static final BundlerParamInfo SIGNING_KEYCHAIN = - new StandardBundlerParam<>( - Arguments.CLIOptions.MAC_SIGNING_KEYCHAIN.getId(), - String.class, - params -> "", - null); - - public static final BundlerParamInfo INSTALLER_NAME = - new StandardBundlerParam<> ( - "mac.installerName", - String.class, - params -> { - String nm = APP_NAME.fetchFrom(params); - if (nm == null) return null; - - String version = VERSION.fetchFrom(params); - if (version == null) { - return nm; - } else { - return nm + "-" + version; - } - }, - (s, p) -> s); - - protected void validateAppImageAndBundeler( - Map params) - throws ConfigException, UnsupportedPlatformException { - if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) { - File applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params); - if (!applicationImage.exists()) { - throw new ConfigException( - MessageFormat.format(I18N.getString( - "message.app-image-dir-does-not-exist"), - PREDEFINED_APP_IMAGE.getID(), - applicationImage.toString()), - MessageFormat.format(I18N.getString( - "message.app-image-dir-does-not-exist.advice"), - PREDEFINED_APP_IMAGE.getID())); - } - if (APP_NAME.fetchFrom(params) == null) { - throw new ConfigException( - I18N.getString("message.app-image-requires-app-name"), - I18N.getString( - "message.app-image-requires-app-name.advice")); - } - if (IDENTIFIER.fetchFrom(params) == null) { - throw new ConfigException( - I18N.getString("message.app-image-requires-identifier"), - I18N.getString( - "message.app-image-requires-identifier.advice")); - } - } else { - APP_BUNDLER.fetchFrom(params).validate(params); - } - } - - protected File prepareAppBundle(Map p, - boolean pkg) throws PackagerException { - File predefinedImage = StandardBundlerParam.getPredefinedAppImage(p); - if (predefinedImage != null) { - return predefinedImage; - } - File appImageRoot = APP_IMAGE_TEMP_ROOT.fetchFrom(p); - if (pkg) { - // create pkg in dmg - return new MacPkgBundler().bundle(p, appImageRoot); - } else { - return APP_BUNDLER.fetchFrom(p).doBundle(p, appImageRoot, true); - } - } - - @Override - public Collection> getBundleParameters() { - Collection> results = new LinkedHashSet<>(); - - results.addAll(MacAppBundler.getAppBundleParameters()); - results.addAll(Arrays.asList( - APP_BUNDLER, - CONFIG_ROOT, - APP_IMAGE_TEMP_ROOT, - PREDEFINED_APP_IMAGE - )); - - return results; - } - - @Override - public String getBundleType() { - return "INSTALLER"; - } - - public static String findKey(String key, String keychainName, - boolean verbose) { - if (Platform.getPlatform() != Platform.MAC) { - return null; - } - - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintStream ps = new PrintStream(baos)) { - List searchOptions = new ArrayList<>(); - searchOptions.add("security"); - searchOptions.add("find-certificate"); - searchOptions.add("-c"); - searchOptions.add(key); - searchOptions.add("-a"); - if (keychainName != null && !keychainName.isEmpty()) { - searchOptions.add(keychainName); - } - - ProcessBuilder pb = new ProcessBuilder(searchOptions); - - IOUtils.exec(pb, verbose, false, ps); - Pattern p = Pattern.compile("\"alis\"=\"([^\"]+)\""); - Matcher m = p.matcher(baos.toString()); - if (!m.find()) { - Log.error("Did not find a key matching '" + key + "'"); - return null; - } - String matchedKey = m.group(1); - if (m.find()) { - Log.error("Found more than one key matching '" + key + "'"); - return null; - } - Log.debug("Using key '" + matchedKey + "'"); - return matchedKey; - } catch (IOException ioe) { - Log.verbose(ioe); - return null; - } - } -} --- /dev/null 2019-11-18 20:42:45.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/MacBaseInstallerBundler.java 2019-11-18 20:42:40.211028800 -0500 @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.file.Files; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +public abstract class MacBaseInstallerBundler extends AbstractBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MacResources"); + + // This could be generalized more to be for any type of Image Bundler + public static final BundlerParamInfo APP_BUNDLER = + new StandardBundlerParam<>( + "mac.app.bundler", + MacAppBundler.class, + params -> new MacAppBundler(), + (s, p) -> null); + + public final BundlerParamInfo APP_IMAGE_TEMP_ROOT = + new StandardBundlerParam<>( + "mac.app.imageRoot", + File.class, + params -> { + File imageDir = IMAGES_ROOT.fetchFrom(params); + if (!imageDir.exists()) imageDir.mkdirs(); + try { + return Files.createTempDirectory( + imageDir.toPath(), "image-").toFile(); + } catch (IOException e) { + return new File(imageDir, getID()+ ".image"); + } + }, + (s, p) -> new File(s)); + + public static final BundlerParamInfo SIGNING_KEY_USER = + new StandardBundlerParam<>( + Arguments.CLIOptions.MAC_SIGNING_KEY_NAME.getId(), + String.class, + params -> "", + null); + + public static final BundlerParamInfo SIGNING_KEYCHAIN = + new StandardBundlerParam<>( + Arguments.CLIOptions.MAC_SIGNING_KEYCHAIN.getId(), + String.class, + params -> "", + null); + + public static final BundlerParamInfo INSTALLER_NAME = + new StandardBundlerParam<> ( + "mac.installerName", + String.class, + params -> { + String nm = APP_NAME.fetchFrom(params); + if (nm == null) return null; + + String version = VERSION.fetchFrom(params); + if (version == null) { + return nm; + } else { + return nm + "-" + version; + } + }, + (s, p) -> s); + + protected void validateAppImageAndBundeler( + Map params) throws ConfigException { + if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) { + File applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params); + if (!applicationImage.exists()) { + throw new ConfigException( + MessageFormat.format(I18N.getString( + "message.app-image-dir-does-not-exist"), + PREDEFINED_APP_IMAGE.getID(), + applicationImage.toString()), + MessageFormat.format(I18N.getString( + "message.app-image-dir-does-not-exist.advice"), + PREDEFINED_APP_IMAGE.getID())); + } + if (APP_NAME.fetchFrom(params) == null) { + throw new ConfigException( + I18N.getString("message.app-image-requires-app-name"), + I18N.getString( + "message.app-image-requires-app-name.advice")); + } + } else { + APP_BUNDLER.fetchFrom(params).validate(params); + } + } + + protected File prepareAppBundle(Map params) + throws PackagerException { + File predefinedImage = + StandardBundlerParam.getPredefinedAppImage(params); + if (predefinedImage != null) { + return predefinedImage; + } + File appImageRoot = APP_IMAGE_TEMP_ROOT.fetchFrom(params); + + return APP_BUNDLER.fetchFrom(params).doBundle( + params, appImageRoot, true); + } + + @Override + public String getBundleType() { + return "INSTALLER"; + } + + public static String findKey(String key, String keychainName, + boolean verbose) { + if (Platform.getPlatform() != Platform.MAC) { + return null; + } + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos)) { + List searchOptions = new ArrayList<>(); + searchOptions.add("security"); + searchOptions.add("find-certificate"); + searchOptions.add("-c"); + searchOptions.add(key); + searchOptions.add("-a"); + if (keychainName != null && !keychainName.isEmpty()) { + searchOptions.add(keychainName); + } + + ProcessBuilder pb = new ProcessBuilder(searchOptions); + + IOUtils.exec(pb, false, ps); + Pattern p = Pattern.compile("\"alis\"=\"([^\"]+)\""); + Matcher m = p.matcher(baos.toString()); + if (!m.find()) { + Log.error("Did not find a key matching '" + key + "'"); + return null; + } + String matchedKey = m.group(1); + if (m.find()) { + Log.error("Found more than one key matching '" + key + "'"); + return null; + } + Log.verbose("Using key '" + matchedKey + "'"); + return matchedKey; + } catch (IOException ioe) { + Log.verbose(ioe); + return null; + } + } +} --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificate.java 2019-11-18 20:43:05.646363200 -0500 +++ /dev/null 2019-11-18 20:43:07.000000000 -0500 @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Locale; - -final class MacCertificate { - private final String certificate; - private final boolean verbose; - - MacCertificate(String certificate) { - this.certificate = certificate; - this.verbose = false; - } - - MacCertificate(String certificate, boolean verbose) { - this.certificate = certificate; - this.verbose = verbose; - } - - boolean isValid() { - return verifyCertificate(this.certificate, verbose); - } - - private static File findCertificate(String certificate, boolean verbose) { - File result = null; - - List args = new ArrayList<>(); - args.add("security"); - args.add("find-certificate"); - args.add("-c"); - args.add(certificate); - args.add("-a"); - args.add("-p"); - - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintStream ps = new PrintStream(baos)) { - ProcessBuilder security = new ProcessBuilder(args); - IOUtils.exec(security, verbose, false, ps); - - File output = File.createTempFile("tempfile", ".tmp"); - PrintStream p = new PrintStream( - new BufferedOutputStream( - new FileOutputStream(output, true))); - BufferedReader bfReader = new BufferedReader( - new InputStreamReader( - new ByteArrayInputStream(baos.toByteArray()))); - String line = null; - - while((line = bfReader.readLine()) != null){ - p.println(line); - } - - p.close(); - result = output; - } - catch (IOException ignored) {} - - return result; - } - - private static Date findCertificateDate(String filename, boolean verbose) { - Date result = null; - - List args = new ArrayList<>(); - args.add("/usr/bin/openssl"); - args.add("x509"); - args.add("-noout"); - args.add("-enddate"); - args.add("-in"); - args.add(filename); - - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintStream ps = new PrintStream(baos)) { - ProcessBuilder security = new ProcessBuilder(args); - IOUtils.exec(security, verbose, false, ps); - String output = baos.toString(); - output = output.substring(output.indexOf("=") + 1); - DateFormat df = new SimpleDateFormat( - "MMM dd kk:mm:ss yyyy z", Locale.ENGLISH); - result = df.parse(output); - } catch (IOException | ParseException ex) { - Log.debug(ex); - } - - return result; - } - - private static boolean verifyCertificate( - String certificate, boolean verbose) { - boolean result = false; - - try { - File file = null; - Date certificateDate = null; - - try { - file = findCertificate(certificate, verbose); - - if (file != null) { - certificateDate = findCertificateDate( - file.getCanonicalPath(), verbose); - } - } - finally { - if (file != null) { - file.delete(); - } - } - - if (certificateDate != null) { - Calendar c = Calendar.getInstance(); - Date today = c.getTime(); - - if (certificateDate.after(today)) { - result = true; - } - } - } - catch (IOException ignored) {} - - return result; - } -} --- /dev/null 2019-11-18 20:43:07.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/MacCertificate.java 2019-11-18 20:43:02.082486200 -0500 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.file.StandardCopyOption; +import java.nio.file.Files; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +public final class MacCertificate { + private final String certificate; + + public MacCertificate(String certificate) { + this.certificate = certificate; + } + + public boolean isValid() { + return verifyCertificate(this.certificate); + } + + private static File findCertificate(String certificate) { + File result = null; + + List args = new ArrayList<>(); + args.add("security"); + args.add("find-certificate"); + args.add("-c"); + args.add(certificate); + args.add("-a"); + args.add("-p"); + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos)) { + ProcessBuilder security = new ProcessBuilder(args); + IOUtils.exec(security, false, ps); + + File output = File.createTempFile("tempfile", ".tmp"); + + Files.copy(new ByteArrayInputStream(baos.toByteArray()), + output.toPath(), StandardCopyOption.REPLACE_EXISTING); + + result = output; + } + catch (IOException ignored) {} + + return result; + } + + private static Date findCertificateDate(String filename) { + Date result = null; + + List args = new ArrayList<>(); + args.add("/usr/bin/openssl"); + args.add("x509"); + args.add("-noout"); + args.add("-enddate"); + args.add("-in"); + args.add(filename); + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos)) { + ProcessBuilder security = new ProcessBuilder(args); + IOUtils.exec(security, false, ps); + String output = baos.toString(); + output = output.substring(output.indexOf("=") + 1); + DateFormat df = new SimpleDateFormat( + "MMM dd kk:mm:ss yyyy z", Locale.ENGLISH); + result = df.parse(output); + } catch (IOException | ParseException ex) { + Log.verbose(ex); + } + + return result; + } + + private static boolean verifyCertificate(String certificate) { + boolean result = false; + + try { + File file = null; + Date certificateDate = null; + + try { + file = findCertificate(certificate); + + if (file != null) { + certificateDate = findCertificateDate( + file.getCanonicalPath()); + } + } + finally { + if (file != null) { + file.delete(); + } + } + + if (certificateDate != null) { + Calendar c = Calendar.getInstance(); + Date today = c.getTime(); + + if (certificateDate.after(today)) { + result = true; + } + } + } + catch (IOException ignored) {} + + return result; + } +} --- /dev/null 2019-11-18 20:43:27.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/MacDmgBundler.java 2019-11-18 20:43:23.852145700 -0500 @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.*; +import java.nio.file.Files; +import java.text.MessageFormat; +import java.util.*; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +public class MacDmgBundler extends MacBaseInstallerBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MacResources"); + + static final String DEFAULT_BACKGROUND_IMAGE="background_dmg.png"; + static final String DEFAULT_DMG_SETUP_SCRIPT="DMGsetup.scpt"; + static final String TEMPLATE_BUNDLE_ICON = "java.icns"; + + static final String DEFAULT_LICENSE_PLIST="lic_template.plist"; + + public static final BundlerParamInfo INSTALLER_SUFFIX = + new StandardBundlerParam<> ( + "mac.dmg.installerName.suffix", + String.class, + params -> "", + (s, p) -> s); + + public File bundle(Map params, + File outdir) throws PackagerException { + Log.verbose(MessageFormat.format(I18N.getString("message.building-dmg"), + APP_NAME.fetchFrom(params))); + + IOUtils.writableOutputDir(outdir.toPath()); + + File appImageDir = APP_IMAGE_TEMP_ROOT.fetchFrom(params); + try { + appImageDir.mkdirs(); + + if (prepareAppBundle(params) != null && + prepareConfigFiles(params)) { + File configScript = getConfig_Script(params); + if (configScript.exists()) { + Log.verbose(MessageFormat.format( + I18N.getString("message.running-script"), + configScript.getAbsolutePath())); + IOUtils.run("bash", configScript); + } + + return buildDMG(params, outdir); + } + return null; + } catch (IOException ex) { + Log.verbose(ex); + throw new PackagerException(ex); + } + } + + private static final String hdiutil = "/usr/bin/hdiutil"; + + private void prepareDMGSetupScript(String volumeName, + Map params) throws IOException { + File dmgSetup = getConfig_VolumeScript(params); + Log.verbose(MessageFormat.format( + I18N.getString("message.preparing-dmg-setup"), + dmgSetup.getAbsolutePath())); + + //prepare config for exe + Map data = new HashMap<>(); + data.put("DEPLOY_ACTUAL_VOLUME_NAME", volumeName); + data.put("DEPLOY_APPLICATION_NAME", APP_NAME.fetchFrom(params)); + + data.put("DEPLOY_INSTALL_LOCATION", "(path to applications folder)"); + data.put("DEPLOY_INSTALL_NAME", "Applications"); + + createResource(DEFAULT_DMG_SETUP_SCRIPT, params) + .setCategory(I18N.getString("resource.dmg-setup-script")) + .setSubstitutionData(data) + .saveToFile(dmgSetup); + } + + private File getConfig_VolumeScript(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-dmg-setup.scpt"); + } + + private File getConfig_VolumeBackground( + Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-background.png"); + } + + private File getConfig_VolumeIcon(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-volume.icns"); + } + + private File getConfig_LicenseFile(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-license.plist"); + } + + private void prepareLicense(Map params) { + try { + String licFileStr = LICENSE_FILE.fetchFrom(params); + if (licFileStr == null) { + return; + } + + File licFile = new File(licFileStr); + byte[] licenseContentOriginal = + Files.readAllBytes(licFile.toPath()); + String licenseInBase64 = + Base64.getEncoder().encodeToString(licenseContentOriginal); + + Map data = new HashMap<>(); + data.put("APPLICATION_LICENSE_TEXT", licenseInBase64); + + createResource(DEFAULT_LICENSE_PLIST, params) + .setCategory(I18N.getString("resource.license-setup")) + .setSubstitutionData(data) + .saveToFile(getConfig_LicenseFile(params)); + + } catch (IOException ex) { + Log.verbose(ex); + } + } + + private boolean prepareConfigFiles(Map params) + throws IOException { + + createResource(DEFAULT_BACKGROUND_IMAGE, params) + .setCategory(I18N.getString("resource.dmg-background")) + .saveToFile(getConfig_VolumeBackground(params)); + + createResource(TEMPLATE_BUNDLE_ICON, params) + .setCategory(I18N.getString("resource.volume-icon")) + .setExternal(MacAppBundler.ICON_ICNS.fetchFrom(params)) + .saveToFile(getConfig_VolumeIcon(params)); + + createResource(null, params) + .setCategory(I18N.getString("resource.post-install-script")) + .saveToFile(getConfig_Script(params)); + + prepareLicense(params); + + // In theory we need to extract name from results of attach command + // However, this will be a problem for customization as name will + // possibly change every time and developer will not be able to fix it + // As we are using tmp dir chance we get "different" name are low => + // Use fixed name we used for bundle + prepareDMGSetupScript(APP_NAME.fetchFrom(params), params); + + return true; + } + + // name of post-image script + private File getConfig_Script(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-post-image.sh"); + } + + // Location of SetFile utility may be different depending on MacOS version + // We look for several known places and if none of them work will + // try ot find it + private String findSetFileUtility() { + String typicalPaths[] = {"/Developer/Tools/SetFile", + "/usr/bin/SetFile", "/Developer/usr/bin/SetFile"}; + + String setFilePath = null; + for (String path: typicalPaths) { + File f = new File(path); + if (f.exists() && f.canExecute()) { + setFilePath = path; + break; + } + } + + // Validate SetFile, if Xcode is not installed it will run, but exit with error + // code + if (setFilePath != null) { + try { + ProcessBuilder pb = new ProcessBuilder(setFilePath, "-h"); + Process p = pb.start(); + int code = p.waitFor(); + if (code == 0) { + return setFilePath; + } + } catch (Exception ignored) {} + + // No need for generic find attempt. We found it, but it does not work. + // Probably due to missing xcode. + return null; + } + + // generic find attempt + try { + ProcessBuilder pb = new ProcessBuilder("xcrun", "-find", "SetFile"); + Process p = pb.start(); + InputStreamReader isr = new InputStreamReader(p.getInputStream()); + BufferedReader br = new BufferedReader(isr); + String lineRead = br.readLine(); + if (lineRead != null) { + File f = new File(lineRead); + if (f.exists() && f.canExecute()) { + return f.getAbsolutePath(); + } + } + } catch (IOException ignored) {} + + return null; + } + + private File buildDMG( + Map params, File outdir) + throws IOException { + File imagesRoot = IMAGES_ROOT.fetchFrom(params); + if (!imagesRoot.exists()) imagesRoot.mkdirs(); + + File protoDMG = new File(imagesRoot, + APP_NAME.fetchFrom(params) +"-tmp.dmg"); + File finalDMG = new File(outdir, INSTALLER_NAME.fetchFrom(params) + + INSTALLER_SUFFIX.fetchFrom(params) + ".dmg"); + + File srcFolder = APP_IMAGE_TEMP_ROOT.fetchFrom(params); + File predefinedImage = + StandardBundlerParam.getPredefinedAppImage(params); + if (predefinedImage != null) { + srcFolder = predefinedImage; + } + + Log.verbose(MessageFormat.format(I18N.getString( + "message.creating-dmg-file"), finalDMG.getAbsolutePath())); + + protoDMG.delete(); + if (finalDMG.exists() && !finalDMG.delete()) { + throw new IOException(MessageFormat.format(I18N.getString( + "message.dmg-cannot-be-overwritten"), + finalDMG.getAbsolutePath())); + } + + protoDMG.getParentFile().mkdirs(); + finalDMG.getParentFile().mkdirs(); + + String hdiUtilVerbosityFlag = VERBOSE.fetchFrom(params) ? + "-verbose" : "-quiet"; + + // create temp image + ProcessBuilder pb = new ProcessBuilder( + hdiutil, + "create", + hdiUtilVerbosityFlag, + "-srcfolder", srcFolder.getAbsolutePath(), + "-volname", APP_NAME.fetchFrom(params), + "-ov", protoDMG.getAbsolutePath(), + "-fs", "HFS+", + "-format", "UDRW"); + IOUtils.exec(pb); + + // mount temp image + pb = new ProcessBuilder( + hdiutil, + "attach", + protoDMG.getAbsolutePath(), + hdiUtilVerbosityFlag, + "-mountroot", imagesRoot.getAbsolutePath()); + IOUtils.exec(pb); + + File mountedRoot = new File(imagesRoot.getAbsolutePath(), + APP_NAME.fetchFrom(params)); + + try { + // volume icon + File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns"); + IOUtils.copyFile(getConfig_VolumeIcon(params), + volumeIconFile); + + // background image + File bgdir = new File(mountedRoot, ".background"); + bgdir.mkdirs(); + IOUtils.copyFile(getConfig_VolumeBackground(params), + new File(bgdir, "background.png")); + + // Indicate that we want a custom icon + // NB: attributes of the root directory are ignored + // when creating the volume + // Therefore we have to do this after we mount image + String setFileUtility = findSetFileUtility(); + if (setFileUtility != null) { + //can not find utility => keep going without icon + try { + volumeIconFile.setWritable(true); + // The "creator" attribute on a file is a legacy attribute + // but it seems Finder excepts these bytes to be + // "icnC" for the volume icon + // (might not work on Mac 10.13 with old XCode) + pb = new ProcessBuilder( + setFileUtility, + "-c", "icnC", + volumeIconFile.getAbsolutePath()); + IOUtils.exec(pb); + volumeIconFile.setReadOnly(); + + pb = new ProcessBuilder( + setFileUtility, + "-a", "C", + mountedRoot.getAbsolutePath()); + IOUtils.exec(pb); + } catch (IOException ex) { + Log.error(ex.getMessage()); + Log.verbose("Cannot enable custom icon using SetFile utility"); + } + } else { + Log.verbose(I18N.getString("message.setfile.dmg")); + } + + // We will not consider setting background image and creating link to + // /Application folder in DMG as critical error, since it can fail in + // headless enviroment. + try { + pb = new ProcessBuilder("osascript", + getConfig_VolumeScript(params).getAbsolutePath()); + IOUtils.exec(pb); + } catch (IOException ex) { + Log.verbose(ex); + } + } finally { + // Detach the temporary image + pb = new ProcessBuilder( + hdiutil, + "detach", + "-force", + hdiUtilVerbosityFlag, + mountedRoot.getAbsolutePath()); + IOUtils.exec(pb); + } + + // Compress it to a new image + pb = new ProcessBuilder( + hdiutil, + "convert", + protoDMG.getAbsolutePath(), + hdiUtilVerbosityFlag, + "-format", "UDZO", + "-o", finalDMG.getAbsolutePath()); + IOUtils.exec(pb); + + //add license if needed + if (getConfig_LicenseFile(params).exists()) { + //hdiutil unflatten your_image_file.dmg + pb = new ProcessBuilder( + hdiutil, + "unflatten", + finalDMG.getAbsolutePath() + ); + IOUtils.exec(pb); + + //add license + pb = new ProcessBuilder( + hdiutil, + "udifrez", + finalDMG.getAbsolutePath(), + "-xml", + getConfig_LicenseFile(params).getAbsolutePath() + ); + IOUtils.exec(pb); + + //hdiutil flatten your_image_file.dmg + pb = new ProcessBuilder( + hdiutil, + "flatten", + finalDMG.getAbsolutePath() + ); + IOUtils.exec(pb); + + } + + //Delete the temporary image + protoDMG.delete(); + + Log.verbose(MessageFormat.format(I18N.getString( + "message.output-to-location"), + APP_NAME.fetchFrom(params), finalDMG.getAbsolutePath())); + + return finalDMG; + } + + + ////////////////////////////////////////////////////////////////////////// + // Implement Bundler + ////////////////////////////////////////////////////////////////////////// + + @Override + public String getName() { + return I18N.getString("dmg.bundler.name"); + } + + @Override + public String getID() { + return "dmg"; + } + + @Override + public boolean validate(Map params) + throws ConfigException { + try { + Objects.requireNonNull(params); + + //run basic validation to ensure requirements are met + //we are not interested in return code, only possible exception + validateAppImageAndBundeler(params); + + return true; + } catch (RuntimeException re) { + if (re.getCause() instanceof ConfigException) { + throw (ConfigException) re.getCause(); + } else { + throw new ConfigException(re); + } + } + } + + @Override + public File execute(Map params, + File outputParentDir) throws PackagerException { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported(boolean runtimeInstaller) { + return isSupported(); + } + + public final static String[] required = + {"/usr/bin/hdiutil", "/usr/bin/osascript"}; + public static boolean isSupported() { + try { + for (String s : required) { + File f = new File(s); + if (!f.exists() || !f.canExecute()) { + return false; + } + } + return true; + } catch (Exception e) { + return false; + } + } + + @Override + public boolean isDefault() { + return true; + } + +} --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java 2019-11-18 20:43:37.929845100 -0500 +++ /dev/null 2019-11-18 20:43:39.000000000 -0500 @@ -1,579 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.io.Writer; -import java.net.URLEncoder; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; - -import static jdk.jpackage.internal.StandardBundlerParam.*; -import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEYCHAIN; -import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEY_USER; - -public class MacPkgBundler extends MacBaseInstallerBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MacResources"); - - private static final String DEFAULT_BACKGROUND_IMAGE = "background_pkg.png"; - - private static final String TEMPLATE_PREINSTALL_SCRIPT = - "preinstall.template"; - private static final String TEMPLATE_POSTINSTALL_SCRIPT = - "postinstall.template"; - - private static final BundlerParamInfo PACKAGES_ROOT = - new StandardBundlerParam<>( - "mac.pkg.packagesRoot", - File.class, - params -> { - File packagesRoot = - new File(TEMP_ROOT.fetchFrom(params), "packages"); - packagesRoot.mkdirs(); - return packagesRoot; - }, - (s, p) -> new File(s)); - - - protected final BundlerParamInfo SCRIPTS_DIR = - new StandardBundlerParam<>( - "mac.pkg.scriptsDir", - File.class, - params -> { - File scriptsDir = - new File(CONFIG_ROOT.fetchFrom(params), "scripts"); - scriptsDir.mkdirs(); - return scriptsDir; - }, - (s, p) -> new File(s)); - - public static final - BundlerParamInfo DEVELOPER_ID_INSTALLER_SIGNING_KEY = - new StandardBundlerParam<>( - "mac.signing-key-developer-id-installer", - String.class, - params -> { - String result = MacBaseInstallerBundler.findKey( - "Developer ID Installer: " - + SIGNING_KEY_USER.fetchFrom(params), - SIGNING_KEYCHAIN.fetchFrom(params), - VERBOSE.fetchFrom(params)); - if (result != null) { - MacCertificate certificate = new MacCertificate( - result, VERBOSE.fetchFrom(params)); - - if (!certificate.isValid()) { - Log.error(MessageFormat.format( - I18N.getString("error.certificate.expired"), - result)); - } - } - - return result; - }, - (s, p) -> s); - - public static final BundlerParamInfo MAC_INSTALL_DIR = - new StandardBundlerParam<>( - "mac-install-dir", - String.class, - params -> { - String dir = INSTALL_DIR.fetchFrom(params); - return (dir != null) ? dir : "/Applications"; - }, - (s, p) -> s - ); - - public static final BundlerParamInfo INSTALLER_SUFFIX = - new StandardBundlerParam<> ( - "mac.pkg.installerName.suffix", - String.class, - params -> "", - (s, p) -> s); - - public File bundle(Map params, - File outdir) throws PackagerException { - Log.verbose(MessageFormat.format(I18N.getString("message.building-pkg"), - APP_NAME.fetchFrom(params))); - if (!outdir.isDirectory() && !outdir.mkdirs()) { - throw new PackagerException( - "error.cannot-create-output-dir", - outdir.getAbsolutePath()); - } - if (!outdir.canWrite()) { - throw new PackagerException( - "error.cannot-write-to-output-dir", - outdir.getAbsolutePath()); - } - - File appImageDir = null; - try { - appImageDir = prepareAppBundle(params, false); - - if (appImageDir != null && prepareConfigFiles(params)) { - - File configScript = getConfig_Script(params); - if (configScript.exists()) { - Log.verbose(MessageFormat.format(I18N.getString( - "message.running-script"), - configScript.getAbsolutePath())); - IOUtils.run("bash", configScript, false); - } - - return createPKG(params, outdir, appImageDir); - } - return null; - } catch (IOException ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - private File getPackages_AppPackage(Map params) { - return new File(PACKAGES_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-app.pkg"); - } - - private File getPackages_DaemonPackage(Map params) { - return new File(PACKAGES_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-daemon.pkg"); - } - - private File getConfig_DistributionXMLFile( - Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), "distribution.dist"); - } - - private File getConfig_BackgroundImage(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-background.png"); - } - - private File getScripts_PreinstallFile(Map params) { - return new File(SCRIPTS_DIR.fetchFrom(params), "preinstall"); - } - - private File getScripts_PostinstallFile( - Map params) { - return new File(SCRIPTS_DIR.fetchFrom(params), "postinstall"); - } - - private String getAppIdentifier(Map params) { - return IDENTIFIER.fetchFrom(params); - } - - private String getDaemonIdentifier(Map params) { - return IDENTIFIER.fetchFrom(params) + ".daemon"; - } - - private void preparePackageScripts(Map params) - throws IOException { - Log.verbose(I18N.getString("message.preparing-scripts")); - - Map data = new HashMap<>(); - - data.put("INSTALL_LOCATION", MAC_INSTALL_DIR.fetchFrom(params)); - - Writer w = new BufferedWriter( - new FileWriter(getScripts_PreinstallFile(params))); - String content = preprocessTextResource( - getScripts_PreinstallFile(params).getName(), - I18N.getString("resource.pkg-preinstall-script"), - TEMPLATE_PREINSTALL_SCRIPT, - data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - getScripts_PreinstallFile(params).setExecutable(true, false); - - w = new BufferedWriter( - new FileWriter(getScripts_PostinstallFile(params))); - content = preprocessTextResource( - getScripts_PostinstallFile(params).getName(), - I18N.getString("resource.pkg-postinstall-script"), - TEMPLATE_POSTINSTALL_SCRIPT, - data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - getScripts_PostinstallFile(params).setExecutable(true, false); - } - - private void prepareDistributionXMLFile(Map params) - throws IOException { - File f = getConfig_DistributionXMLFile(params); - - Log.verbose(MessageFormat.format(I18N.getString( - "message.preparing-distribution-dist"), f.getAbsolutePath())); - - PrintStream out = new PrintStream(f); - - out.println( - ""); - out.println(""); - - out.println("" + APP_NAME.fetchFrom(params) + ""); - out.println(""); - - String licFileStr = LICENSE_FILE.fetchFrom(params); - if (licFileStr != null) { - File licFile = new File(licFileStr); - out.println(""); - } - - /* - * Note that the content of the distribution file - * below is generated by productbuild --synthesize - */ - - String appId = getAppIdentifier(params); - - out.println(""); - - out.println(""); - out.println(""); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(""); - out.println(""); - out.println(""); - out.println(" "); - out.println(""); - out.println("" - + URLEncoder.encode(getPackages_AppPackage(params).getName(), - "UTF-8") + ""); - - out.println(""); - - out.close(); - } - - private boolean prepareConfigFiles(Map params) - throws IOException { - File imageTarget = getConfig_BackgroundImage(params); - fetchResource(imageTarget.getName(), - I18N.getString("resource.pkg-background-image"), - DEFAULT_BACKGROUND_IMAGE, - imageTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - - prepareDistributionXMLFile(params); - - fetchResource(getConfig_Script(params).getName(), - I18N.getString("resource.post-install-script"), - (String) null, - getConfig_Script(params), - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - - return true; - } - - // name of post-image script - private File getConfig_Script(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-post-image.sh"); - } - - private void patchCPLFile(File cpl) throws IOException { - String cplData = Files.readString(cpl.toPath()); - String[] lines = cplData.split("\n"); - try (PrintWriter out = new PrintWriter(new BufferedWriter( - new FileWriter(cpl)))) { - boolean skip = false; // Used to skip Java.runtime bundle, since - // pkgbuild with --root will find two bundles app and Java runtime. - // We cannot generate component proprty list when using - // --component argument. - for (int i = 0; i < lines.length; i++) { - if (lines[i].trim().equals("BundleIsRelocatable")) { - out.println(lines[i]); - out.println(""); - i++; - } else if (lines[i].trim().equals("ChildBundles")) { - skip = true; - } else if (skip && lines[i].trim().equals("")) { - skip = false; - } else { - if (!skip) { - out.println(lines[i]); - } - } - } - } - } - - // pkgbuild includes all components from "--root" and subfolders, - // so if we have app image in folder which contains other images, then they - // will be included as well. It does have "--filter" option which use regex - // to exclude files/folder, but it will overwrite default one which excludes - // based on doc "any .svn or CVS directories, and any .DS_Store files". - // So easy aproach will be to copy user provided app-image into temp folder - // if root path contains other files. - private String getRoot(Map params, - File appLocation) throws IOException { - String root = appLocation.getParent() == null ? - "." : appLocation.getParent(); - File rootDir = new File(root); - File[] list = rootDir.listFiles(); - if (list != null) { // Should not happend - // We should only have app image and/or .DS_Store - if (list.length == 1) { - return root; - } else if (list.length == 2) { - // Check case with app image and .DS_Store - if (list[0].toString().toLowerCase().endsWith(".ds_store") || - list[1].toString().toLowerCase().endsWith(".ds_store")) { - return root; // Only app image and .DS_Store - } - } - } - - // Copy to new root - Path newRoot = Files.createTempDirectory( - TEMP_ROOT.fetchFrom(params).toPath(), - "root-"); - - IOUtils.copyRecursive(appLocation.toPath(), - newRoot.resolve(appLocation.getName())); - - return newRoot.toString(); - } - - private File createPKG(Map params, - File outdir, File appLocation) { - // generic find attempt - try { - File appPKG = getPackages_AppPackage(params); - - String root = getRoot(params, appLocation); - - // Generate default CPL file - File cpl = new File(CONFIG_ROOT.fetchFrom(params).getAbsolutePath() - + File.separator + "cpl.plist"); - ProcessBuilder pb = new ProcessBuilder("pkgbuild", - "--root", - root, - "--install-location", - MAC_INSTALL_DIR.fetchFrom(params), - "--analyze", - cpl.getAbsolutePath()); - - IOUtils.exec(pb, false); - - patchCPLFile(cpl); - - preparePackageScripts(params); - - // build application package - pb = new ProcessBuilder("pkgbuild", - "--root", - root, - "--install-location", - MAC_INSTALL_DIR.fetchFrom(params), - "--component-plist", - cpl.getAbsolutePath(), - "--scripts", - SCRIPTS_DIR.fetchFrom(params).getAbsolutePath(), - appPKG.getAbsolutePath()); - IOUtils.exec(pb, false); - - // build final package - File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(params) - + INSTALLER_SUFFIX.fetchFrom(params) - + ".pkg"); - outdir.mkdirs(); - - List commandLine = new ArrayList<>(); - commandLine.add("productbuild"); - - commandLine.add("--resources"); - commandLine.add(CONFIG_ROOT.fetchFrom(params).getAbsolutePath()); - - // maybe sign - if (Optional.ofNullable(MacAppImageBuilder. - SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) { - if (Platform.getMajorVersion() > 10 || - (Platform.getMajorVersion() == 10 && - Platform.getMinorVersion() >= 12)) { - // we need this for OS X 10.12+ - Log.verbose(I18N.getString("message.signing.pkg")); - } - - String signingIdentity = - DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params); - if (signingIdentity != null) { - commandLine.add("--sign"); - commandLine.add(signingIdentity); - } - - String keychainName = SIGNING_KEYCHAIN.fetchFrom(params); - if (keychainName != null && !keychainName.isEmpty()) { - commandLine.add("--keychain"); - commandLine.add(keychainName); - } - } - - commandLine.add("--distribution"); - commandLine.add( - getConfig_DistributionXMLFile(params).getAbsolutePath()); - commandLine.add("--package-path"); - commandLine.add(PACKAGES_ROOT.fetchFrom(params).getAbsolutePath()); - - commandLine.add(finalPKG.getAbsolutePath()); - - pb = new ProcessBuilder(commandLine); - IOUtils.exec(pb, false); - - return finalPKG; - } catch (Exception ignored) { - Log.verbose(ignored); - return null; - } - } - - ////////////////////////////////////////////////////////////////////////// - // Implement Bundler - ////////////////////////////////////////////////////////////////////////// - - @Override - public String getName() { - return I18N.getString("pkg.bundler.name"); - } - - @Override - public String getDescription() { - return I18N.getString("pkg.bundler.description"); - } - - @Override - public String getID() { - return "pkg"; - } - - @Override - public Collection> getBundleParameters() { - Collection> results = new LinkedHashSet<>(); - results.addAll(MacAppBundler.getAppBundleParameters()); - results.addAll(getPKGBundleParameters()); - return results; - } - - public Collection> getPKGBundleParameters() { - Collection> results = new LinkedHashSet<>(); - - results.addAll(MacAppBundler.getAppBundleParameters()); - results.addAll(Arrays.asList( - DEVELOPER_ID_INSTALLER_SIGNING_KEY, - // IDENTIFIER, - INSTALLER_SUFFIX, - LICENSE_FILE, - // SERVICE_HINT, - SIGNING_KEYCHAIN)); - - return results; - } - - @Override - public boolean validate(Map params) - throws UnsupportedPlatformException, ConfigException { - try { - if (params == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - - // run basic validation to ensure requirements are met - // we are not interested in return code, only possible exception - validateAppImageAndBundeler(params); - - // reject explicitly set sign to true and no valid signature key - if (Optional.ofNullable(MacAppImageBuilder. - SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.FALSE)) { - String signingIdentity = - DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params); - if (signingIdentity == null) { - throw new ConfigException( - I18N.getString("error.explicit-sign-no-cert"), - I18N.getString( - "error.explicit-sign-no-cert.advice")); - } - } - - // hdiutil is always available so there's no need - // to test for availability. - - return true; - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - @Override - public File execute(Map params, - File outputParentDir) throws PackagerException { - return bundle(params, outputParentDir); - } - - @Override - public boolean supported(boolean runtimeInstaller) { - return Platform.getPlatform() == Platform.MAC; - } - -} --- /dev/null 2019-11-18 20:43:39.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/MacPkgBundler.java 2019-11-18 20:43:34.608535400 -0500 @@ -0,0 +1,555 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.*; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; +import static jdk.incubator.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEYCHAIN; +import static jdk.incubator.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEY_USER; +import static jdk.incubator.jpackage.internal.MacAppImageBuilder.MAC_CF_BUNDLE_IDENTIFIER; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; + +public class MacPkgBundler extends MacBaseInstallerBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MacResources"); + + private static final String DEFAULT_BACKGROUND_IMAGE = "background_pkg.png"; + + private static final String TEMPLATE_PREINSTALL_SCRIPT = + "preinstall.template"; + private static final String TEMPLATE_POSTINSTALL_SCRIPT = + "postinstall.template"; + + private static final BundlerParamInfo PACKAGES_ROOT = + new StandardBundlerParam<>( + "mac.pkg.packagesRoot", + File.class, + params -> { + File packagesRoot = + new File(TEMP_ROOT.fetchFrom(params), "packages"); + packagesRoot.mkdirs(); + return packagesRoot; + }, + (s, p) -> new File(s)); + + + protected final BundlerParamInfo SCRIPTS_DIR = + new StandardBundlerParam<>( + "mac.pkg.scriptsDir", + File.class, + params -> { + File scriptsDir = + new File(CONFIG_ROOT.fetchFrom(params), "scripts"); + scriptsDir.mkdirs(); + return scriptsDir; + }, + (s, p) -> new File(s)); + + public static final + BundlerParamInfo DEVELOPER_ID_INSTALLER_SIGNING_KEY = + new StandardBundlerParam<>( + "mac.signing-key-developer-id-installer", + String.class, + params -> { + String result = MacBaseInstallerBundler.findKey( + "Developer ID Installer: " + + SIGNING_KEY_USER.fetchFrom(params), + SIGNING_KEYCHAIN.fetchFrom(params), + VERBOSE.fetchFrom(params)); + if (result != null) { + MacCertificate certificate = new MacCertificate(result); + + if (!certificate.isValid()) { + Log.error(MessageFormat.format( + I18N.getString("error.certificate.expired"), + result)); + } + } + + return result; + }, + (s, p) -> s); + + public static final BundlerParamInfo MAC_INSTALL_DIR = + new StandardBundlerParam<>( + "mac-install-dir", + String.class, + params -> { + String dir = INSTALL_DIR.fetchFrom(params); + return (dir != null) ? dir : "/Applications"; + }, + (s, p) -> s + ); + + public static final BundlerParamInfo INSTALLER_SUFFIX = + new StandardBundlerParam<> ( + "mac.pkg.installerName.suffix", + String.class, + params -> "", + (s, p) -> s); + + public File bundle(Map params, + File outdir) throws PackagerException { + Log.verbose(MessageFormat.format(I18N.getString("message.building-pkg"), + APP_NAME.fetchFrom(params))); + + IOUtils.writableOutputDir(outdir.toPath()); + + try { + File appImageDir = prepareAppBundle(params); + + if (appImageDir != null && prepareConfigFiles(params)) { + + File configScript = getConfig_Script(params); + if (configScript.exists()) { + Log.verbose(MessageFormat.format(I18N.getString( + "message.running-script"), + configScript.getAbsolutePath())); + IOUtils.run("bash", configScript); + } + + return createPKG(params, outdir, appImageDir); + } + return null; + } catch (IOException ex) { + Log.verbose(ex); + throw new PackagerException(ex); + } + } + + private File getPackages_AppPackage(Map params) { + return new File(PACKAGES_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-app.pkg"); + } + + private File getConfig_DistributionXMLFile( + Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), "distribution.dist"); + } + + private File getConfig_BackgroundImage(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-background.png"); + } + + private File getConfig_BackgroundImageDarkAqua(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-background-darkAqua.png"); + } + + private File getScripts_PreinstallFile(Map params) { + return new File(SCRIPTS_DIR.fetchFrom(params), "preinstall"); + } + + private File getScripts_PostinstallFile( + Map params) { + return new File(SCRIPTS_DIR.fetchFrom(params), "postinstall"); + } + + private String getAppIdentifier(Map params) { + return MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params); + } + + private void preparePackageScripts(Map params) + throws IOException { + Log.verbose(I18N.getString("message.preparing-scripts")); + + Map data = new HashMap<>(); + + Path appLocation = Path.of(MAC_INSTALL_DIR.fetchFrom(params), + APP_NAME.fetchFrom(params) + ".app", "Contents", "app"); + + data.put("INSTALL_LOCATION", MAC_INSTALL_DIR.fetchFrom(params)); + data.put("APP_LOCATION", appLocation.toString()); + + createResource(TEMPLATE_PREINSTALL_SCRIPT, params) + .setCategory(I18N.getString("resource.pkg-preinstall-script")) + .setSubstitutionData(data) + .saveToFile(getScripts_PreinstallFile(params)); + getScripts_PreinstallFile(params).setExecutable(true, false); + + createResource(TEMPLATE_POSTINSTALL_SCRIPT, params) + .setCategory(I18N.getString("resource.pkg-postinstall-script")) + .setSubstitutionData(data) + .saveToFile(getScripts_PostinstallFile(params)); + getScripts_PostinstallFile(params).setExecutable(true, false); + } + + private static String URLEncoding(String pkgName) throws URISyntaxException { + URI uri = new URI(null, null, pkgName, null); + return uri.toASCIIString(); + } + + private void prepareDistributionXMLFile(Map params) + throws IOException { + File f = getConfig_DistributionXMLFile(params); + + Log.verbose(MessageFormat.format(I18N.getString( + "message.preparing-distribution-dist"), f.getAbsolutePath())); + + IOUtils.createXml(f.toPath(), xml -> { + xml.writeStartElement("installer-gui-script"); + xml.writeAttribute("minSpecVersion", "1"); + + xml.writeStartElement("title"); + xml.writeCharacters(APP_NAME.fetchFrom(params)); + xml.writeEndElement(); + + xml.writeStartElement("background"); + xml.writeAttribute("file", getConfig_BackgroundImage(params).getName()); + xml.writeAttribute("mime-type", "image/png"); + xml.writeAttribute("alignment", "bottomleft"); + xml.writeAttribute("scaling", "none"); + xml.writeEndElement(); + + xml.writeStartElement("background-darkAqua"); + xml.writeAttribute("file", getConfig_BackgroundImageDarkAqua(params).getName()); + xml.writeAttribute("mime-type", "image/png"); + xml.writeAttribute("alignment", "bottomleft"); + xml.writeAttribute("scaling", "none"); + xml.writeEndElement(); + + String licFileStr = LICENSE_FILE.fetchFrom(params); + if (licFileStr != null) { + File licFile = new File(licFileStr); + xml.writeStartElement("license"); + xml.writeAttribute("file", licFile.getAbsolutePath()); + xml.writeAttribute("mime-type", "text/rtf"); + xml.writeEndElement(); + } + + /* + * Note that the content of the distribution file + * below is generated by productbuild --synthesize + */ + String appId = getAppIdentifier(params); + + xml.writeStartElement("pkg-ref"); + xml.writeAttribute("id", appId); + xml.writeEndElement(); // + xml.writeStartElement("options"); + xml.writeAttribute("customize", "never"); + xml.writeAttribute("require-scripts", "false"); + xml.writeEndElement(); // + xml.writeStartElement("choices-outline"); + xml.writeStartElement("line"); + xml.writeAttribute("choice", "default"); + xml.writeStartElement("line"); + xml.writeAttribute("choice", appId); + xml.writeEndElement(); // + xml.writeEndElement(); // + xml.writeEndElement(); // + xml.writeStartElement("choice"); + xml.writeAttribute("id", "default"); + xml.writeEndElement(); // + xml.writeStartElement("choice"); + xml.writeAttribute("id", appId); + xml.writeAttribute("visible", "false"); + xml.writeStartElement("pkg-ref"); + xml.writeAttribute("id", appId); + xml.writeEndElement(); // + xml.writeEndElement(); // + xml.writeStartElement("pkg-ref"); + xml.writeAttribute("id", appId); + xml.writeAttribute("version", VERSION.fetchFrom(params)); + xml.writeAttribute("onConclusion", "none"); + try { + xml.writeCharacters(URLEncoding( + getPackages_AppPackage(params).getName())); + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + xml.writeEndElement(); // + + xml.writeEndElement(); // + }); + } + + private boolean prepareConfigFiles(Map params) + throws IOException { + + createResource(DEFAULT_BACKGROUND_IMAGE, params) + .setCategory(I18N.getString("resource.pkg-background-image")) + .saveToFile(getConfig_BackgroundImage(params)); + + createResource(DEFAULT_BACKGROUND_IMAGE, params) + .setCategory(I18N.getString("resource.pkg-background-image")) + .saveToFile(getConfig_BackgroundImageDarkAqua(params)); + + prepareDistributionXMLFile(params); + + createResource(null, params) + .setCategory(I18N.getString("resource.post-install-script")) + .saveToFile(getConfig_Script(params)); + + return true; + } + + // name of post-image script + private File getConfig_Script(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-post-image.sh"); + } + + private void patchCPLFile(File cpl) throws IOException { + String cplData = Files.readString(cpl.toPath()); + String[] lines = cplData.split("\n"); + try (PrintWriter out = new PrintWriter(Files.newBufferedWriter( + cpl.toPath()))) { + int skip = 0; + // Used to skip Java.runtime bundle, since + // pkgbuild with --root will find two bundles app and Java runtime. + // We cannot generate component proprty list when using + // --component argument. + for (int i = 0; i < lines.length; i++) { + if (lines[i].trim().equals("BundleIsRelocatable")) { + out.println(lines[i]); + out.println(""); + i++; + } else if (lines[i].trim().equals("ChildBundles")) { + ++skip; + } else if ((skip > 0) && lines[i].trim().equals("")) { + --skip; + } else { + if (skip == 0) { + out.println(lines[i]); + } + } + } + } + } + + // pkgbuild includes all components from "--root" and subfolders, + // so if we have app image in folder which contains other images, then they + // will be included as well. It does have "--filter" option which use regex + // to exclude files/folder, but it will overwrite default one which excludes + // based on doc "any .svn or CVS directories, and any .DS_Store files". + // So easy aproach will be to copy user provided app-image into temp folder + // if root path contains other files. + private String getRoot(Map params, + File appLocation) throws IOException { + String root = appLocation.getParent() == null ? + "." : appLocation.getParent(); + File rootDir = new File(root); + File[] list = rootDir.listFiles(); + if (list != null) { // Should not happend + // We should only have app image and/or .DS_Store + if (list.length == 1) { + return root; + } else if (list.length == 2) { + // Check case with app image and .DS_Store + if (list[0].toString().toLowerCase().endsWith(".ds_store") || + list[1].toString().toLowerCase().endsWith(".ds_store")) { + return root; // Only app image and .DS_Store + } + } + } + + // Copy to new root + Path newRoot = Files.createTempDirectory( + TEMP_ROOT.fetchFrom(params).toPath(), + "root-"); + + IOUtils.copyRecursive(appLocation.toPath(), + newRoot.resolve(appLocation.getName())); + + return newRoot.toString(); + } + + private File createPKG(Map params, + File outdir, File appLocation) { + // generic find attempt + try { + File appPKG = getPackages_AppPackage(params); + + String root = getRoot(params, appLocation); + + // Generate default CPL file + File cpl = new File(CONFIG_ROOT.fetchFrom(params).getAbsolutePath() + + File.separator + "cpl.plist"); + ProcessBuilder pb = new ProcessBuilder("pkgbuild", + "--root", + root, + "--install-location", + MAC_INSTALL_DIR.fetchFrom(params), + "--analyze", + cpl.getAbsolutePath()); + + IOUtils.exec(pb); + + patchCPLFile(cpl); + + preparePackageScripts(params); + + // build application package + pb = new ProcessBuilder("pkgbuild", + "--root", + root, + "--install-location", + MAC_INSTALL_DIR.fetchFrom(params), + "--component-plist", + cpl.getAbsolutePath(), + "--scripts", + SCRIPTS_DIR.fetchFrom(params).getAbsolutePath(), + appPKG.getAbsolutePath()); + IOUtils.exec(pb); + + // build final package + File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(params) + + INSTALLER_SUFFIX.fetchFrom(params) + + ".pkg"); + outdir.mkdirs(); + + List commandLine = new ArrayList<>(); + commandLine.add("productbuild"); + + commandLine.add("--resources"); + commandLine.add(CONFIG_ROOT.fetchFrom(params).getAbsolutePath()); + + // maybe sign + if (Optional.ofNullable(MacAppImageBuilder. + SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) { + if (Platform.getMajorVersion() > 10 || + (Platform.getMajorVersion() == 10 && + Platform.getMinorVersion() >= 12)) { + // we need this for OS X 10.12+ + Log.verbose(I18N.getString("message.signing.pkg")); + } + + String signingIdentity = + DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params); + if (signingIdentity != null) { + commandLine.add("--sign"); + commandLine.add(signingIdentity); + } + + String keychainName = SIGNING_KEYCHAIN.fetchFrom(params); + if (keychainName != null && !keychainName.isEmpty()) { + commandLine.add("--keychain"); + commandLine.add(keychainName); + } + } + + commandLine.add("--distribution"); + commandLine.add( + getConfig_DistributionXMLFile(params).getAbsolutePath()); + commandLine.add("--package-path"); + commandLine.add(PACKAGES_ROOT.fetchFrom(params).getAbsolutePath()); + + commandLine.add(finalPKG.getAbsolutePath()); + + pb = new ProcessBuilder(commandLine); + IOUtils.exec(pb); + + return finalPKG; + } catch (Exception ignored) { + Log.verbose(ignored); + return null; + } + } + + ////////////////////////////////////////////////////////////////////////// + // Implement Bundler + ////////////////////////////////////////////////////////////////////////// + + @Override + public String getName() { + return I18N.getString("pkg.bundler.name"); + } + + @Override + public String getID() { + return "pkg"; + } + + @Override + public boolean validate(Map params) + throws ConfigException { + try { + Objects.requireNonNull(params); + + // run basic validation to ensure requirements are met + // we are not interested in return code, only possible exception + validateAppImageAndBundeler(params); + + if (MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) == null) { + throw new ConfigException( + I18N.getString("message.app-image-requires-identifier"), + I18N.getString( + "message.app-image-requires-identifier.advice")); + } + + // reject explicitly set sign to true and no valid signature key + if (Optional.ofNullable(MacAppImageBuilder. + SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.FALSE)) { + String signingIdentity = + DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params); + if (signingIdentity == null) { + throw new ConfigException( + I18N.getString("error.explicit-sign-no-cert"), + I18N.getString( + "error.explicit-sign-no-cert.advice")); + } + } + + // hdiutil is always available so there's no need + // to test for availability. + + return true; + } catch (RuntimeException re) { + if (re.getCause() instanceof ConfigException) { + throw (ConfigException) re.getCause(); + } else { + throw new ConfigException(re); + } + } + } + + @Override + public File execute(Map params, + File outputParentDir) throws PackagerException { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported(boolean runtimeInstaller) { + return true; + } + + @Override + public boolean isDefault() { + return false; + } + +} --- /dev/null 2019-11-18 20:43:59.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/DMGsetup.scpt 2019-11-18 20:43:56.028364700 -0500 @@ -0,0 +1,38 @@ +tell application "Finder" + tell disk "DEPLOY_ACTUAL_VOLUME_NAME" + open + set current view of container window to icon view + set toolbar visible of container window to false + set statusbar visible of container window to false + + -- size of window should match size of background + set the bounds of container window to {400, 100, 917, 380} + + set theViewOptions to the icon view options of container window + set arrangement of theViewOptions to not arranged + set icon size of theViewOptions to 128 + set background picture of theViewOptions to file ".background:background.png" + + -- Create alias for install location + make new alias file at container window to DEPLOY_INSTALL_LOCATION with properties {name:"DEPLOY_INSTALL_NAME"} + + set allTheFiles to the name of every item of container window + repeat with theFile in allTheFiles + set theFilePath to POSIX Path of theFile + if theFilePath is "/DEPLOY_APPLICATION_NAME.app" + -- Position application location + set position of item theFile of container window to {120, 130} + else if theFilePath is "/DEPLOY_INSTALL_NAME" + -- Position install location + set position of item theFile of container window to {390, 130} + else + -- Move all other files far enough to be not visible if user has "show hidden files" option set + set position of item theFile of container window to {1000, 130} + end + end repeat + + update without registering applications + delay 5 + close + end tell +end tell --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info-lite.plist.template 2019-11-18 20:44:10.847620800 -0500 +++ /dev/null 2019-11-18 20:44:12.000000000 -0500 @@ -1,38 +0,0 @@ - - - - - LSMinimumSystemVersion - 10.9 - CFBundleDevelopmentRegion - English - CFBundleAllowMixedLocalizations - - CFBundleExecutable - DEPLOY_LAUNCHER_NAME - CFBundleIconFile - DEPLOY_ICON_FILE - CFBundleIdentifier - DEPLOY_BUNDLE_IDENTIFIER - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - DEPLOY_BUNDLE_NAME - CFBundlePackageType - APPL - CFBundleShortVersionString - DEPLOY_BUNDLE_SHORT_VERSION - CFBundleSignature - ???? - - LSApplicationCategoryType - DEPLOY_BUNDLE_CATEGORY - CFBundleVersion - DEPLOY_BUNDLE_CFBUNDLE_VERSION - NSHumanReadableCopyright - DEPLOY_BUNDLE_COPYRIGHTDEPLOY_FILE_ASSOCIATIONS - NSHighResolutionCapable - true - - --- /dev/null 2019-11-18 20:44:12.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/Info-lite.plist.template 2019-11-18 20:44:07.434547500 -0500 @@ -0,0 +1,37 @@ + + + + + LSMinimumSystemVersion + 10.9 + CFBundleDevelopmentRegion + English + CFBundleAllowMixedLocalizations + + CFBundleExecutable + DEPLOY_LAUNCHER_NAME + CFBundleIconFile + DEPLOY_ICON_FILE + CFBundleIdentifier + DEPLOY_BUNDLE_IDENTIFIER + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + DEPLOY_BUNDLE_NAME + CFBundlePackageType + APPL + CFBundleShortVersionString + DEPLOY_BUNDLE_SHORT_VERSION + CFBundleSignature + ???? + + LSApplicationCategoryType + Unknown + CFBundleVersion + DEPLOY_BUNDLE_CFBUNDLE_VERSION + NSHumanReadableCopyright + DEPLOY_BUNDLE_COPYRIGHTDEPLOY_FILE_ASSOCIATIONS + NSHighResolutionCapable + true + + --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore.entitlements 2019-11-18 20:44:32.478623800 -0500 +++ /dev/null 2019-11-18 20:44:33.000000000 -0500 @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - --- /dev/null 2019-11-18 20:44:34.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/MacAppStore.entitlements 2019-11-18 20:44:28.968598100 -0500 @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore_Inherit.entitlements 2019-11-18 20:44:53.744557900 -0500 +++ /dev/null 2019-11-18 20:44:55.000000000 -0500 @@ -1,10 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.inherit - - - --- /dev/null 2019-11-18 20:44:55.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/MacAppStore_Inherit.entitlements 2019-11-18 20:44:50.226193800 -0500 @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + + --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties 2019-11-18 20:45:15.181413200 -0500 +++ /dev/null 2019-11-18 20:45:16.000000000 -0500 @@ -1,104 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -app.bundler.name=Mac Application Image -app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers -store.bundler.name=Mac App Store Ready Bundler -store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store. -dmg.bundler.name=DMG Installer -dmg.bundler.description=Mac DMG Installer Bundle -pkg.bundler.name=PKG Installer -pkg.bundler.description=Mac PKG Installer Bundle. - -error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}]. -error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. -error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified. -error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. -error.non-existent-runtime=The file for the Runtime/JRE directory does not exist. -error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE. -error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory. -error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK. -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration. -error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true. -error.no-app-signing-key=No Mac App Store App Signing Key -error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. -error.no-pkg-signing-key=No Mac App Store Installer Signing Key -error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. -error.certificate.expired=Error: Certificate expired {0}. -error.dmg-does-not-do-daemons=DMG bundler doesn't support services. -error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false. - -resource.bundle-config-file=Bundle config file -resource.app-info-plist=Application Info.plist -resource.runtime-info-plist=Java Runtime Info.plist -resource.mac-app-store-entitlements=Mac App Store Entitlements -resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements -resource.dmg-setup-script=DMG setup script -resource.license-setup=License setup -resource.dmg-background=dmg background -resource.volume-icon=volume icon -resource.post-install-script=script to run after application image is populated -resource.pkg-preinstall-script=PKG preinstall script -resource.pkg-postinstall-script=PKG postinstall script -resource.pkg-background-image=pkg background image - - -message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. -message.null-classpath=Null app resources? -message.preparing-info-plist=Preparing Info.plist: {0}. -message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. -message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3. -message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative. -message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings. -message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots. -message.creating-association-with-null-extension=Creating association with null extension. -message.ignoring.symlink=Warning: codesign is skipping the symlink {0}. -message.keychain.error=Error: unable to get keychain list. -message.building-bundle=Building Mac App Store Bundle for {0}. -mesasge.intermediate-bundle-location=Intermediate application bundle image: {0} -message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. -message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.could-not-retrieve-name=Could not retrieve gecos name. -message.app-image-requires-app-name=When using an external app image you must specify the app name. -message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument. -message.app-image-requires-identifier=When using an external app image you must specify the identifier. -message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument. -message.building-dmg=Building DMG package for {0}. -message.running-script=Running shell script on application image [{0}]. -message.intermediate-image-location=[DEBUG] Intermediate application bundle image: {0}. -message.preparing-dmg-setup=Preparing dmg setup: {0}. -message.creating-dmg-file=Creating DMG file: {0}. -message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed. -message.output-to-location=Result DMG installer for {0}: {1}. -message.building-pkg=Building PKG package for {0}. -message.preparing-scripts=Preparing package scripts. -message.preparing-distribution-dist=Preparing distribution.dist: {0}. -message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. - --- /dev/null 2019-11-18 20:45:17.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/MacResources.properties 2019-11-18 20:45:11.841299000 -0500 @@ -0,0 +1,89 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +app.bundler.name=Mac Application Image +store.bundler.name=Mac App Store Ready Bundler +dmg.bundler.name=Mac DMG Package +pkg.bundler.name=Mac PKG Package + +error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}] +error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. +error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified +error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. +error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration +error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true +error.no-app-signing-key=No Mac App Store App Signing Key +error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. +error.no-pkg-signing-key=No Mac App Store Installer Signing Key +error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. +error.certificate.expired=Error: Certificate expired {0} +error.no.xcode.signing=Xcode with command line developer tools is required for signing +error.no.xcode.signing.advice=Install Xcode with command line developer tools. + +resource.bundle-config-file=Bundle config file +resource.app-info-plist=Application Info.plist +resource.runtime-info-plist=Java Runtime Info.plist +resource.mac-app-store-entitlements=Mac App Store Entitlements +resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements +resource.dmg-setup-script=DMG setup script +resource.license-setup=License setup +resource.dmg-background=dmg background +resource.volume-icon=volume icon +resource.post-install-script=script to run after application image is populated +resource.pkg-preinstall-script=PKG preinstall script +resource.pkg-postinstall-script=PKG postinstall script +resource.pkg-background-image=pkg background image + + +message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. +message.null-classpath=Null app resources? +message.preparing-info-plist=Preparing Info.plist: {0}. +message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. +message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3. +message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative. +message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings. +message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots. +message.creating-association-with-null-extension=Creating association with null extension. +message.ignoring.symlink=Warning: codesign is skipping the symlink {0}. +message.keychain.error=Error: unable to get keychain list. +message.building-bundle=Building Mac App Store Package for {0}. +message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. +message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. +message.app-image-requires-app-name=When using an external app image you must specify the app name. +message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument. +message.app-image-requires-identifier=Unable to extract identifier from app image. +message.app-image-requires-identifier.advice=Use "--verbose" for extended error message or specify it via "--mac-package-identifier". +message.building-dmg=Building DMG package for {0}. +message.running-script=Running shell script on application image [{0}]. +message.preparing-dmg-setup=Preparing dmg setup: {0}. +message.creating-dmg-file=Creating DMG file: {0}. +message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed. +message.output-to-location=Result DMG installer for {0}: {1}. +message.building-pkg=Building PKG package for {0}. +message.preparing-scripts=Preparing package scripts. +message.preparing-distribution-dist=Preparing distribution.dist: {0}. +message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. +message.setfile.dmg=Setting custom icon on DMG file skipped because 'SetFile' utility was not found. Installing Xcode with Command Line Tools should resolve this issue. --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties 2019-11-18 20:45:36.618716500 -0500 +++ /dev/null 2019-11-18 20:45:38.000000000 -0500 @@ -1,104 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -app.bundler.name=Mac Application Image -app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers -store.bundler.name=Mac App Store Ready Bundler -store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store. -dmg.bundler.name=DMG Installer -dmg.bundler.description=Mac DMG Installer Bundle -pkg.bundler.name=PKG Installer -pkg.bundler.description=Mac PKG Installer Bundle. - -error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}]. -error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. -error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified. -error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. -error.non-existent-runtime=The file for the Runtime/JRE directory does not exist. -error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE. -error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory. -error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK. -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration. -error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true. -error.no-app-signing-key=No Mac App Store App Signing Key -error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. -error.no-pkg-signing-key=No Mac App Store Installer Signing Key -error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. -error.certificate.expired=Error: Certificate expired {0}. -error.dmg-does-not-do-daemons=DMG bundler doesn't support services. -error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false. - -resource.bundle-config-file=Bundle config file -resource.app-info-plist=Application Info.plist -resource.runtime-info-plist=Java Runtime Info.plist -resource.mac-app-store-entitlements=Mac App Store Entitlements -resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements -resource.dmg-setup-script=DMG setup script -resource.license-setup=License setup -resource.dmg-background=dmg background -resource.volume-icon=volume icon -resource.post-install-script=script to run after application image is populated -resource.pkg-preinstall-script=PKG preinstall script -resource.pkg-postinstall-script=PKG postinstall script -resource.pkg-background-image=pkg background image - - -message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. -message.null-classpath=Null app resources? -message.preparing-info-plist=Preparing Info.plist: {0}. -message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. -message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3. -message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative. -message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings. -message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots. -message.creating-association-with-null-extension=Creating association with null extension. -message.ignoring.symlink=Warning: codesign is skipping the symlink {0}. -message.keychain.error=Error: unable to get keychain list. -message.building-bundle=Building Mac App Store Bundle for {0}. -mesasge.intermediate-bundle-location=Intermediate application bundle image: {0} -message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. -message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.could-not-retrieve-name=Could not retrieve gecos name. -message.app-image-requires-app-name=When using an external app image you must specify the app name. -message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument. -message.app-image-requires-identifier=When using an external app image you must specify the identifier. -message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument. -message.building-dmg=Building DMG package for {0}. -message.running-script=Running shell script on application image [{0}]. -message.intermediate-image-location=[DEBUG] Intermediate application bundle image: {0}. -message.preparing-dmg-setup=Preparing dmg setup: {0}. -message.creating-dmg-file=Creating DMG file: {0}. -message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed. -message.output-to-location=Result DMG installer for {0}: {1}. -message.building-pkg=Building PKG package for {0}. -message.preparing-scripts=Preparing package scripts. -message.preparing-distribution-dist=Preparing distribution.dist: {0}. -message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. - --- /dev/null 2019-11-18 20:45:38.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/MacResources_ja.properties 2019-11-18 20:45:33.134404400 -0500 @@ -0,0 +1,89 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +app.bundler.name=Mac Application Image +store.bundler.name=Mac App Store Ready Bundler +dmg.bundler.name=Mac DMG Package +pkg.bundler.name=Mac PKG Package + +error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}] +error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. +error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified +error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. +error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration +error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true +error.no-app-signing-key=No Mac App Store App Signing Key +error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. +error.no-pkg-signing-key=No Mac App Store Installer Signing Key +error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. +error.certificate.expired=Error: Certificate expired {0} +error.no.xcode.signing=Xcode with command line developer tools is required for signing +error.no.xcode.signing.advice=Install Xcode with command line developer tools. + +resource.bundle-config-file=Bundle config file +resource.app-info-plist=Application Info.plist +resource.runtime-info-plist=Java Runtime Info.plist +resource.mac-app-store-entitlements=Mac App Store Entitlements +resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements +resource.dmg-setup-script=DMG setup script +resource.license-setup=License setup +resource.dmg-background=dmg background +resource.volume-icon=volume icon +resource.post-install-script=script to run after application image is populated +resource.pkg-preinstall-script=PKG preinstall script +resource.pkg-postinstall-script=PKG postinstall script +resource.pkg-background-image=pkg background image + + +message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. +message.null-classpath=Null app resources? +message.preparing-info-plist=Preparing Info.plist: {0}. +message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. +message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3. +message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative. +message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings. +message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots. +message.creating-association-with-null-extension=Creating association with null extension. +message.ignoring.symlink=Warning: codesign is skipping the symlink {0}. +message.keychain.error=Error: unable to get keychain list. +message.building-bundle=Building Mac App Store Package for {0}. +message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. +message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. +message.app-image-requires-app-name=When using an external app image you must specify the app name. +message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument. +message.app-image-requires-identifier=Unable to extract identifier from app image. +message.app-image-requires-identifier.advice=Use "--verbose" for extended error message or specify it via "--mac-package-identifier". +message.building-dmg=Building DMG package for {0}. +message.running-script=Running shell script on application image [{0}]. +message.preparing-dmg-setup=Preparing dmg setup: {0}. +message.creating-dmg-file=Creating DMG file: {0}. +message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed. +message.output-to-location=Result DMG installer for {0}: {1}. +message.building-pkg=Building PKG package for {0}. +message.preparing-scripts=Preparing package scripts. +message.preparing-distribution-dist=Preparing distribution.dist: {0}. +message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. +message.setfile.dmg=Setting custom icon on DMG file skipped because 'SetFile' utility was not found. Installing Xcode with Command Line Tools should resolve this issue. --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties 2019-11-18 20:45:57.825415900 -0500 +++ /dev/null 2019-11-18 20:45:59.000000000 -0500 @@ -1,104 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -app.bundler.name=Mac Application Image -app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers -store.bundler.name=Mac App Store Ready Bundler -store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store. -dmg.bundler.name=DMG Installer -dmg.bundler.description=Mac DMG Installer Bundle -pkg.bundler.name=PKG Installer -pkg.bundler.description=Mac PKG Installer Bundle. - -error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}]. -error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. -error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified. -error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. -error.non-existent-runtime=The file for the Runtime/JRE directory does not exist. -error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE. -error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory. -error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK. -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration. -error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true. -error.no-app-signing-key=No Mac App Store App Signing Key -error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. -error.no-pkg-signing-key=No Mac App Store Installer Signing Key -error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. -error.certificate.expired=Error: Certificate expired {0}. -error.dmg-does-not-do-daemons=DMG bundler doesn't support services. -error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false. - -resource.bundle-config-file=Bundle config file -resource.app-info-plist=Application Info.plist -resource.runtime-info-plist=Java Runtime Info.plist -resource.mac-app-store-entitlements=Mac App Store Entitlements -resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements -resource.dmg-setup-script=DMG setup script -resource.license-setup=License setup -resource.dmg-background=dmg background -resource.volume-icon=volume icon -resource.post-install-script=script to run after application image is populated -resource.pkg-preinstall-script=PKG preinstall script -resource.pkg-postinstall-script=PKG postinstall script -resource.pkg-background-image=pkg background image - - -message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. -message.null-classpath=Null app resources? -message.preparing-info-plist=Preparing Info.plist: {0}. -message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. -message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3. -message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative. -message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings. -message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots. -message.creating-association-with-null-extension=Creating association with null extension. -message.ignoring.symlink=Warning: codesign is skipping the symlink {0}. -message.keychain.error=Error: unable to get keychain list. -message.building-bundle=Building Mac App Store Bundle for {0}. -mesasge.intermediate-bundle-location=Intermediate application bundle image: {0} -message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. -message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.could-not-retrieve-name=Could not retrieve gecos name. -message.app-image-requires-app-name=When using an external app image you must specify the app name. -message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument. -message.app-image-requires-identifier=When using an external app image you must specify the identifier. -message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument. -message.building-dmg=Building DMG package for {0}. -message.running-script=Running shell script on application image [{0}]. -message.intermediate-image-location=[DEBUG] Intermediate application bundle image: {0}. -message.preparing-dmg-setup=Preparing dmg setup: {0}. -message.creating-dmg-file=Creating DMG file: {0}. -message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed. -message.output-to-location=Result DMG installer for {0}: {1}. -message.building-pkg=Building PKG package for {0}. -message.preparing-scripts=Preparing package scripts. -message.preparing-distribution-dist=Preparing distribution.dist: {0}. -message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. - --- /dev/null 2019-11-18 20:45:59.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/MacResources_zh_CN.properties 2019-11-18 20:45:54.359116200 -0500 @@ -0,0 +1,89 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +app.bundler.name=Mac Application Image +store.bundler.name=Mac App Store Ready Bundler +dmg.bundler.name=Mac DMG Package +pkg.bundler.name=Mac PKG Package + +error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}] +error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. +error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified +error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. +error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration +error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true +error.no-app-signing-key=No Mac App Store App Signing Key +error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. +error.no-pkg-signing-key=No Mac App Store Installer Signing Key +error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. +error.certificate.expired=Error: Certificate expired {0} +error.no.xcode.signing=Xcode with command line developer tools is required for signing +error.no.xcode.signing.advice=Install Xcode with command line developer tools. + +resource.bundle-config-file=Bundle config file +resource.app-info-plist=Application Info.plist +resource.runtime-info-plist=Java Runtime Info.plist +resource.mac-app-store-entitlements=Mac App Store Entitlements +resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements +resource.dmg-setup-script=DMG setup script +resource.license-setup=License setup +resource.dmg-background=dmg background +resource.volume-icon=volume icon +resource.post-install-script=script to run after application image is populated +resource.pkg-preinstall-script=PKG preinstall script +resource.pkg-postinstall-script=PKG postinstall script +resource.pkg-background-image=pkg background image + + +message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. +message.null-classpath=Null app resources? +message.preparing-info-plist=Preparing Info.plist: {0}. +message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. +message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3. +message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative. +message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings. +message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots. +message.creating-association-with-null-extension=Creating association with null extension. +message.ignoring.symlink=Warning: codesign is skipping the symlink {0}. +message.keychain.error=Error: unable to get keychain list. +message.building-bundle=Building Mac App Store Package for {0}. +message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. +message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. +message.app-image-requires-app-name=When using an external app image you must specify the app name. +message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument. +message.app-image-requires-identifier=Unable to extract identifier from app image. +message.app-image-requires-identifier.advice=Use "--verbose" for extended error message or specify it via "--mac-package-identifier". +message.building-dmg=Building DMG package for {0}. +message.running-script=Running shell script on application image [{0}]. +message.preparing-dmg-setup=Preparing dmg setup: {0}. +message.creating-dmg-file=Creating DMG file: {0}. +message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed. +message.output-to-location=Result DMG installer for {0}: {1}. +message.building-pkg=Building PKG package for {0}. +message.preparing-scripts=Preparing package scripts. +message.preparing-distribution-dist=Preparing distribution.dist: {0}. +message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. +message.setfile.dmg=Setting custom icon on DMG file skipped because 'SetFile' utility was not found. Installing Xcode with Command Line Tools should resolve this issue. --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Runtime-Info.plist.template 2019-11-18 20:46:19.026686600 -0500 +++ /dev/null 2019-11-18 20:46:20.000000000 -0500 @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - libjli.dylib - CFBundleIdentifier - CF_BUNDLE_IDENTIFIER - CFBundleInfoDictionaryVersion - 7.0 - CFBundleName - CF_BUNDLE_NAME - CFBundlePackageType - BNDL - CFBundleShortVersionString - CF_BUNDLE_SHORT_VERSION_STRING - CFBundleSignature - ???? - CFBundleVersion - CF_BUNDLE_VERSION - - --- /dev/null 2019-11-18 20:46:20.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/Runtime-Info.plist.template 2019-11-18 20:46:15.666335300 -0500 @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + libjli.dylib + CFBundleIdentifier + CF_BUNDLE_IDENTIFIER + CFBundleInfoDictionaryVersion + 7.0 + CFBundleName + CF_BUNDLE_NAME + CFBundlePackageType + BNDL + CFBundleShortVersionString + CF_BUNDLE_SHORT_VERSION_STRING + CFBundleSignature + ???? + CFBundleVersion + CF_BUNDLE_VERSION + + Binary files /dev/null and new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/background_dmg.png differ Binary files /dev/null and new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/background_pkg.png differ Binary files /dev/null and new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/java.icns differ --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist 2019-11-18 20:47:12.883213500 -0500 +++ /dev/null 2019-11-18 20:47:14.000000000 -0500 @@ -1,244 +0,0 @@ - - - - - LPic - - - Attributes - 0x0000 - Data - AAAAAgAAAAAAAAAAAAQAAA== - ID - 5000 - Name - - - - STR# - - - Attributes - 0x0000 - Data - AAYPRW5nbGlzaCBkZWZhdWx0BUFncmVlCERpc2FncmVlBVByaW50B1NhdmUuLi56SWYgeW91IGFncmVlIHdpdGggdGhlIHRlcm1zIG9mIHRoaXMgbGljZW5zZSwgY2xpY2sgIkFncmVlIiB0byBhY2Nlc3MgdGhlIHNvZnR3YXJlLiAgSWYgeW91IGRvIG5vdCBhZ3JlZSwgcHJlc3MgIkRpc2FncmVlLiI= - ID - 5000 - Name - English buttons - - - Attributes - 0x0000 - Data - AAYHRGV1dHNjaAtBa3plcHRpZXJlbghBYmxlaG5lbgdEcnVja2VuClNpY2hlcm4uLi7nS2xpY2tlbiBTaWUgaW4g0kFremVwdGllcmVu0ywgd2VubiBTaWUgbWl0IGRlbiBCZXN0aW1tdW5nZW4gZGVzIFNvZnR3YXJlLUxpemVuenZlcnRyYWdzIGVpbnZlcnN0YW5kZW4gc2luZC4gRmFsbHMgbmljaHQsIGJpdHRlINJBYmxlaG5lbtMgYW5rbGlja2VuLiBTaWUga5pubmVuIGRpZSBTb2Z0d2FyZSBudXIgaW5zdGFsbGllcmVuLCB3ZW5uIFNpZSDSQWt6ZXB0aWVyZW7TIGFuZ2VrbGlja3QgaGFiZW4u - ID - 5001 - Name - German - - - Attributes - 0x0000 - Data - AAYHRW5nbGlzaAVBZ3JlZQhEaXNhZ3JlZQVQcmludAdTYXZlLi4ue0lmIHlvdSBhZ3JlZSB3aXRoIHRoZSB0ZXJtcyBvZiB0aGlzIGxpY2Vuc2UsIHByZXNzICJBZ3JlZSIgdG8gaW5zdGFsbCB0aGUgc29mdHdhcmUuICBJZiB5b3UgZG8gbm90IGFncmVlLCBwcmVzcyAiRGlzYWdyZWUiLg== - ID - 5002 - Name - English - - - Attributes - 0x0000 - Data - AAYHRXNwYZZvbAdBY2VwdGFyCk5vIGFjZXB0YXIISW1wcmltaXIKR3VhcmRhci4uLsBTaSBlc3SHIGRlIGFjdWVyZG8gY29uIGxvcyB0jnJtaW5vcyBkZSBlc3RhIGxpY2VuY2lhLCBwdWxzZSAiQWNlcHRhciIgcGFyYSBpbnN0YWxhciBlbCBzb2Z0d2FyZS4gRW4gZWwgc3VwdWVzdG8gZGUgcXVlIG5vIGVzdI4gZGUgYWN1ZXJkbyBjb24gbG9zIHSOcm1pbm9zIGRlIGVzdGEgbGljZW5jaWEsIHB1bHNlICJObyBhY2VwdGFyLiI= - ID - 5003 - Name - Spanish - - - Attributes - 0x0000 - Data - AAYIRnJhbo1haXMIQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4= - ID - 5004 - Name - French - - - Attributes - 0x0000 - Data - AAYISXRhbGlhbm8HQWNjZXR0bwdSaWZpdXRvBlN0YW1wYQtSZWdpc3RyYS4uLn9TZSBhY2NldHRpIGxlIGNvbmRpemlvbmkgZGkgcXVlc3RhIGxpY2VuemEsIGZhaSBjbGljIHN1ICJBY2NldHRvIiBwZXIgaW5zdGFsbGFyZSBpbCBzb2Z0d2FyZS4gQWx0cmltZW50aSBmYWkgY2xpYyBzdSAiUmlmaXV0byIu - ID - 5005 - Name - Italian - - - Attributes - 0x0000 - Data - AAYISmFwYW5lc2UKk6+I04K1gtyCtwyTr4jTgrWC3IK5gvEIiPON/IK3gukHlduRti4uLrSWe4Ncg3SDZ4NFg0eDQY5nl3CLlpH4jF+W8YLMj/CMj4LJk6+I04KzguqC6Y/qjYeCyYLNgUGDXIN0g2eDRYNHg0GC8INDg5ODWINngVuDi4K3gumCvYLfgsmBdZOviNOCtYLcgreBdoLwiZ+CtYLEgq2CvoKzgqKBQoFAk6+I04KzguqCyIKij+qNh4LJgs2BQYF1k6+I04K1gtyCuYLxgXaC8ImfgrWCxIKtgr6Cs4KigUI= - ID - 5006 - Name - Japanese - - - Attributes - 0x0000 - Data - AAYKTmVkZXJsYW5kcwJKYQNOZWUFUHJpbnQJQmV3YWFyLi4upEluZGllbiB1IGFra29vcmQgZ2FhdCBtZXQgZGUgdm9vcndhYXJkZW4gdmFuIGRlemUgbGljZW50aWUsIGt1bnQgdSBvcCAnSmEnIGtsaWtrZW4gb20gZGUgcHJvZ3JhbW1hdHV1ciB0ZSBpbnN0YWxsZXJlbi4gSW5kaWVuIHUgbmlldCBha2tvb3JkIGdhYXQsIGtsaWt0IHUgb3AgJ05lZScu - ID - 5007 - Name - Dutch - - - Attributes - 0x0000 - Data - AAYGU3ZlbnNrCEdvZGuKbm5zBkF2YppqcwhTa3JpdiB1dAhTcGFyYS4uLpNPbSBEdSBnb2Rrim5uZXIgbGljZW5zdmlsbGtvcmVuIGtsaWNrYSBwjCAiR29ka4pubnMiIGaaciBhdHQgaW5zdGFsbGVyYSBwcm9ncmFtcHJvZHVrdGVuLiBPbSBEdSBpbnRlIGdvZGuKbm5lciBsaWNlbnN2aWxsa29yZW4sIGtsaWNrYSBwjCAiQXZimmpzIi4= - ID - 5008 - Name - Swedish - - - Attributes - 0x0000 - Data - AAYRUG9ydHVndZBzLCBCcmFzaWwJQ29uY29yZGFyCURpc2NvcmRhcghJbXByaW1pcglTYWx2YXIuLi6MU2UgZXN0hyBkZSBhY29yZG8gY29tIG9zIHRlcm1vcyBkZXN0YSBsaWNlbo1hLCBwcmVzc2lvbmUgIkNvbmNvcmRhciIgcGFyYSBpbnN0YWxhciBvIHNvZnR3YXJlLiBTZSBui28gZXN0hyBkZSBhY29yZG8sIHByZXNzaW9uZSAiRGlzY29yZGFyIi4= - ID - 5009 - Name - Brazilian Portuguese - - - Attributes - 0x0000 - Data - AAYSU2ltcGxpZmllZCBDaGluZXNlBM2s0uIGsrvNrNLiBLTy06EGtOa0oqGtVMjnufvE+s2s0uKxvtDtv8nQrdLptcTM9b/uo6zH67C0obDNrNLiobHAtLCy17C0y8jtvP6ho8jnufvE+rK7zazS4qOsx+uwtKGwsrvNrNLiobGhow== - ID - 5010 - Name - Simplified Chinese - - - Attributes - 0x0000 - Data - AAYTVHJhZGl0aW9uYWwgQ2hpbmVzZQSmULdOBqSjplC3TgSmQ6ZMBsB4pnOhS1CmcKpHsXqmULdOpbuzXKVpw9K4zKq6sfi02qFBvdCr9qGnplC3TqGopUimd7jLs27F6aFDpnCqR6SjplC3TqFBvdCr9qGnpKOmULdOoaihQw== - ID - 5011 - Name - Traditional Chinese - - - Attributes - 0x0000 - Data - AAYFRGFuc2sERW5pZwVVZW5pZwdVZHNrcml2CkFya2l2ZXIuLi6YSHZpcyBkdSBhY2NlcHRlcmVyIGJldGluZ2Vsc2VybmUgaSBsaWNlbnNhZnRhbGVuLCBza2FsIGR1IGtsaWtrZSBwjCDSRW5pZ9MgZm9yIGF0IGluc3RhbGxlcmUgc29mdHdhcmVuLiBLbGlrIHCMINJVZW5pZ9MgZm9yIGF0IGFubnVsbGVyZSBpbnN0YWxsZXJpbmdlbi4= - ID - 5012 - Name - Danish - - - Attributes - 0x0000 - Data - AAYFU3VvbWkISHl2imtzeW4KRW4gaHl2imtzeQdUdWxvc3RhCVRhbGxlbm5hyW9IeXaKa3N5IGxpc2Vuc3Npc29waW11a3NlbiBlaGRvdCBvc29pdHRhbWFsbGEg1Uh5doprc3nVLiBKb3MgZXQgaHl2imtzeSBzb3BpbXVrc2VuIGVodG9qYSwgb3NvaXRhINVFbiBoeXaKa3N51S4= - ID - 5013 - Name - Finnish - - - Attributes - 0x0000 - Data - AAYRRnJhbo1haXMgY2FuYWRpZW4IQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4= - ID - 5014 - Name - French Canadian - - - Attributes - 0x0000 - Data - AAYGS29yZWFuBLW/wMcJtb/AxyC+yMfUBsfBuLDGrgfA+sDlLi4ufrvnv+sgsOi+4LytwMcgs7u/67+hILW/wMfHz7jpLCAitb/AxyIgtNzD37imILStt68gvNLHwcauv/6+7rimILyzxKHHz73KvcO/wC4gtb/Ax8fPwfYgvsq0wrTZuOksICK1v8DHIL7Ix9QiILTcw9+4piC0qbijvcq9w7/ALg== - ID - 5015 - Name - Korean - - - Attributes - 0x0000 - Data - AAYFTm9yc2sERW5pZwlJa2tlIGVuaWcIU2tyaXYgdXQKQXJraXZlci4uLqNIdmlzIERlIGVyIGVuaWcgaSBiZXN0ZW1tZWxzZW5lIGkgZGVubmUgbGlzZW5zYXZ0YWxlbiwga2xpa2tlciBEZSBwjCAiRW5pZyIta25hcHBlbiBmb3IgjCBpbnN0YWxsZXJlIHByb2dyYW12YXJlbi4gSHZpcyBEZSBpa2tlIGVyIGVuaWcsIGtsaWtrZXIgRGUgcIwgIklra2UgZW5pZyIu - ID - 5016 - Name - Norwegian - - - TEXT - - - Attributes - 0x0000 - Data - APPLICATION_LICENSE_TEXT - ID - 5000 - Name - English SLA - - - TMPL - - - Attributes - 0x0000 - Data - E0RlZmF1bHQgTGFuZ3VhZ2UgSUREV1JEBUNvdW50T0NOVAQqKioqTFNUQwtzeXMgbGFuZyBJRERXUkQebG9jYWwgcmVzIElEIChvZmZzZXQgZnJvbSA1MDAwRFdSRBAyLWJ5dGUgbGFuZ3VhZ2U/RFdSRAQqKioqTFNURQ== - ID - 128 - Name - LPic - - - plst - - - Attributes - 0x0050 - Data - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - ID - 0 - Name - - - - styl - - - Attributes - 0x0000 - Data - AAMAAAAAAAwACQAUAAAAAAAAAAAAAAAAACcADAAJABQBAAAAAAAAAAAAAAAAKgAMAAkAFAAAAAAAAAAAAAA= - ID - 5000 - Name - English SLA - - - - --- /dev/null 2019-11-18 20:47:14.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/lic_template.plist 2019-11-18 20:47:09.408600200 -0500 @@ -0,0 +1,244 @@ + + + + + LPic + + + Attributes + 0x0000 + Data + AAAAAgAAAAAAAAAAAAQAAA== + ID + 5000 + Name + + + + STR# + + + Attributes + 0x0000 + Data + AAYPRW5nbGlzaCBkZWZhdWx0BUFncmVlCERpc2FncmVlBVByaW50B1NhdmUuLi56SWYgeW91IGFncmVlIHdpdGggdGhlIHRlcm1zIG9mIHRoaXMgbGljZW5zZSwgY2xpY2sgIkFncmVlIiB0byBhY2Nlc3MgdGhlIHNvZnR3YXJlLiAgSWYgeW91IGRvIG5vdCBhZ3JlZSwgcHJlc3MgIkRpc2FncmVlLiI= + ID + 5000 + Name + English buttons + + + Attributes + 0x0000 + Data + AAYHRGV1dHNjaAtBa3plcHRpZXJlbghBYmxlaG5lbgdEcnVja2VuClNpY2hlcm4uLi7nS2xpY2tlbiBTaWUgaW4g0kFremVwdGllcmVu0ywgd2VubiBTaWUgbWl0IGRlbiBCZXN0aW1tdW5nZW4gZGVzIFNvZnR3YXJlLUxpemVuenZlcnRyYWdzIGVpbnZlcnN0YW5kZW4gc2luZC4gRmFsbHMgbmljaHQsIGJpdHRlINJBYmxlaG5lbtMgYW5rbGlja2VuLiBTaWUga5pubmVuIGRpZSBTb2Z0d2FyZSBudXIgaW5zdGFsbGllcmVuLCB3ZW5uIFNpZSDSQWt6ZXB0aWVyZW7TIGFuZ2VrbGlja3QgaGFiZW4u + ID + 5001 + Name + German + + + Attributes + 0x0000 + Data + AAYHRW5nbGlzaAVBZ3JlZQhEaXNhZ3JlZQVQcmludAdTYXZlLi4ue0lmIHlvdSBhZ3JlZSB3aXRoIHRoZSB0ZXJtcyBvZiB0aGlzIGxpY2Vuc2UsIHByZXNzICJBZ3JlZSIgdG8gaW5zdGFsbCB0aGUgc29mdHdhcmUuICBJZiB5b3UgZG8gbm90IGFncmVlLCBwcmVzcyAiRGlzYWdyZWUiLg== + ID + 5002 + Name + English + + + Attributes + 0x0000 + Data + AAYHRXNwYZZvbAdBY2VwdGFyCk5vIGFjZXB0YXIISW1wcmltaXIKR3VhcmRhci4uLsBTaSBlc3SHIGRlIGFjdWVyZG8gY29uIGxvcyB0jnJtaW5vcyBkZSBlc3RhIGxpY2VuY2lhLCBwdWxzZSAiQWNlcHRhciIgcGFyYSBpbnN0YWxhciBlbCBzb2Z0d2FyZS4gRW4gZWwgc3VwdWVzdG8gZGUgcXVlIG5vIGVzdI4gZGUgYWN1ZXJkbyBjb24gbG9zIHSOcm1pbm9zIGRlIGVzdGEgbGljZW5jaWEsIHB1bHNlICJObyBhY2VwdGFyLiI= + ID + 5003 + Name + Spanish + + + Attributes + 0x0000 + Data + AAYIRnJhbo1haXMIQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4= + ID + 5004 + Name + French + + + Attributes + 0x0000 + Data + AAYISXRhbGlhbm8HQWNjZXR0bwdSaWZpdXRvBlN0YW1wYQtSZWdpc3RyYS4uLn9TZSBhY2NldHRpIGxlIGNvbmRpemlvbmkgZGkgcXVlc3RhIGxpY2VuemEsIGZhaSBjbGljIHN1ICJBY2NldHRvIiBwZXIgaW5zdGFsbGFyZSBpbCBzb2Z0d2FyZS4gQWx0cmltZW50aSBmYWkgY2xpYyBzdSAiUmlmaXV0byIu + ID + 5005 + Name + Italian + + + Attributes + 0x0000 + Data + AAYISmFwYW5lc2UKk6+I04K1gtyCtwyTr4jTgrWC3IK5gvEIiPON/IK3gukHlduRti4uLrSWe4Ncg3SDZ4NFg0eDQY5nl3CLlpH4jF+W8YLMj/CMj4LJk6+I04KzguqC6Y/qjYeCyYLNgUGDXIN0g2eDRYNHg0GC8INDg5ODWINngVuDi4K3gumCvYLfgsmBdZOviNOCtYLcgreBdoLwiZ+CtYLEgq2CvoKzgqKBQoFAk6+I04KzguqCyIKij+qNh4LJgs2BQYF1k6+I04K1gtyCuYLxgXaC8ImfgrWCxIKtgr6Cs4KigUI= + ID + 5006 + Name + Japanese + + + Attributes + 0x0000 + Data + AAYKTmVkZXJsYW5kcwJKYQNOZWUFUHJpbnQJQmV3YWFyLi4upEluZGllbiB1IGFra29vcmQgZ2FhdCBtZXQgZGUgdm9vcndhYXJkZW4gdmFuIGRlemUgbGljZW50aWUsIGt1bnQgdSBvcCAnSmEnIGtsaWtrZW4gb20gZGUgcHJvZ3JhbW1hdHV1ciB0ZSBpbnN0YWxsZXJlbi4gSW5kaWVuIHUgbmlldCBha2tvb3JkIGdhYXQsIGtsaWt0IHUgb3AgJ05lZScu + ID + 5007 + Name + Dutch + + + Attributes + 0x0000 + Data + AAYGU3ZlbnNrCEdvZGuKbm5zBkF2YppqcwhTa3JpdiB1dAhTcGFyYS4uLpNPbSBEdSBnb2Rrim5uZXIgbGljZW5zdmlsbGtvcmVuIGtsaWNrYSBwjCAiR29ka4pubnMiIGaaciBhdHQgaW5zdGFsbGVyYSBwcm9ncmFtcHJvZHVrdGVuLiBPbSBEdSBpbnRlIGdvZGuKbm5lciBsaWNlbnN2aWxsa29yZW4sIGtsaWNrYSBwjCAiQXZimmpzIi4= + ID + 5008 + Name + Swedish + + + Attributes + 0x0000 + Data + AAYRUG9ydHVndZBzLCBCcmFzaWwJQ29uY29yZGFyCURpc2NvcmRhcghJbXByaW1pcglTYWx2YXIuLi6MU2UgZXN0hyBkZSBhY29yZG8gY29tIG9zIHRlcm1vcyBkZXN0YSBsaWNlbo1hLCBwcmVzc2lvbmUgIkNvbmNvcmRhciIgcGFyYSBpbnN0YWxhciBvIHNvZnR3YXJlLiBTZSBui28gZXN0hyBkZSBhY29yZG8sIHByZXNzaW9uZSAiRGlzY29yZGFyIi4= + ID + 5009 + Name + Brazilian Portuguese + + + Attributes + 0x0000 + Data + AAYSU2ltcGxpZmllZCBDaGluZXNlBM2s0uIGsrvNrNLiBLTy06EGtOa0oqGtVMjnufvE+s2s0uKxvtDtv8nQrdLptcTM9b/uo6zH67C0obDNrNLiobHAtLCy17C0y8jtvP6ho8jnufvE+rK7zazS4qOsx+uwtKGwsrvNrNLiobGhow== + ID + 5010 + Name + Simplified Chinese + + + Attributes + 0x0000 + Data + AAYTVHJhZGl0aW9uYWwgQ2hpbmVzZQSmULdOBqSjplC3TgSmQ6ZMBsB4pnOhS1CmcKpHsXqmULdOpbuzXKVpw9K4zKq6sfi02qFBvdCr9qGnplC3TqGopUimd7jLs27F6aFDpnCqR6SjplC3TqFBvdCr9qGnpKOmULdOoaihQw== + ID + 5011 + Name + Traditional Chinese + + + Attributes + 0x0000 + Data + AAYFRGFuc2sERW5pZwVVZW5pZwdVZHNrcml2CkFya2l2ZXIuLi6YSHZpcyBkdSBhY2NlcHRlcmVyIGJldGluZ2Vsc2VybmUgaSBsaWNlbnNhZnRhbGVuLCBza2FsIGR1IGtsaWtrZSBwjCDSRW5pZ9MgZm9yIGF0IGluc3RhbGxlcmUgc29mdHdhcmVuLiBLbGlrIHCMINJVZW5pZ9MgZm9yIGF0IGFubnVsbGVyZSBpbnN0YWxsZXJpbmdlbi4= + ID + 5012 + Name + Danish + + + Attributes + 0x0000 + Data + AAYFU3VvbWkISHl2imtzeW4KRW4gaHl2imtzeQdUdWxvc3RhCVRhbGxlbm5hyW9IeXaKa3N5IGxpc2Vuc3Npc29waW11a3NlbiBlaGRvdCBvc29pdHRhbWFsbGEg1Uh5doprc3nVLiBKb3MgZXQgaHl2imtzeSBzb3BpbXVrc2VuIGVodG9qYSwgb3NvaXRhINVFbiBoeXaKa3N51S4= + ID + 5013 + Name + Finnish + + + Attributes + 0x0000 + Data + AAYRRnJhbo1haXMgY2FuYWRpZW4IQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4= + ID + 5014 + Name + French Canadian + + + Attributes + 0x0000 + Data + AAYGS29yZWFuBLW/wMcJtb/AxyC+yMfUBsfBuLDGrgfA+sDlLi4ufrvnv+sgsOi+4LytwMcgs7u/67+hILW/wMfHz7jpLCAitb/AxyIgtNzD37imILStt68gvNLHwcauv/6+7rimILyzxKHHz73KvcO/wC4gtb/Ax8fPwfYgvsq0wrTZuOksICK1v8DHIL7Ix9QiILTcw9+4piC0qbijvcq9w7/ALg== + ID + 5015 + Name + Korean + + + Attributes + 0x0000 + Data + AAYFTm9yc2sERW5pZwlJa2tlIGVuaWcIU2tyaXYgdXQKQXJraXZlci4uLqNIdmlzIERlIGVyIGVuaWcgaSBiZXN0ZW1tZWxzZW5lIGkgZGVubmUgbGlzZW5zYXZ0YWxlbiwga2xpa2tlciBEZSBwjCAiRW5pZyIta25hcHBlbiBmb3IgjCBpbnN0YWxsZXJlIHByb2dyYW12YXJlbi4gSHZpcyBEZSBpa2tlIGVyIGVuaWcsIGtsaWtrZXIgRGUgcIwgIklra2UgZW5pZyIu + ID + 5016 + Name + Norwegian + + + TEXT + + + Attributes + 0x0000 + Data + APPLICATION_LICENSE_TEXT + ID + 5000 + Name + English SLA + + + TMPL + + + Attributes + 0x0000 + Data + E0RlZmF1bHQgTGFuZ3VhZ2UgSUREV1JEBUNvdW50T0NOVAQqKioqTFNUQwtzeXMgbGFuZyBJRERXUkQebG9jYWwgcmVzIElEIChvZmZzZXQgZnJvbSA1MDAwRFdSRBAyLWJ5dGUgbGFuZ3VhZ2U/RFdSRAQqKioqTFNURQ== + ID + 128 + Name + LPic + + + plst + + + Attributes + 0x0050 + Data + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ID + 0 + Name + + + + styl + + + Attributes + 0x0000 + Data + AAMAAAAAAAwACQAUAAAAAAAAAAAAAAAAACcADAAJABQBAAAAAAAAAAAAAAAAKgAMAAkAFAAAAAAAAAAAAAA= + ID + 5000 + Name + English SLA + + + + --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/postinstall.template 2019-11-18 20:47:33.847878900 -0500 +++ /dev/null 2019-11-18 20:47:35.000000000 -0500 @@ -1,6 +0,0 @@ -#!/usr/bin/env sh - -chown root:wheel "INSTALL_LOCATION" -chmod a+rX "INSTALL_LOCATION" - -exit 0 --- /dev/null 2019-11-18 20:47:35.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/postinstall.template 2019-11-18 20:47:30.406049000 -0500 @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +chown root:wheel "INSTALL_LOCATION" +chmod a+rX "INSTALL_LOCATION" +chmod +r "APP_LOCATION/"*.jar + +exit 0 --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/preinstall.template 2019-11-18 20:47:55.209538000 -0500 +++ /dev/null 2019-11-18 20:47:56.000000000 -0500 @@ -1,8 +0,0 @@ -#!/usr/bin/env sh - -if [ ! -d "INSTALL_LOCATION" ] -then - mkdir -p "INSTALL_LOCATION" -fi - -exit 0 --- /dev/null 2019-11-18 20:47:56.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/resources/preinstall.template 2019-11-18 20:47:51.660309000 -0500 @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +if [ ! -d "INSTALL_LOCATION" ] +then + mkdir -p "INSTALL_LOCATION" +fi + +exit 0 --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java 2019-11-18 20:48:06.159766700 -0500 +++ /dev/null 2019-11-18 20:48:07.000000000 -0500 @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -@FunctionalInterface -interface ArgAction { - void execute(); -} --- /dev/null 2019-11-18 20:48:07.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/classes/module-info.java.extra 2019-11-18 20:48:02.755410500 -0500 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +provides jdk.incubator.jpackage.internal.Bundler with + jdk.incubator.jpackage.internal.MacAppBundler, + jdk.incubator.jpackage.internal.MacAppStoreBundler, + jdk.incubator.jpackage.internal.MacDmgBundler, + jdk.incubator.jpackage.internal.MacPkgBundler; + --- old/src/jdk.jpackage/macosx/native/jpackageapplauncher/main.m 2019-11-18 20:48:27.564504600 -0500 +++ /dev/null 2019-11-18 20:48:29.000000000 -0500 @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#import -#include -#include - -typedef bool (*start_launcher)(int argc, char* argv[]); -typedef void (*stop_launcher)(); - -int main(int argc, char *argv[]) { -#if !__has_feature(objc_arc) - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; -#endif - - int result = 1; - - @try { - setlocale(LC_ALL, "en_US.utf8"); - - NSBundle *mainBundle = [NSBundle mainBundle]; - NSString *mainBundlePath = [mainBundle bundlePath]; - NSString *libraryName = [mainBundlePath stringByAppendingPathComponent:@"Contents/MacOS/libapplauncher.dylib"]; - - void* library = dlopen([libraryName UTF8String], RTLD_LAZY); - - if (library == NULL) { - NSLog(@"%@ not found.\n", libraryName); - } - - if (library != NULL) { - start_launcher start = - (start_launcher)dlsym(library, "start_launcher"); - stop_launcher stop = - (stop_launcher)dlsym(library, "stop_launcher"); - - if (start != NULL && stop != NULL) { - if (start(argc, argv) == true) { - result = 0; - stop(); - } - } else if (start == NULL) { - NSLog(@"start_launcher not found in %@.\n", libraryName); - } else { - NSLog(@"stop_launcher not found in %@.\n", libraryName); - } - dlclose(library); - } - } @catch (NSException *exception) { - NSLog(@"%@: %@", exception, [exception callStackSymbols]); - result = 1; - } - -#if !__has_feature(objc_arc) - [pool drain]; -#endif - - return result; -} --- /dev/null 2019-11-18 20:48:29.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/native/jpackageapplauncher/main.m 2019-11-18 20:48:24.091551800 -0500 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import +#include +#include + +typedef bool (*start_launcher)(int argc, char* argv[]); +typedef void (*stop_launcher)(); + +int main(int argc, char *argv[]) { +#if !__has_feature(objc_arc) + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +#endif + + int result = 1; + + @try { + setlocale(LC_ALL, "en_US.utf8"); + + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *mainBundlePath = [mainBundle bundlePath]; + NSString *libraryName = [mainBundlePath stringByAppendingPathComponent:@"Contents/MacOS/libapplauncher.dylib"]; + + void* library = dlopen([libraryName UTF8String], RTLD_LAZY); + + if (library == NULL) { + NSLog(@"%@ not found.\n", libraryName); + } + + if (library != NULL) { + start_launcher start = + (start_launcher)dlsym(library, "start_launcher"); + stop_launcher stop = + (stop_launcher)dlsym(library, "stop_launcher"); + + if (start != NULL && stop != NULL) { + if (start(argc, argv) == true) { + result = 0; + stop(); + } + } else if (start == NULL) { + NSLog(@"start_launcher not found in %@.\n", libraryName); + } else { + NSLog(@"stop_launcher not found in %@.\n", libraryName); + } + dlclose(library); + } + } @catch (NSException *exception) { + NSLog(@"%@: %@", exception, [exception callStackSymbols]); + result = 1; + } + +#if !__has_feature(objc_arc) + [pool drain]; +#endif + + return result; +} --- old/src/jdk.jpackage/macosx/native/libapplauncher/MacPlatform.h 2019-11-18 20:48:38.551213000 -0500 +++ /dev/null 2019-11-18 20:48:39.000000000 -0500 @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef MACPLATFORM_H -#define MACPLATFORM_H - -#include "Platform.h" -#include "PosixPlatform.h" - -class MacPlatform : virtual public Platform, PosixPlatform { -private: - bool UsePListForConfigFile(); - -protected: - virtual TString getTmpDirString(); - -public: - MacPlatform(void); - virtual ~MacPlatform(void); - -public: - virtual void ShowMessage(TString title, TString description); - virtual void ShowMessage(TString description); - - virtual TCHAR* ConvertStringToFileSystemString( - TCHAR* Source, bool &release); - virtual TCHAR* ConvertFileSystemStringToString( - TCHAR* Source, bool &release); - - virtual void SetCurrentDirectory(TString Value); - virtual TString GetPackageRootDirectory(); - virtual TString GetAppDataDirectory(); - virtual TString GetBundledJavaLibraryFileName(TString RuntimePath); - virtual TString GetAppName(); - - TString GetPackageAppDirectory(); - TString GetPackageLauncherDirectory(); - TString GetPackageRuntimeBinDirectory(); - - virtual ISectionalPropertyContainer* GetConfigFile(TString FileName); - virtual TString GetModuleFileName(); - - virtual bool IsMainThread(); - virtual TPlatformNumber GetMemorySize(); - - virtual std::map GetKeys(); -}; - - -#endif // MACPLATFORM_H --- /dev/null 2019-11-18 20:48:40.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/native/libapplauncher/MacPlatform.h 2019-11-18 20:48:35.029195800 -0500 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef MACPLATFORM_H +#define MACPLATFORM_H + +#include "Platform.h" +#include "PosixPlatform.h" + +class MacPlatform : virtual public Platform, PosixPlatform { +private: + bool UsePListForConfigFile(); + +protected: + virtual TString getTmpDirString(); + +public: + MacPlatform(void); + virtual ~MacPlatform(void); + +public: + virtual void ShowMessage(TString title, TString description); + virtual void ShowMessage(TString description); + + virtual TCHAR* ConvertStringToFileSystemString( + TCHAR* Source, bool &release); + virtual TCHAR* ConvertFileSystemStringToString( + TCHAR* Source, bool &release); + + virtual TString GetPackageRootDirectory(); + virtual TString GetAppDataDirectory(); + virtual TString GetBundledJavaLibraryFileName(TString RuntimePath); + virtual TString GetAppName(); + + TString GetPackageAppDirectory(); + TString GetPackageLauncherDirectory(); + TString GetPackageRuntimeBinDirectory(); + + virtual ISectionalPropertyContainer* GetConfigFile(TString FileName); + virtual TString GetModuleFileName(); + + virtual bool IsMainThread(); + virtual TPlatformNumber GetMemorySize(); + + virtual std::map GetKeys(); +}; + + +#endif // MACPLATFORM_H --- old/src/jdk.jpackage/macosx/native/libapplauncher/MacPlatform.mm 2019-11-18 20:48:59.953210800 -0500 +++ /dev/null 2019-11-18 20:49:01.000000000 -0500 @@ -1,507 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Platform.h" - -#include "MacPlatform.h" -#include "Helpers.h" -#include "Package.h" -#include "PropertyFile.h" -#include "IniFile.h" - -#include -#include -#include -#include -#include - -#import -#import - -#include -#include - -#ifdef __OBJC__ -#import -#endif //__OBJC__ - -#define MAC_JPACKAGE_TMP_DIR \ - "/Library/Application Support/Java/JPackage/tmp" - -NSString* StringToNSString(TString Value) { - NSString* result = [NSString stringWithCString : Value.c_str() - encoding : [NSString defaultCStringEncoding]]; - return result; -} - -FileSystemStringToString::FileSystemStringToString(const TCHAR* value) { - bool release = false; - PlatformString lvalue = PlatformString(value); - Platform& platform = Platform::GetInstance(); - TCHAR* buffer = platform.ConvertFileSystemStringToString(lvalue, release); - FData = buffer; - - if (buffer != NULL && release == true) { - delete[] buffer; - } -} - -FileSystemStringToString::operator TString() { - return FData; -} - -StringToFileSystemString::StringToFileSystemString(const TString &value) { - FRelease = false; - PlatformString lvalue = PlatformString(value); - Platform& platform = Platform::GetInstance(); - FData = platform.ConvertStringToFileSystemString(lvalue, FRelease); -} - -StringToFileSystemString::~StringToFileSystemString() { - if (FRelease == true) { - delete[] FData; - } -} - -StringToFileSystemString::operator TCHAR* () { - return FData; -} - -MacPlatform::MacPlatform(void) : Platform(), PosixPlatform() { -} - -MacPlatform::~MacPlatform(void) { -} - -TString MacPlatform::GetPackageAppDirectory() { - return FilePath::IncludeTrailingSeparator( - GetPackageRootDirectory()) + _T("Java"); -} - -TString MacPlatform::GetPackageLauncherDirectory() { - return FilePath::IncludeTrailingSeparator( - GetPackageRootDirectory()) + _T("MacOS"); -} - -TString MacPlatform::GetPackageRuntimeBinDirectory() { - return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + - _T("runtime/Contents/Home/bin"); -} - -bool MacPlatform::UsePListForConfigFile() { - return FilePath::FileExists(GetConfigFileName()) == false; -} - -void MacPlatform::ShowMessage(TString Title, TString Description) { - NSString *ltitle = StringToNSString(Title); - NSString *ldescription = StringToNSString(Description); - - NSLog(@"%@:%@", ltitle, ldescription); -} - -void MacPlatform::ShowMessage(TString Description) { - TString appname = GetModuleFileName(); - appname = FilePath::ExtractFileName(appname); - ShowMessage(appname, Description); -} - -TString MacPlatform::getTmpDirString() { - return TString(MAC_JPACKAGE_TMP_DIR); -} - -TCHAR* MacPlatform::ConvertStringToFileSystemString(TCHAR* Source, - bool &release) { - TCHAR* result = NULL; - release = false; - CFStringRef StringRef = CFStringCreateWithCString(kCFAllocatorDefault, - Source, kCFStringEncodingUTF8); - - if (StringRef != NULL) { - @ try { - CFIndex length = - CFStringGetMaximumSizeOfFileSystemRepresentation(StringRef); - result = new char[length + 1]; - if (result != NULL) { - if (CFStringGetFileSystemRepresentation(StringRef, - result, length)) { - release = true; - } else { - delete[] result; - result = NULL; - } - } - } - @finally - { - CFRelease(StringRef); - } - } - - return result; -} - -TCHAR* MacPlatform::ConvertFileSystemStringToString(TCHAR* Source, - bool &release) { - TCHAR* result = NULL; - release = false; - CFStringRef StringRef = CFStringCreateWithFileSystemRepresentation( - kCFAllocatorDefault, Source); - - if (StringRef != NULL) { - @ try { - CFIndex length = CFStringGetLength(StringRef); - - if (length > 0) { - CFIndex maxSize = CFStringGetMaximumSizeForEncoding( - length, kCFStringEncodingUTF8); - - result = new char[maxSize + 1]; - if (result != NULL) { - if (CFStringGetCString(StringRef, result, maxSize, - kCFStringEncodingUTF8) == true) { - release = true; - } else { - delete[] result; - result = NULL; - } - } - } - } - @finally - { - CFRelease(StringRef); - } - } - - return result; -} - -void MacPlatform::SetCurrentDirectory(TString Value) { - chdir(PlatformString(Value).toPlatformString()); -} - -TString MacPlatform::GetPackageRootDirectory() { - NSBundle *mainBundle = [NSBundle mainBundle]; - NSString *mainBundlePath = [mainBundle bundlePath]; - NSString *contentsPath = - [mainBundlePath stringByAppendingString : @"/Contents"]; - TString result = [contentsPath UTF8String]; - return result; -} - -TString MacPlatform::GetAppDataDirectory() { - TString result; - NSArray *paths = NSSearchPathForDirectoriesInDomains( - NSApplicationSupportDirectory, NSUserDomainMask, YES); - NSString *applicationSupportDirectory = [paths firstObject]; - result = [applicationSupportDirectory UTF8String]; - return result; -} - -TString MacPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) { - TString result; - - // first try lib/, then lib/jli - result = FilePath::IncludeTrailingSeparator(RuntimePath) + - _T("Contents/Home/lib/libjli.dylib"); - - if (FilePath::FileExists(result) == false) { - result = FilePath::IncludeTrailingSeparator(RuntimePath) + - _T("Contents/Home/lib/jli/libjli.dylib"); - - if (FilePath::FileExists(result) == false) { - // cannot find - NSLog(@"Cannot find libjli.dysym!"); - result = _T(""); - } - } - - return result; -} - -TString MacPlatform::GetAppName() { - NSString *appName = [[NSProcessInfo processInfo] processName]; - TString result = [appName UTF8String]; - return result; -} - -void PosixProcess::Cleanup() { - if (FOutputHandle != 0) { - close(FOutputHandle); - FOutputHandle = 0; - } - - if (FInputHandle != 0) { - close(FInputHandle); - FInputHandle = 0; - } - - sigaction(SIGINT, &savintr, (struct sigaction *) 0); - sigaction(SIGQUIT, &savequit, (struct sigaction *) 0); - sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *) 0); -} - -#define PIPE_READ 0 -#define PIPE_WRITE 1 - -bool PosixProcess::Execute(const TString Application, - const std::vector Arguments, bool AWait) { - bool result = false; - - if (FRunning == false) { - FRunning = true; - - int handles[2]; - - if (pipe(handles) == -1) { - return false; - } - - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigemptyset(&savintr.sa_mask); - sigemptyset(&savequit.sa_mask); - sigaction(SIGINT, &sa, &savintr); - sigaction(SIGQUIT, &sa, &savequit); - sigaddset(&sa.sa_mask, SIGCHLD); - sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock); - - FChildPID = fork(); - - // PID returned by vfork is 0 for the child process and the - // PID of the child process for the parent. - if (FChildPID == -1) { - // Error - TString message = PlatformString::Format( - _T("Error: Unable to create process %s"), - Application.data()); - throw Exception(message); - } else if (FChildPID == 0) { - Cleanup(); - TString command = Application; - - for (std::vector::const_iterator iterator = - Arguments.begin(); iterator != Arguments.end(); - iterator++) { - command += TString(_T(" ")) + *iterator; - } -#ifdef DEBUG - printf("%s\n", command.data()); -#endif // DEBUG - - dup2(handles[PIPE_READ], STDIN_FILENO); - dup2(handles[PIPE_WRITE], STDOUT_FILENO); - - close(handles[PIPE_READ]); - close(handles[PIPE_WRITE]); - - execl("/bin/sh", "sh", "-c", command.data(), (char *) 0); - - _exit(127); - } else { - FOutputHandle = handles[PIPE_READ]; - FInputHandle = handles[PIPE_WRITE]; - - if (AWait == true) { - ReadOutput(); - Wait(); - Cleanup(); - FRunning = false; - result = true; - } else { - result = true; - } - } - } - - return result; -} - -void AppendPListArrayToIniFile(NSDictionary *infoDictionary, - IniFile *result, TString Section) { - NSString *sectionKey = - [NSString stringWithUTF8String : PlatformString(Section).toMultibyte()]; - NSDictionary *array = [infoDictionary objectForKey : sectionKey]; - - for (id option in array) { - if ([option isKindOfClass : [NSString class]]) { - TString arg = [option UTF8String]; - - TString name; - TString value; - - if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) { - result->Append(Section, name, value); - } - } - } -} - -void AppendPListDictionaryToIniFile(NSDictionary *infoDictionary, - IniFile *result, TString Section, bool FollowSection = true) { - NSDictionary *dictionary = NULL; - - if (FollowSection == true) { - NSString *sectionKey = [NSString stringWithUTF8String : PlatformString( - Section).toMultibyte()]; - dictionary = [infoDictionary objectForKey : sectionKey]; - } else { - dictionary = infoDictionary; - } - - for (id key in dictionary) { - id option = [dictionary valueForKey : key]; - - if ([key isKindOfClass : [NSString class]] && - [option isKindOfClass : [NSString class]]) { - TString name = [key UTF8String]; - TString value = [option UTF8String]; - result->Append(Section, name, value); - } - } -} - -// Convert parts of the info.plist to the INI format the rest of the jpackage -// uses unless a jpackage config file exists. -ISectionalPropertyContainer* MacPlatform::GetConfigFile(TString FileName) { - IniFile* result = new IniFile(); - if (result == NULL) { - return NULL; - } - - if (UsePListForConfigFile() == false) { - result->LoadFromFile(FileName); - } else { - NSBundle *mainBundle = [NSBundle mainBundle]; - NSDictionary *infoDictionary = [mainBundle infoDictionary]; - std::map keys = GetKeys(); - - // JPackage options. - AppendPListDictionaryToIniFile(infoDictionary, result, - keys[CONFIG_SECTION_APPLICATION], false); - - // jvmargs - AppendPListArrayToIniFile(infoDictionary, result, - keys[CONFIG_SECTION_JAVAOPTIONS]); - - // Generate AppCDS Cache - AppendPListDictionaryToIniFile(infoDictionary, result, - keys[CONFIG_SECTION_APPCDSJAVAOPTIONS]); - AppendPListDictionaryToIniFile(infoDictionary, result, - keys[CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS]); - - // args - AppendPListArrayToIniFile(infoDictionary, result, - keys[CONFIG_SECTION_ARGOPTIONS]); - } - - return result; -} - -TString GetModuleFileNameOSX() { - Dl_info module_info; - if (dladdr(reinterpret_cast (GetModuleFileNameOSX), - &module_info) == 0) { - // Failed to find the symbol we asked for. - return std::string(); - } - return TString(module_info.dli_fname); -} - -TString MacPlatform::GetModuleFileName() { - TString result; - DynamicBuffer buffer(MAX_PATH); - uint32_t size = buffer.GetSize(); - - if (_NSGetExecutablePath(buffer.GetData(), &size) == 0) { - result = FileSystemStringToString(buffer.GetData()); - } - - return result; -} - -bool MacPlatform::IsMainThread() { - bool result = (pthread_main_np() == 1); - return result; -} - -TPlatformNumber MacPlatform::GetMemorySize() { - unsigned long long memory = [[NSProcessInfo processInfo] physicalMemory]; - - // Convert from bytes to megabytes. - TPlatformNumber result = memory / 1048576; - - return result; -} - -std::map MacPlatform::GetKeys() { - std::map keys; - - if (UsePListForConfigFile() == false) { - return Platform::GetKeys(); - } else { - keys.insert(std::map::value_type(CONFIG_VERSION, - _T("app.version"))); - keys.insert(std::map::value_type(CONFIG_MAINJAR_KEY, - _T("JavaMainJarName"))); - keys.insert(std::map::value_type(CONFIG_MAINMODULE_KEY, - _T("JavaMainModuleName"))); - keys.insert(std::map::value_type( - CONFIG_MAINCLASSNAME_KEY, _T("JavaMainClassName"))); - keys.insert(std::map::value_type( - CONFIG_CLASSPATH_KEY, _T("JavaAppClasspath"))); - keys.insert(std::map::value_type(APP_NAME_KEY, - _T("CFBundleName"))); - keys.insert(std::map::value_type(JAVA_RUNTIME_KEY, - _T("JavaRuntime"))); - keys.insert(std::map::value_type(JPACKAGE_APP_DATA_DIR, - _T("CFBundleIdentifier"))); - - keys.insert(std::map::value_type(CONFIG_SPLASH_KEY, - _T("app.splash"))); - keys.insert(std::map::value_type(CONFIG_APP_MEMORY, - _T("app.memory"))); - keys.insert(std::map::value_type(CONFIG_APP_DEBUG, - _T("app.debug"))); - keys.insert(std::map::value_type( - CONFIG_APPLICATION_INSTANCE, _T("app.application.instance"))); - - keys.insert(std::map::value_type( - CONFIG_SECTION_APPLICATION, _T("Application"))); - keys.insert(std::map::value_type( - CONFIG_SECTION_JAVAOPTIONS, _T("JavaOptions"))); - keys.insert(std::map::value_type( - CONFIG_SECTION_APPCDSJAVAOPTIONS, _T("AppCDSJavaOptions"))); - keys.insert(std::map::value_type( - CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS, - _T("AppCDSGenerateCacheJavaOptions"))); - keys.insert(std::map::value_type( - CONFIG_SECTION_ARGOPTIONS, _T("ArgOptions"))); - } - - return keys; -} --- /dev/null 2019-11-18 20:49:01.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/native/libapplauncher/MacPlatform.mm 2019-11-18 20:48:56.462238200 -0500 @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Platform.h" + +#include "MacPlatform.h" +#include "Helpers.h" +#include "Package.h" +#include "PropertyFile.h" +#include "IniFile.h" + +#include +#include +#include +#include +#include + +#import +#import + +#include +#include + +#ifdef __OBJC__ +#import +#endif //__OBJC__ + +#define MAC_JPACKAGE_TMP_DIR \ + "/Library/Application Support/Java/JPackage/tmp" + +NSString* StringToNSString(TString Value) { + NSString* result = [NSString stringWithCString : Value.c_str() + encoding : [NSString defaultCStringEncoding]]; + return result; +} + +FileSystemStringToString::FileSystemStringToString(const TCHAR* value) { + bool release = false; + PlatformString lvalue = PlatformString(value); + Platform& platform = Platform::GetInstance(); + TCHAR* buffer = platform.ConvertFileSystemStringToString(lvalue, release); + FData = buffer; + + if (buffer != NULL && release == true) { + delete[] buffer; + } +} + +FileSystemStringToString::operator TString() { + return FData; +} + +StringToFileSystemString::StringToFileSystemString(const TString &value) { + FRelease = false; + PlatformString lvalue = PlatformString(value); + Platform& platform = Platform::GetInstance(); + FData = platform.ConvertStringToFileSystemString(lvalue, FRelease); +} + +StringToFileSystemString::~StringToFileSystemString() { + if (FRelease == true) { + delete[] FData; + } +} + +StringToFileSystemString::operator TCHAR* () { + return FData; +} + +MacPlatform::MacPlatform(void) : Platform(), PosixPlatform() { +} + +MacPlatform::~MacPlatform(void) { +} + +TString MacPlatform::GetPackageAppDirectory() { + return FilePath::IncludeTrailingSeparator( + GetPackageRootDirectory()) + _T("app"); +} + +TString MacPlatform::GetPackageLauncherDirectory() { + return FilePath::IncludeTrailingSeparator( + GetPackageRootDirectory()) + _T("MacOS"); +} + +TString MacPlatform::GetPackageRuntimeBinDirectory() { + return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + + _T("runtime/Contents/Home/bin"); +} + +bool MacPlatform::UsePListForConfigFile() { + return FilePath::FileExists(GetConfigFileName()) == false; +} + +void MacPlatform::ShowMessage(TString Title, TString Description) { + NSString *ltitle = StringToNSString(Title); + NSString *ldescription = StringToNSString(Description); + + NSLog(@"%@:%@", ltitle, ldescription); +} + +void MacPlatform::ShowMessage(TString Description) { + TString appname = GetModuleFileName(); + appname = FilePath::ExtractFileName(appname); + ShowMessage(appname, Description); +} + +TString MacPlatform::getTmpDirString() { + return TString(MAC_JPACKAGE_TMP_DIR); +} + +TCHAR* MacPlatform::ConvertStringToFileSystemString(TCHAR* Source, + bool &release) { + TCHAR* result = NULL; + release = false; + CFStringRef StringRef = CFStringCreateWithCString(kCFAllocatorDefault, + Source, kCFStringEncodingUTF8); + + if (StringRef != NULL) { + @ try { + CFIndex length = + CFStringGetMaximumSizeOfFileSystemRepresentation(StringRef); + result = new char[length + 1]; + if (result != NULL) { + if (CFStringGetFileSystemRepresentation(StringRef, + result, length)) { + release = true; + } else { + delete[] result; + result = NULL; + } + } + } + @finally + { + CFRelease(StringRef); + } + } + + return result; +} + +TCHAR* MacPlatform::ConvertFileSystemStringToString(TCHAR* Source, + bool &release) { + TCHAR* result = NULL; + release = false; + CFStringRef StringRef = CFStringCreateWithFileSystemRepresentation( + kCFAllocatorDefault, Source); + + if (StringRef != NULL) { + @ try { + CFIndex length = CFStringGetLength(StringRef); + + if (length > 0) { + CFIndex maxSize = CFStringGetMaximumSizeForEncoding( + length, kCFStringEncodingUTF8); + + result = new char[maxSize + 1]; + if (result != NULL) { + if (CFStringGetCString(StringRef, result, maxSize, + kCFStringEncodingUTF8) == true) { + release = true; + } else { + delete[] result; + result = NULL; + } + } + } + } + @finally + { + CFRelease(StringRef); + } + } + + return result; +} + +TString MacPlatform::GetPackageRootDirectory() { + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *mainBundlePath = [mainBundle bundlePath]; + NSString *contentsPath = + [mainBundlePath stringByAppendingString : @"/Contents"]; + TString result = [contentsPath UTF8String]; + return result; +} + +TString MacPlatform::GetAppDataDirectory() { + TString result; + NSArray *paths = NSSearchPathForDirectoriesInDomains( + NSApplicationSupportDirectory, NSUserDomainMask, YES); + NSString *applicationSupportDirectory = [paths firstObject]; + result = [applicationSupportDirectory UTF8String]; + return result; +} + +TString MacPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) { + TString result; + + // first try lib/, then lib/jli + result = FilePath::IncludeTrailingSeparator(RuntimePath) + + _T("Contents/Home/lib/libjli.dylib"); + + if (FilePath::FileExists(result) == false) { + result = FilePath::IncludeTrailingSeparator(RuntimePath) + + _T("Contents/Home/lib/jli/libjli.dylib"); + + if (FilePath::FileExists(result) == false) { + // cannot find + NSLog(@"Cannot find libjli.dysym!"); + result = _T(""); + } + } + + return result; +} + +TString MacPlatform::GetAppName() { + NSString *appName = [[NSProcessInfo processInfo] processName]; + TString result = [appName UTF8String]; + return result; +} + +void PosixProcess::Cleanup() { + if (FOutputHandle != 0) { + close(FOutputHandle); + FOutputHandle = 0; + } + + if (FInputHandle != 0) { + close(FInputHandle); + FInputHandle = 0; + } + + sigaction(SIGINT, &savintr, (struct sigaction *) 0); + sigaction(SIGQUIT, &savequit, (struct sigaction *) 0); + sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *) 0); +} + +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +bool PosixProcess::Execute(const TString Application, + const std::vector Arguments, bool AWait) { + bool result = false; + + if (FRunning == false) { + FRunning = true; + + int handles[2]; + + if (pipe(handles) == -1) { + return false; + } + + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigemptyset(&savintr.sa_mask); + sigemptyset(&savequit.sa_mask); + sigaction(SIGINT, &sa, &savintr); + sigaction(SIGQUIT, &sa, &savequit); + sigaddset(&sa.sa_mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock); + + FChildPID = fork(); + + // PID returned by vfork is 0 for the child process and the + // PID of the child process for the parent. + if (FChildPID == -1) { + // Error + TString message = PlatformString::Format( + _T("Error: Unable to create process %s"), + Application.data()); + throw Exception(message); + } else if (FChildPID == 0) { + Cleanup(); + TString command = Application; + + for (std::vector::const_iterator iterator = + Arguments.begin(); iterator != Arguments.end(); + iterator++) { + command += TString(_T(" ")) + *iterator; + } +#ifdef DEBUG + printf("%s\n", command.data()); +#endif // DEBUG + + dup2(handles[PIPE_READ], STDIN_FILENO); + dup2(handles[PIPE_WRITE], STDOUT_FILENO); + + close(handles[PIPE_READ]); + close(handles[PIPE_WRITE]); + + execl("/bin/sh", "sh", "-c", command.data(), (char *) 0); + + _exit(127); + } else { + FOutputHandle = handles[PIPE_READ]; + FInputHandle = handles[PIPE_WRITE]; + + if (AWait == true) { + ReadOutput(); + Wait(); + Cleanup(); + FRunning = false; + result = true; + } else { + result = true; + } + } + } + + return result; +} + +void AppendPListArrayToIniFile(NSDictionary *infoDictionary, + IniFile *result, TString Section) { + NSString *sectionKey = + [NSString stringWithUTF8String : PlatformString(Section).toMultibyte()]; + NSDictionary *array = [infoDictionary objectForKey : sectionKey]; + + for (id option in array) { + if ([option isKindOfClass : [NSString class]]) { + TString arg = [option UTF8String]; + + TString name; + TString value; + + if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) { + result->Append(Section, name, value); + } + } + } +} + +void AppendPListDictionaryToIniFile(NSDictionary *infoDictionary, + IniFile *result, TString Section, bool FollowSection = true) { + NSDictionary *dictionary = NULL; + + if (FollowSection == true) { + NSString *sectionKey = [NSString stringWithUTF8String : PlatformString( + Section).toMultibyte()]; + dictionary = [infoDictionary objectForKey : sectionKey]; + } else { + dictionary = infoDictionary; + } + + for (id key in dictionary) { + id option = [dictionary valueForKey : key]; + + if ([key isKindOfClass : [NSString class]] && + [option isKindOfClass : [NSString class]]) { + TString name = [key UTF8String]; + TString value = [option UTF8String]; + result->Append(Section, name, value); + } + } +} + +// Convert parts of the info.plist to the INI format the rest of the jpackage +// uses unless a jpackage config file exists. +ISectionalPropertyContainer* MacPlatform::GetConfigFile(TString FileName) { + IniFile* result = new IniFile(); + if (result == NULL) { + return NULL; + } + + if (UsePListForConfigFile() == false) { + result->LoadFromFile(FileName); + } else { + NSBundle *mainBundle = [NSBundle mainBundle]; + NSDictionary *infoDictionary = [mainBundle infoDictionary]; + std::map keys = GetKeys(); + + // JPackage options. + AppendPListDictionaryToIniFile(infoDictionary, result, + keys[CONFIG_SECTION_APPLICATION], false); + + // jvmargs + AppendPListArrayToIniFile(infoDictionary, result, + keys[CONFIG_SECTION_JAVAOPTIONS]); + + // Generate AppCDS Cache + AppendPListDictionaryToIniFile(infoDictionary, result, + keys[CONFIG_SECTION_APPCDSJAVAOPTIONS]); + AppendPListDictionaryToIniFile(infoDictionary, result, + keys[CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS]); + + // args + AppendPListArrayToIniFile(infoDictionary, result, + keys[CONFIG_SECTION_ARGOPTIONS]); + } + + return result; +} + +TString GetModuleFileNameOSX() { + Dl_info module_info; + if (dladdr(reinterpret_cast (GetModuleFileNameOSX), + &module_info) == 0) { + // Failed to find the symbol we asked for. + return std::string(); + } + return TString(module_info.dli_fname); +} + +TString MacPlatform::GetModuleFileName() { + TString result; + DynamicBuffer buffer(MAX_PATH); + uint32_t size = buffer.GetSize(); + + if (_NSGetExecutablePath(buffer.GetData(), &size) == 0) { + result = FileSystemStringToString(buffer.GetData()); + } + + return result; +} + +bool MacPlatform::IsMainThread() { + bool result = (pthread_main_np() == 1); + return result; +} + +TPlatformNumber MacPlatform::GetMemorySize() { + unsigned long long memory = [[NSProcessInfo processInfo] physicalMemory]; + + // Convert from bytes to megabytes. + TPlatformNumber result = memory / 1048576; + + return result; +} + +std::map MacPlatform::GetKeys() { + std::map keys; + + if (UsePListForConfigFile() == false) { + return Platform::GetKeys(); + } else { + keys.insert(std::map::value_type(CONFIG_VERSION, + _T("app.version"))); + keys.insert(std::map::value_type(CONFIG_MAINJAR_KEY, + _T("JavaMainJarName"))); + keys.insert(std::map::value_type(CONFIG_MAINMODULE_KEY, + _T("JavaMainModuleName"))); + keys.insert(std::map::value_type( + CONFIG_MAINCLASSNAME_KEY, _T("JavaMainClassName"))); + keys.insert(std::map::value_type( + CONFIG_CLASSPATH_KEY, _T("JavaAppClasspath"))); + keys.insert(std::map::value_type(APP_NAME_KEY, + _T("CFBundleName"))); + keys.insert(std::map::value_type(JAVA_RUNTIME_KEY, + _T("JavaRuntime"))); + keys.insert(std::map::value_type(JPACKAGE_APP_DATA_DIR, + _T("CFBundleIdentifier"))); + + keys.insert(std::map::value_type(CONFIG_SPLASH_KEY, + _T("app.splash"))); + keys.insert(std::map::value_type(CONFIG_APP_MEMORY, + _T("app.memory"))); + keys.insert(std::map::value_type(CONFIG_APP_DEBUG, + _T("app.debug"))); + keys.insert(std::map::value_type( + CONFIG_APPLICATION_INSTANCE, _T("app.application.instance"))); + + keys.insert(std::map::value_type( + CONFIG_SECTION_APPLICATION, _T("Application"))); + keys.insert(std::map::value_type( + CONFIG_SECTION_JAVAOPTIONS, _T("JavaOptions"))); + keys.insert(std::map::value_type( + CONFIG_SECTION_APPCDSJAVAOPTIONS, _T("AppCDSJavaOptions"))); + keys.insert(std::map::value_type( + CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS, + _T("AppCDSGenerateCacheJavaOptions"))); + keys.insert(std::map::value_type( + CONFIG_SECTION_ARGOPTIONS, _T("ArgOptions"))); + } + + return keys; +} --- old/src/jdk.jpackage/macosx/native/libapplauncher/PlatformDefs.h 2019-11-18 20:49:21.153663700 -0500 +++ /dev/null 2019-11-18 20:49:22.000000000 -0500 @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef PLATFORM_DEFS_H -#define PLATFORM_DEFS_H - -#include -#include -#include -#include -#include -#include - -using namespace std; - -#ifndef MAC -#define MAC -#endif - -#define _T(x) x - -typedef char TCHAR; -typedef std::string TString; -#define StringLength strlen - -typedef unsigned long DWORD; - -#define TRAILING_PATHSEPARATOR '/' -#define BAD_TRAILING_PATHSEPARATOR '\\' -#define PATH_SEPARATOR ':' -#define BAD_PATH_SEPARATOR ';' -#define MAX_PATH 1000 - -typedef long TPlatformNumber; -typedef pid_t TProcessID; - -#define HMODULE void* - -typedef void* Module; -typedef void* Procedure; - - -// StringToFileSystemString is a stack object. It's usage is -// simply inline to convert a -// TString to a file system string. Example: -// -// return dlopen(StringToFileSystemString(FileName), RTLD_LAZY); -// -class StringToFileSystemString { - // Prohibit Heap-Based StringToFileSystemString -private: - static void *operator new(size_t size); - static void operator delete(void *ptr); - -private: - TCHAR* FData; - bool FRelease; - -public: - StringToFileSystemString(const TString &value); - ~StringToFileSystemString(); - - operator TCHAR* (); -}; - - -// FileSystemStringToString is a stack object. It's usage is -// simply inline to convert a -// file system string to a TString. Example: -// -// DynamicBuffer buffer(MAX_PATH); -// if (readlink("/proc/self/exe", buffer.GetData(), MAX_PATH) != -1) -// result = FileSystemStringToString(buffer.GetData()); -// -class FileSystemStringToString { - // Prohibit Heap-Based FileSystemStringToString -private: - static void *operator new(size_t size); - static void operator delete(void *ptr); - -private: - TString FData; - -public: - FileSystemStringToString(const TCHAR* value); - - operator TString (); -}; - -#endif // PLATFORM_DEFS_H --- /dev/null 2019-11-18 20:49:22.000000000 -0500 +++ new/src/jdk.incubator.jpackage/macosx/native/libapplauncher/PlatformDefs.h 2019-11-18 20:49:17.449665000 -0500 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef PLATFORM_DEFS_H +#define PLATFORM_DEFS_H + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#ifndef MAC +#define MAC +#endif + +#define _T(x) x + +typedef char TCHAR; +typedef std::string TString; +#define StringLength strlen + +typedef unsigned long DWORD; + +#define TRAILING_PATHSEPARATOR '/' +#define BAD_TRAILING_PATHSEPARATOR '\\' +#define PATH_SEPARATOR ':' +#define BAD_PATH_SEPARATOR ';' +#define MAX_PATH 1000 + +typedef long TPlatformNumber; +typedef pid_t TProcessID; + +#define HMODULE void* + +typedef void* Module; +typedef void* Procedure; + + +// StringToFileSystemString is a stack object. It's usage is +// simply inline to convert a +// TString to a file system string. Example: +// +// return dlopen(StringToFileSystemString(FileName), RTLD_LAZY); +// +class StringToFileSystemString { + // Prohibit Heap-Based StringToFileSystemString +private: + static void *operator new(size_t size); + static void operator delete(void *ptr); + +private: + TCHAR* FData; + bool FRelease; + +public: + StringToFileSystemString(const TString &value); + ~StringToFileSystemString(); + + operator TCHAR* (); +}; + + +// FileSystemStringToString is a stack object. It's usage is +// simply inline to convert a +// file system string to a TString. Example: +// +// DynamicBuffer buffer(MAX_PATH); +// if (readlink("/proc/self/exe", buffer.GetData(), MAX_PATH) != -1) +// result = FileSystemStringToString(buffer.GetData()); +// +class FileSystemStringToString { + // Prohibit Heap-Based FileSystemStringToString +private: + static void *operator new(size_t size); + static void operator delete(void *ptr); + +private: + TString FData; + +public: + FileSystemStringToString(const TCHAR* value); + + operator TString (); +}; + +#endif // PLATFORM_DEFS_H --- /dev/null 2019-11-18 20:49:31.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/ToolProviderFactory.java 2019-11-18 20:49:28.476601700 -0500 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage; + +import jdk.incubator.jpackage.internal.JPackageToolProvider; +import java.io.PrintWriter; +import java.util.Optional; +import java.util.spi.ToolProvider; + +/** + * A factory class to obtain a {@linkplain ToolProvider tool provider} + * for the incubating {@code jpackage} tool. + * + * It is planned to implement {@code jpackage} tool as a service provider + * to {@link ToolProvider} in a future release at which point + * {@link ToolProvider#findFirst} can be used to look up jpackage tool. + * + * @since 14 + */ + +public class ToolProviderFactory { + + private static ToolProvider provider = new JPackageToolProvider(); + + // Prevent creating an instance of this class + private ToolProviderFactory() { + } + + /** + * Returns an {@link Optional} containing the {@code ToolProvider} + * if the given toolname is "jpackage". Returns an empty + * {@code Optional} if the given toolname is not "jpackage". + * + * @param toolname {@code String} name of tool to look for. + * @return an {@link Optional} containing the {@code ToolPovider} + * + * @since 14 + */ + public static Optional findFirst(String toolName) { + if ("jpackage".equals(toolName)) { + return Optional.of(provider); + } else { + return Optional.empty(); + } + } + +} --- /dev/null 2019-11-18 20:49:42.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/AbstractAppImageBuilder.java 2019-11-18 20:49:39.580984100 -0500 @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.ArrayList; + +import jdk.incubator.jpackage.internal.resources.ResourceLocator; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +/* + * AbstractAppImageBuilder + * This is sub-classed by each of the platform dependent AppImageBuilder + * classes, and contains resource processing code common to all platforms. + */ + +public abstract class AbstractAppImageBuilder { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MainResources"); + + private final Path root; + + public AbstractAppImageBuilder(Map unused, Path root) { + this.root = root; + } + + public InputStream getResourceAsStream(String name) { + return ResourceLocator.class.getResourceAsStream(name); + } + + public abstract void prepareApplicationFiles( + Map params) throws IOException; + public abstract void prepareJreFiles( + Map params) throws IOException; + public abstract Path getAppDir(); + public abstract Path getAppModsDir(); + + public Path getRuntimeRoot() { + return this.root; + } + + protected void copyEntry(Path appDir, File srcdir, String fname) + throws IOException { + Path dest = appDir.resolve(fname); + Files.createDirectories(dest.getParent()); + File src = new File(srcdir, fname); + if (src.isDirectory()) { + IOUtils.copyRecursive(src.toPath(), dest); + } else { + Files.copy(src.toPath(), dest); + } + } + + public void writeCfgFile(Map params, + File cfgFileName) throws IOException { + cfgFileName.getParentFile().mkdirs(); + cfgFileName.delete(); + File mainJar = JLinkBundlerHelper.getMainJar(params); + ModFile.ModType mainJarType = ModFile.ModType.Unknown; + + if (mainJar != null) { + mainJarType = new ModFile(mainJar).getModType(); + } + + String mainModule = StandardBundlerParam.MODULE.fetchFrom(params); + + try (PrintStream out = new PrintStream(cfgFileName)) { + + out.println("[Application]"); + out.println("app.name=" + APP_NAME.fetchFrom(params)); + out.println("app.version=" + VERSION.fetchFrom(params)); + out.println("app.runtime=" + getCfgRuntimeDir()); + out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); + out.println("app.classpath=" + + getCfgClassPath(CLASSPATH.fetchFrom(params))); + + // The main app is required to be a jar, modular or unnamed. + if (mainModule != null && + (mainJarType == ModFile.ModType.Unknown || + mainJarType == ModFile.ModType.ModularJar)) { + out.println("app.mainmodule=" + mainModule); + } else { + String mainClass = + StandardBundlerParam.MAIN_CLASS.fetchFrom(params); + // If the app is contained in an unnamed jar then launch it the + // legacy way and the main class string must be + // of the format com/foo/Main + if (mainJar != null) { + out.println("app.mainjar=" + getCfgAppDir() + + mainJar.toPath().getFileName().toString()); + } + if (mainClass != null) { + out.println("app.mainclass=" + + mainClass.replace("\\", "/")); + } + } + + out.println(); + out.println("[JavaOptions]"); + List jvmargs = JAVA_OPTIONS.fetchFrom(params); + for (String arg : jvmargs) { + out.println(arg); + } + Path modsDir = getAppModsDir(); + + if (modsDir != null && modsDir.toFile().exists()) { + out.println("--module-path"); + out.println(getCfgAppDir().replace("\\","/") + "mods"); + } + + out.println(); + out.println("[ArgOptions]"); + List args = ARGUMENTS.fetchFrom(params); + for (String arg : args) { + if (arg.endsWith("=") && + (arg.indexOf("=") == arg.lastIndexOf("="))) { + out.print(arg.substring(0, arg.length() - 1)); + out.println("\\="); + } else { + out.println(arg); + } + } + } + } + + File getRuntimeImageDir(File runtimeImageTop) { + return runtimeImageTop; + } + + protected String getCfgAppDir() { + return "$ROOTDIR" + File.separator + + getAppDir().getFileName() + File.separator; + } + + protected String getCfgRuntimeDir() { + return "$ROOTDIR" + File.separator + "runtime"; + } + + String getCfgClassPath(String classpath) { + String cfgAppDir = getCfgAppDir(); + + StringBuilder sb = new StringBuilder(); + for (String path : classpath.split("[:;]")) { + if (path.length() > 0) { + sb.append(cfgAppDir); + sb.append(path); + sb.append(File.pathSeparator); + } + } + if (sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } + return sb.toString(); + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/InvalidBundlerParamException.java 2019-11-18 20:49:53.938187300 -0500 +++ /dev/null 2019-11-18 20:49:55.000000000 -0500 @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -public class InvalidBundlerParamException extends RuntimeException { - private static final long serialVersionUID = 1L; - public InvalidBundlerParamException(String message) { - super(message); - } -} --- /dev/null 2019-11-18 20:49:55.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/AbstractBundler.java 2019-11-18 20:49:50.432446000 -0500 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + + +/** + * AbstractBundler + * + * This is the base class all bundlers extend from. + * It contains methods and parameters common to all bundlers. + * The concrete implementations are in the platform specific bundlers. + */ +abstract class AbstractBundler implements Bundler { + + static final BundlerParamInfo IMAGES_ROOT = + new StandardBundlerParam<>( + "imagesRoot", + File.class, + params -> new File( + StandardBundlerParam.TEMP_ROOT.fetchFrom(params), "images"), + (s, p) -> null); + + @Override + public String toString() { + return getName(); + } + + @Override + public void cleanup(Map params) { + try { + IOUtils.deleteRecursive( + StandardBundlerParam.TEMP_ROOT.fetchFrom(params)); + } catch (IOException e) { + Log.verbose(e.getMessage()); + } + } +} --- /dev/null 2019-11-18 20:50:15.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/AbstractImageBundler.java 2019-11-18 20:50:11.935044500 -0500 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.text.MessageFormat; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.io.File; +import java.io.IOException; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +/** + * AbstractImageBundler + * + * This is the base class for each of the Application Image Bundlers. + * + * It contains methods and parameters common to all Image Bundlers. + * + * Application Image Bundlers are created in "create-app-image" mode, + * or as an intermediate step in "create-installer" mode. + * + * The concrete implementations are in the platform specific Bundlers. + */ +public abstract class AbstractImageBundler extends AbstractBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MainResources"); + + public void imageBundleValidation(Map params) + throws ConfigException { + StandardBundlerParam.validateMainClassInfoFromAppResources(params); + + } + + protected File createRoot(Map params, + File outputDirectory, boolean dependentTask, String name) + throws PackagerException { + + IOUtils.writableOutputDir(outputDirectory.toPath()); + + if (!dependentTask) { + Log.verbose(MessageFormat.format( + I18N.getString("message.creating-app-bundle"), + name, outputDirectory.getAbsolutePath())); + } + + // NAME will default to CLASS, so the real problem is no MAIN_CLASS + if (name == null) { + throw new PackagerException("ERR_NoMainClass"); + } + + // Create directory structure + File rootDirectory = new File(outputDirectory, name); + + if (rootDirectory.exists()) { + throw new PackagerException("error.root-exists", + rootDirectory.getAbsolutePath()); + } + + rootDirectory.mkdirs(); + + return rootDirectory; + } + +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java 2019-11-18 20:50:26.402467600 -0500 +++ /dev/null 2019-11-18 20:50:27.000000000 -0500 @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.io.File; -import jdk.jpackage.internal.Arguments.CLIOptions; - -/* - * AddLauncherArguments - * - * Processes a add-launcher properties file to create the Map of - * bundle params applicable to the add-launcher: - * - * BundlerParams p = (new AddLauncherArguments(file)).getLauncherMap(); - * - * A add-launcher is another executable program generated by either the - * create-app-image mode or the create-installer mode. - * The add-launcher may be the same program with different configuration, - * or a completely different program created from the same files. - * - * There may be multiple add-launchers, each created by using the - * command line arg "--add-launcher - * - * The add-launcher properties file may have any of: - * - * appVersion - * module - * add-modules - * main-jar - * main-class - * icon - * arguments - * java-options - * win-console - * - */ -class AddLauncherArguments { - - private final String name; - private final String filename; - private Map allArgs; - private Map bundleParams; - - AddLauncherArguments(String name, String filename) { - this.name = name; - this.filename = filename; - } - - private void initLauncherMap() { - if (bundleParams != null) { - return; - } - - allArgs = Arguments.getPropertiesFromFile(filename); - allArgs.put(CLIOptions.NAME.getId(), name); - - bundleParams = new HashMap<>(); - String mainJar = getOptionValue(CLIOptions.MAIN_JAR); - String mainClass = getOptionValue(CLIOptions.APPCLASS); - String module = getOptionValue(CLIOptions.MODULE); - - if (module != null && mainClass != null) { - putUnlessNull(bundleParams, CLIOptions.MODULE.getId(), - module + "/" + mainClass); - } else if (module != null) { - putUnlessNull(bundleParams, CLIOptions.MODULE.getId(), - module); - } else { - putUnlessNull(bundleParams, CLIOptions.MAIN_JAR.getId(), - mainJar); - putUnlessNull(bundleParams, CLIOptions.APPCLASS.getId(), - mainClass); - } - - putUnlessNull(bundleParams, CLIOptions.NAME.getId(), - getOptionValue(CLIOptions.NAME)); - - putUnlessNull(bundleParams, CLIOptions.VERSION.getId(), - getOptionValue(CLIOptions.VERSION)); - - putUnlessNull(bundleParams, - CLIOptions.ADD_MODULES.getId(), - getOptionValue(CLIOptions.ADD_MODULES)); - - putUnlessNull(bundleParams, - CLIOptions.WIN_CONSOLE_HINT.getId(), - getOptionValue(CLIOptions.WIN_CONSOLE_HINT)); - - String value = getOptionValue(CLIOptions.ICON); - putUnlessNull(bundleParams, CLIOptions.ICON.getId(), - (value == null) ? null : new File(value)); - - String argumentStr = getOptionValue(CLIOptions.ARGUMENTS); - putUnlessNullOrEmpty(bundleParams, - CLIOptions.ARGUMENTS.getId(), - Arguments.getArgumentList(argumentStr)); - - String jvmargsStr = getOptionValue(CLIOptions.JAVA_OPTIONS); - putUnlessNullOrEmpty(bundleParams, - CLIOptions.JAVA_OPTIONS.getId(), - Arguments.getArgumentList(jvmargsStr)); - } - - private String getOptionValue(CLIOptions option) { - if (option == null || allArgs == null) { - return null; - } - - String id = option.getId(); - - if (allArgs.containsKey(id)) { - return allArgs.get(id); - } - - return null; - } - - Map getLauncherMap() { - initLauncherMap(); - return bundleParams; - } - - private void putUnlessNull(Map params, - String param, Object value) { - if (value != null) { - params.put(param, value); - } - } - - private void putUnlessNullOrEmpty(Map params, - String param, Collection value) { - if (value != null && !value.isEmpty()) { - params.put(param, value); - } - } - - private void putUnlessNullOrEmpty(Map params, - String param, Map value) { - if (value != null && !value.isEmpty()) { - params.put(param, value); - } - } - - static Map merge( - Map original, - Map additional) { - Map tmp = new HashMap<>(original); - if (additional.containsKey("module")) { - tmp.remove("main-jar"); - tmp.remove("main-class"); - } else if (additional.containsKey("main-jar")) { - tmp.remove("module"); - // should we only remove add-modules when it wasn't actually passed - // but was inferred or empty ? - tmp.remove("add-modules"); - } - tmp.putAll(additional); - return tmp; - } - -} --- /dev/null 2019-11-18 20:50:28.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/AddLauncherArguments.java 2019-11-18 20:50:23.029823900 -0500 @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.io.File; +import jdk.incubator.jpackage.internal.Arguments.CLIOptions; + +/* + * AddLauncherArguments + * + * Processes a add-launcher properties file to create the Map of + * bundle params applicable to the add-launcher: + * + * BundlerParams p = (new AddLauncherArguments(file)).getLauncherMap(); + * + * A add-launcher is another executable program generated by either the + * create-app-image mode or the create-installer mode. + * The add-launcher may be the same program with different configuration, + * or a completely different program created from the same files. + * + * There may be multiple add-launchers, each created by using the + * command line arg "--add-launcher + * + * The add-launcher properties file may have any of: + * + * appVersion + * module + * main-jar + * main-class + * icon + * arguments + * java-options + * win-console + * linux-app-category + * + */ +class AddLauncherArguments { + + private final String name; + private final String filename; + private Map allArgs; + private Map bundleParams; + + AddLauncherArguments(String name, String filename) { + this.name = name; + this.filename = filename; + } + + private void initLauncherMap() { + if (bundleParams != null) { + return; + } + + allArgs = Arguments.getPropertiesFromFile(filename); + allArgs.put(CLIOptions.NAME.getId(), name); + + bundleParams = new HashMap<>(); + String mainJar = getOptionValue(CLIOptions.MAIN_JAR); + String mainClass = getOptionValue(CLIOptions.APPCLASS); + String module = getOptionValue(CLIOptions.MODULE); + + if (module != null && mainClass != null) { + putUnlessNull(bundleParams, CLIOptions.MODULE.getId(), + module + "/" + mainClass); + } else if (module != null) { + putUnlessNull(bundleParams, CLIOptions.MODULE.getId(), + module); + } else { + putUnlessNull(bundleParams, CLIOptions.MAIN_JAR.getId(), + mainJar); + putUnlessNull(bundleParams, CLIOptions.APPCLASS.getId(), + mainClass); + } + + putUnlessNull(bundleParams, CLIOptions.NAME.getId(), + getOptionValue(CLIOptions.NAME)); + + putUnlessNull(bundleParams, CLIOptions.VERSION.getId(), + getOptionValue(CLIOptions.VERSION)); + + putUnlessNull(bundleParams, CLIOptions.RELEASE.getId(), + getOptionValue(CLIOptions.RELEASE)); + + putUnlessNull(bundleParams, CLIOptions.LINUX_CATEGORY.getId(), + getOptionValue(CLIOptions.LINUX_CATEGORY)); + + putUnlessNull(bundleParams, + CLIOptions.WIN_CONSOLE_HINT.getId(), + getOptionValue(CLIOptions.WIN_CONSOLE_HINT)); + + String value = getOptionValue(CLIOptions.ICON); + putUnlessNull(bundleParams, CLIOptions.ICON.getId(), + (value == null) ? null : new File(value)); + + // "arguments" and "java-options" even if value is null: + if (allArgs.containsKey(CLIOptions.ARGUMENTS.getId())) { + String argumentStr = getOptionValue(CLIOptions.ARGUMENTS); + bundleParams.put(CLIOptions.ARGUMENTS.getId(), + Arguments.getArgumentList(argumentStr)); + } + + if (allArgs.containsKey(CLIOptions.JAVA_OPTIONS.getId())) { + String jvmargsStr = getOptionValue(CLIOptions.JAVA_OPTIONS); + bundleParams.put(CLIOptions.JAVA_OPTIONS.getId(), + Arguments.getArgumentList(jvmargsStr)); + } + } + + private String getOptionValue(CLIOptions option) { + if (option == null || allArgs == null) { + return null; + } + + String id = option.getId(); + + if (allArgs.containsKey(id)) { + return allArgs.get(id); + } + + return null; + } + + Map getLauncherMap() { + initLauncherMap(); + return bundleParams; + } + + private void putUnlessNull(Map params, + String param, Object value) { + if (value != null) { + params.put(param, value); + } + } + + static Map merge( + Map original, + Map additional) { + Map tmp = new HashMap<>(original); + if (additional.containsKey(CLIOptions.MODULE.getId())) { + tmp.remove(CLIOptions.MAIN_JAR.getId()); + tmp.remove(CLIOptions.APPCLASS.getId()); + } else if (additional.containsKey(CLIOptions.MAIN_JAR.getId())) { + tmp.remove(CLIOptions.MODULE.getId()); + } + if (additional.containsKey(CLIOptions.ARGUMENTS.getId())) { + // if add launcher properties file contains "arguments", even with + // null value, disregard the "arguments" from command line + tmp.remove(CLIOptions.ARGUMENTS.getId()); + } + if (additional.containsKey(CLIOptions.JAVA_OPTIONS.getId())) { + // same thing for java-options + tmp.remove(CLIOptions.JAVA_OPTIONS.getId()); + } + tmp.putAll(additional); + return tmp; + } + +} --- /dev/null 2019-11-18 20:50:47.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/AppImageFile.java 2019-11-18 20:50:43.981554100 -0500 @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +public class AppImageFile { + + // These values will be loaded from AppImage xml file. + private final String creatorVersion; + private final String creatorPlatform; + private final String launcherName; + private final List addLauncherNames; + + private final static String FILENAME = ".jpackage.xml"; + + private final static Map PLATFORM_LABELS = Map.of( + Platform.LINUX, "linux", Platform.WINDOWS, "windows", Platform.MAC, + "macOS"); + + + private AppImageFile() { + this(null, null, null, null); + } + + private AppImageFile(String launcherName, List addLauncherNames, + String creatorVersion, String creatorPlatform) { + this.launcherName = launcherName; + this.addLauncherNames = addLauncherNames; + this.creatorVersion = creatorVersion; + this.creatorPlatform = creatorPlatform; + } + + /** + * Returns list of additional launchers configured for the application. + * Each item in the list is not null or empty string. + * Returns empty list for application without additional launchers. + */ + List getAddLauncherNames() { + return addLauncherNames; + } + + /** + * Returns main application launcher name. Never returns null or empty value. + */ + String getLauncherName() { + return launcherName; + } + + void verifyCompatible() throws ConfigException { + // Just do nothing for now. + } + + /** + * Returns path to application image info file. + * @param appImageDir - path to application image + */ + public static Path getPathInAppImage(Path appImageDir) { + return appImageDir.resolve(FILENAME); + } + + /** + * Saves file with application image info in application image. + * @param appImageDir - path to application image + * @throws IOException + */ + static void save(Path appImageDir, Map params) + throws IOException { + IOUtils.createXml(getPathInAppImage(appImageDir), xml -> { + xml.writeStartElement("jpackage-state"); + xml.writeAttribute("version", getVersion()); + xml.writeAttribute("platform", getPlatform()); + + xml.writeStartElement("main-launcher"); + xml.writeCharacters(APP_NAME.fetchFrom(params)); + xml.writeEndElement(); + + List> addLaunchers = + ADD_LAUNCHERS.fetchFrom(params); + + for (int i = 0; i < addLaunchers.size(); i++) { + Map sl = addLaunchers.get(i); + xml.writeStartElement("add-launcher"); + xml.writeCharacters(APP_NAME.fetchFrom(sl)); + xml.writeEndElement(); + } + }); + } + + /** + * Loads application image info from application image. + * @param appImageDir - path to application image + * @return valid info about application image or null + * @throws IOException + */ + static AppImageFile load(Path appImageDir) throws IOException { + try { + Path path = getPathInAppImage(appImageDir); + DocumentBuilderFactory dbf = + DocumentBuilderFactory.newDefaultInstance(); + dbf.setFeature( + "http://apache.org/xml/features/nonvalidating/load-external-dtd", + false); + DocumentBuilder b = dbf.newDocumentBuilder(); + Document doc = b.parse(new FileInputStream(path.toFile())); + + XPath xPath = XPathFactory.newInstance().newXPath(); + + String mainLauncher = xpathQueryNullable(xPath, + "/jpackage-state/main-launcher/text()", doc); + if (mainLauncher == null) { + // No main launcher, this is fatal. + return new AppImageFile(); + } + + List addLaunchers = new ArrayList(); + + String platform = xpathQueryNullable(xPath, + "/jpackage-state/@platform", doc); + + String version = xpathQueryNullable(xPath, + "/jpackage-state/@version", doc); + + NodeList launcherNameNodes = (NodeList) xPath.evaluate( + "/jpackage-state/add-launcher/text()", doc, + XPathConstants.NODESET); + + for (int i = 0; i != launcherNameNodes.getLength(); i++) { + addLaunchers.add(launcherNameNodes.item(i).getNodeValue()); + } + + AppImageFile file = new AppImageFile( + mainLauncher, addLaunchers, version, platform); + if (!file.isValid()) { + file = new AppImageFile(); + } + return file; + } catch (ParserConfigurationException | SAXException ex) { + // Let caller sort this out + throw new IOException(ex); + } catch (XPathExpressionException ex) { + // This should never happen as XPath expressions should be correct + throw new RuntimeException(ex); + } + } + + /** + * Returns list of launcher names configured for the application. + * The first item in the returned list is main launcher name. + * Following items in the list are names of additional launchers. + */ + static List getLauncherNames(Path appImageDir, + Map params) { + List launchers = new ArrayList<>(); + try { + AppImageFile appImageInfo = AppImageFile.load(appImageDir); + if (appImageInfo != null) { + launchers.add(appImageInfo.getLauncherName()); + launchers.addAll(appImageInfo.getAddLauncherNames()); + return launchers; + } + } catch (IOException ioe) { + Log.verbose(ioe); + } + + launchers.add(APP_NAME.fetchFrom(params)); + ADD_LAUNCHERS.fetchFrom(params).stream().map(APP_NAME::fetchFrom).forEach( + launchers::add); + return launchers; + } + + private static String xpathQueryNullable(XPath xPath, String xpathExpr, + Document xml) throws XPathExpressionException { + NodeList nodes = (NodeList) xPath.evaluate(xpathExpr, xml, + XPathConstants.NODESET); + if (nodes != null && nodes.getLength() > 0) { + return nodes.item(0).getNodeValue(); + } + return null; + } + + private static String getVersion() { + return System.getProperty("java.version"); + } + + private static String getPlatform() { + return PLATFORM_LABELS.get(Platform.getPlatform()); + } + + private boolean isValid() { + if (launcherName == null || launcherName.length() == 0 || + addLauncherNames.indexOf("") != -1) { + // Some launchers have empty names. This is invalid. + return false; + } + + // Add more validation. + + return true; + } + +} --- /dev/null 2019-11-18 20:50:58.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/ApplicationLayout.java 2019-11-18 20:50:55.129014900 -0500 @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.nio.file.Path; +import java.util.Map; + + +/** + * Application directory layout. + */ +public final class ApplicationLayout implements PathGroup.Facade { + enum PathRole { + RUNTIME, APP, LAUNCHERS, DESKTOP, APP_MODS, DLLS + } + + ApplicationLayout(Map paths) { + data = new PathGroup(paths); + } + + private ApplicationLayout(PathGroup data) { + this.data = data; + } + + @Override + public PathGroup pathGroup() { + return data; + } + + @Override + public ApplicationLayout resolveAt(Path root) { + return new ApplicationLayout(pathGroup().resolveAt(root)); + } + + /** + * Path to launchers directory. + */ + public Path launchersDirectory() { + return pathGroup().getPath(PathRole.LAUNCHERS); + } + + /** + * Path to directory with dynamic libraries. + */ + public Path dllDirectory() { + return pathGroup().getPath(PathRole.DLLS); + } + + /** + * Path to application data directory. + */ + public Path appDirectory() { + return pathGroup().getPath(PathRole.APP); + } + + /** + * Path to Java runtime directory. + */ + public Path runtimeDirectory() { + return pathGroup().getPath(PathRole.RUNTIME); + } + + /** + * Path to application mods directory. + */ + public Path appModsDirectory() { + return pathGroup().getPath(PathRole.APP_MODS); + } + + /** + * Path to directory with application's desktop integration files. + */ + public Path destktopIntegrationDirectory() { + return pathGroup().getPath(PathRole.DESKTOP); + } + + static ApplicationLayout linuxAppImage() { + return new ApplicationLayout(Map.of( + PathRole.LAUNCHERS, Path.of("bin"), + PathRole.APP, Path.of("lib/app"), + PathRole.RUNTIME, Path.of("lib/runtime"), + PathRole.DESKTOP, Path.of("lib"), + PathRole.DLLS, Path.of("lib"), + PathRole.APP_MODS, Path.of("lib/app/mods") + )); + } + + static ApplicationLayout windowsAppImage() { + return new ApplicationLayout(Map.of( + PathRole.LAUNCHERS, Path.of(""), + PathRole.APP, Path.of("app"), + PathRole.RUNTIME, Path.of("runtime"), + PathRole.DESKTOP, Path.of(""), + PathRole.DLLS, Path.of(""), + PathRole.APP_MODS, Path.of("app/mods") + )); + } + + static ApplicationLayout macAppImage() { + return new ApplicationLayout(Map.of( + PathRole.LAUNCHERS, Path.of("Contents/MacOS"), + PathRole.APP, Path.of("Contents/app"), + PathRole.RUNTIME, Path.of("Contents/runtime"), + PathRole.DESKTOP, Path.of("Contents/Resources"), + PathRole.DLLS, Path.of("Contents/MacOS"), + PathRole.APP_MODS, Path.of("Contents/app/mods") + )); + } + + public static ApplicationLayout platformAppImage() { + if (Platform.isWindows()) { + return windowsAppImage(); + } + + if (Platform.isLinux()) { + return linuxAppImage(); + } + + if (Platform.isMac()) { + return macAppImage(); + } + + throw Platform.throwUnknownPlatformError(); + } + + public static ApplicationLayout javaRuntime() { + return new ApplicationLayout(Map.of(PathRole.RUNTIME, Path.of(""))); + } + + private final PathGroup data; +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java 2019-11-18 20:51:09.415257000 -0500 +++ /dev/null 2019-11-18 20:51:10.000000000 -0500 @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -@FunctionalInterface -interface ArgAction { - void execute(); -} --- /dev/null 2019-11-18 20:51:11.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/ArgAction.java 2019-11-18 20:51:05.826927600 -0500 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +@FunctionalInterface +interface ArgAction { + void execute(); +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java 2019-11-18 20:51:30.578367500 -0500 +++ /dev/null 2019-11-18 20:51:32.000000000 -0500 @@ -1,886 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.jpackage.internal; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Properties; -import java.util.ResourceBundle; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.stream.Stream; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Arguments - * - * This class encapsulates and processes the command line arguments, - * in effect, implementing all the work of jpackage tool. - * - * The primary entry point, processArguments(): - * Processes and validates command line arguments, constructing DeployParams. - * Validates the DeployParams, and generate the BundleParams. - * Generates List of Bundlers from BundleParams valid for this platform. - * Executes each Bundler in the list. - */ -public class Arguments { - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); - - private static final String APPIMAGE_MODE = "create-app-image"; - private static final String INSTALLER_MODE = "create-installer"; - - private static final String FA_EXTENSIONS = "extension"; - private static final String FA_CONTENT_TYPE = "mime-type"; - private static final String FA_DESCRIPTION = "description"; - private static final String FA_ICON = "icon"; - - public static final BundlerParamInfo CREATE_APP_IMAGE = - new StandardBundlerParam<>( - APPIMAGE_MODE, - Boolean.class, - p -> Boolean.FALSE, - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? - true : Boolean.valueOf(s)); - - public static final BundlerParamInfo CREATE_INSTALLER = - new StandardBundlerParam<>( - INSTALLER_MODE, - Boolean.class, - p -> Boolean.FALSE, - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? - true : Boolean.valueOf(s)); - - // regexp for parsing args (for example, for additional launchers) - private static Pattern pattern = Pattern.compile( - "(?:(?:([\"'])(?:\\\\\\1|.)*?(?:\\1|$))|(?:\\\\[\"'\\s]|[^\\s]))++"); - - private DeployParams deployParams = null; - private BundlerType bundleType = null; - - private int pos = 0; - private List argList = null; - - private List allOptions = null; - - private String input = null; - private String output = null; - - private boolean hasMainJar = false; - private boolean hasMainClass = false; - private boolean hasMainModule = false; - private boolean hasTargetFormat = false; - private boolean hasAppImage = false; - public boolean userProvidedBuildRoot = false; - - private String buildRoot = null; - private String mainJarPath = null; - - private static boolean runtimeInstaller = false; - - private List platformBundlers = null; - - private List addLaunchers = null; - - private static Map argIds = new HashMap<>(); - private static Map argShortIds = new HashMap<>(); - - static { - // init maps for parsing arguments - (EnumSet.allOf(CLIOptions.class)).forEach(option -> { - argIds.put(option.getIdWithPrefix(), option); - if (option.getShortIdWithPrefix() != null) { - argShortIds.put(option.getShortIdWithPrefix(), option); - } - }); - } - - public Arguments(String[] args) throws PackagerException { - argList = new ArrayList(args.length); - for (String arg : args) { - argList.add(arg); - } - Log.debug ("\njpackage argument list: \n" + argList + "\n"); - pos = 0; - - deployParams = new DeployParams(); - bundleType = BundlerType.NONE; - - allOptions = new ArrayList<>(); - - addLaunchers = new ArrayList<>(); - } - - // CLIOptions is public for DeployParamsTest - public enum CLIOptions { - CREATE_APP_IMAGE(APPIMAGE_MODE, OptionCategories.MODE, () -> { - context().bundleType = BundlerType.IMAGE; - context().deployParams.setTargetFormat("image"); - setOptionValue(APPIMAGE_MODE, true); - }), - - CREATE_INSTALLER(INSTALLER_MODE, OptionCategories.MODE, () -> { - setOptionValue(INSTALLER_MODE, true); - context().bundleType = BundlerType.INSTALLER; - String format = "installer"; - context().deployParams.setTargetFormat(format); - }), - - INSTALLER_TYPE("installer-type", OptionCategories.PROPERTY, () -> { - String type = popArg(); - if (BundlerType.INSTALLER.equals(context().bundleType)) { - context().deployParams.setTargetFormat(type); - context().hasTargetFormat = true; - } - setOptionValue("installer-type", type); - }), - - INPUT ("input", "i", OptionCategories.PROPERTY, () -> { - context().input = popArg(); - setOptionValue("input", context().input); - }), - - OUTPUT ("output", "o", OptionCategories.PROPERTY, () -> { - context().output = popArg(); - context().deployParams.setOutput(new File(context().output)); - }), - - DESCRIPTION ("description", "d", OptionCategories.PROPERTY), - - VENDOR ("vendor", OptionCategories.PROPERTY), - - APPCLASS ("main-class", OptionCategories.PROPERTY, () -> { - context().hasMainClass = true; - setOptionValue("main-class", popArg()); - }), - - NAME ("name", "n", OptionCategories.PROPERTY), - - IDENTIFIER ("identifier", OptionCategories.PROPERTY), - - VERBOSE ("verbose", OptionCategories.PROPERTY, () -> { - setOptionValue("verbose", true); - Log.setVerbose(true); - }), - - RESOURCE_DIR("resource-dir", - OptionCategories.PROPERTY, () -> { - String resourceDir = popArg(); - setOptionValue("resource-dir", resourceDir); - }), - - ARGUMENTS ("arguments", OptionCategories.PROPERTY, () -> { - List arguments = getArgumentList(popArg()); - setOptionValue("arguments", arguments); - }), - - ICON ("icon", OptionCategories.PROPERTY), - - COPYRIGHT ("copyright", OptionCategories.PROPERTY), - - LICENSE_FILE ("license-file", OptionCategories.PROPERTY), - - VERSION ("app-version", OptionCategories.PROPERTY), - - JAVA_OPTIONS ("java-options", OptionCategories.PROPERTY, () -> { - List args = getArgumentList(popArg()); - args.forEach(a -> setOptionValue("java-options", a)); - }), - - FILE_ASSOCIATIONS ("file-associations", - OptionCategories.PROPERTY, () -> { - Map args = new HashMap<>(); - - // load .properties file - Map initialMap = getPropertiesFromFile(popArg()); - - String ext = initialMap.get(FA_EXTENSIONS); - if (ext != null) { - args.put(StandardBundlerParam.FA_EXTENSIONS.getID(), ext); - } - - String type = initialMap.get(FA_CONTENT_TYPE); - if (type != null) { - args.put(StandardBundlerParam.FA_CONTENT_TYPE.getID(), type); - } - - String desc = initialMap.get(FA_DESCRIPTION); - if (desc != null) { - args.put(StandardBundlerParam.FA_DESCRIPTION.getID(), desc); - } - - String icon = initialMap.get(FA_ICON); - if (icon != null) { - args.put(StandardBundlerParam.FA_ICON.getID(), icon); - } - - ArrayList> associationList = - new ArrayList>(); - - associationList.add(args); - - // check that we really add _another_ value to the list - setOptionValue("file-associations", associationList); - - }), - - ADD_LAUNCHER ("add-launcher", - OptionCategories.PROPERTY, () -> { - String spec = popArg(); - String name = null; - String filename = spec; - if (spec.contains("=")) { - String[] values = spec.split("=", 2); - name = values[0]; - filename = values[1]; - } - context().addLaunchers.add( - new AddLauncherArguments(name, filename)); - }), - - TEMP_ROOT ("temp-root", OptionCategories.PROPERTY, () -> { - context().buildRoot = popArg(); - context().userProvidedBuildRoot = true; - setOptionValue("temp-root", context().buildRoot); - }), - - INSTALL_DIR ("install-dir", OptionCategories.PROPERTY), - - PREDEFINED_APP_IMAGE ("app-image", OptionCategories.PROPERTY, ()-> { - setOptionValue("app-image", popArg()); - context().hasAppImage = true; - }), - - PREDEFINED_RUNTIME_IMAGE ("runtime-image", OptionCategories.PROPERTY), - - MAIN_JAR ("main-jar", OptionCategories.PROPERTY, () -> { - context().mainJarPath = popArg(); - context().hasMainJar = true; - setOptionValue("main-jar", context().mainJarPath); - }), - - MODULE ("module", "m", OptionCategories.MODULAR, () -> { - context().hasMainModule = true; - setOptionValue("module", popArg()); - }), - - ADD_MODULES ("add-modules", OptionCategories.MODULAR), - - MODULE_PATH ("module-path", "p", OptionCategories.MODULAR), - - MAC_SIGN ("mac-sign", "s", OptionCategories.PLATFORM_MAC, () -> { - setOptionValue("mac-sign", true); - }), - - MAC_BUNDLE_NAME ("mac-bundle-name", OptionCategories.PLATFORM_MAC), - - MAC_BUNDLE_IDENTIFIER("mac-bundle-identifier", - OptionCategories.PLATFORM_MAC), - - MAC_APP_STORE_CATEGORY ("mac-app-store-category", - OptionCategories.PLATFORM_MAC), - - MAC_BUNDLE_SIGNING_PREFIX ("mac-bundle-signing-prefix", - OptionCategories.PLATFORM_MAC), - - MAC_SIGNING_KEY_NAME ("mac-signing-key-user-name", - OptionCategories.PLATFORM_MAC), - - MAC_SIGNING_KEYCHAIN ("mac-signing-keychain", - OptionCategories.PLATFORM_MAC), - - MAC_APP_STORE_ENTITLEMENTS ("mac-app-store-entitlements", - OptionCategories.PLATFORM_MAC), - - WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-menu", true); - }), - - WIN_MENU_GROUP ("win-menu-group", OptionCategories.PLATFORM_WIN), - - WIN_SHORTCUT_HINT ("win-shortcut", - OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-shortcut", true); - }), - - WIN_PER_USER_INSTALLATION ("win-per-user-install", - OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-per-user-install", false); - }), - - WIN_DIR_CHOOSER ("win-dir-chooser", - OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-dir-chooser", true); - }), - - WIN_REGISTRY_NAME ("win-registry-name", OptionCategories.PLATFORM_WIN), - - WIN_UPGRADE_UUID ("win-upgrade-uuid", - OptionCategories.PLATFORM_WIN), - - WIN_CONSOLE_HINT ("win-console", OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-console", true); - }), - - LINUX_BUNDLE_NAME ("linux-bundle-name", - OptionCategories.PLATFORM_LINUX), - - LINUX_DEB_MAINTAINER ("linux-deb-maintainer", - OptionCategories.PLATFORM_LINUX), - - LINUX_RPM_LICENSE_TYPE ("linux-rpm-license-type", - OptionCategories.PLATFORM_LINUX), - - LINUX_PACKAGE_DEPENDENCIES ("linux-package-deps", - OptionCategories.PLATFORM_LINUX), - - LINUX_MENU_GROUP ("linux-menu-group", OptionCategories.PLATFORM_LINUX); - - private final String id; - private final String shortId; - private final OptionCategories category; - private final ArgAction action; - private static Arguments argContext; - - private CLIOptions(String id, OptionCategories category) { - this(id, null, category, null); - } - - private CLIOptions(String id, String shortId, - OptionCategories category) { - this(id, shortId, category, null); - } - - private CLIOptions(String id, - OptionCategories category, ArgAction action) { - this(id, null, category, action); - } - - private CLIOptions(String id, String shortId, - OptionCategories category, ArgAction action) { - this.id = id; - this.shortId = shortId; - this.action = action; - this.category = category; - } - - static void setContext(Arguments context) { - argContext = context; - } - - public static Arguments context() { - if (argContext != null) { - return argContext; - } else { - throw new RuntimeException("Argument context is not set."); - } - } - - public String getId() { - return this.id; - } - - String getIdWithPrefix() { - String prefix = isMode() ? "" : "--"; - return prefix + this.id; - } - - String getShortIdWithPrefix() { - return this.shortId == null ? null : "-" + this.shortId; - } - - void execute() { - if (action != null) { - action.execute(); - } else { - defaultAction(); - } - } - - boolean isMode() { - return category == OptionCategories.MODE; - } - - OptionCategories getCategory() { - return category; - } - - private void defaultAction() { - context().deployParams.addBundleArgument(id, popArg()); - } - - private static void setOptionValue(String option, Object value) { - context().deployParams.addBundleArgument(option, value); - } - - private static String popArg() { - nextArg(); - return (context().pos >= context().argList.size()) ? - "" : context().argList.get(context().pos); - } - - private static String getArg() { - return (context().pos >= context().argList.size()) ? - "" : context().argList.get(context().pos); - } - - private static void nextArg() { - context().pos++; - } - - private static void prevArg() { - context().pos--; - } - - private static boolean hasNextArg() { - return context().pos < context().argList.size(); - } - } - - enum OptionCategories { - MODE, - MODULAR, - PROPERTY, - PLATFORM_MAC, - PLATFORM_WIN, - PLATFORM_LINUX; - } - - public boolean processArguments() throws Exception { - try { - - // init context of arguments - CLIOptions.setContext(this); - - // parse cmd line - String arg; - CLIOptions option; - for (; CLIOptions.hasNextArg(); CLIOptions.nextArg()) { - arg = CLIOptions.getArg(); - if ((option = toCLIOption(arg)) != null) { - // found a CLI option - allOptions.add(option); - option.execute(); - } else { - throw new PackagerException("ERR_InvalidOption", arg); - } - } - - if (allOptions.isEmpty() || !allOptions.get(0).isMode()) { - // first argument should always be a mode. - throw new PackagerException("ERR_MissingMode"); - } - - if (hasMainJar && !hasMainClass) { - // try to get main-class from manifest - String mainClass = getMainClassFromManifest(); - if (mainClass != null) { - CLIOptions.setOptionValue( - CLIOptions.APPCLASS.getId(), mainClass); - } - } - - // display warning for arguments that are not supported - // for current configuration. - - validateArguments(); - - addResources(deployParams, input); - - deployParams.setBundleType(bundleType); - - List> launchersAsMap = - new ArrayList<>(); - - for (AddLauncherArguments sl : addLaunchers) { - launchersAsMap.add(sl.getLauncherMap()); - } - - deployParams.addBundleArgument( - StandardBundlerParam.ADD_LAUNCHERS.getID(), - launchersAsMap); - - // at this point deployParams should be already configured - - deployParams.validate(); - - BundleParams bp = deployParams.getBundleParams(); - - // validate name(s) - ArrayList usedNames = new ArrayList(); - usedNames.add(bp.getName()); // add main app name - - for (AddLauncherArguments sl : addLaunchers) { - Map slMap = sl.getLauncherMap(); - String slName = - (String) slMap.get(Arguments.CLIOptions.NAME.getId()); - if (slName == null) { - throw new PackagerException("ERR_NoAddLauncherName"); - } - // same rules apply to additional launcher names as app name - DeployParams.validateName(slName, false); - for (String usedName : usedNames) { - if (slName.equals(usedName)) { - throw new PackagerException("ERR_NoUniqueName"); - } - } - usedNames.add(slName); - } - if (runtimeInstaller && bp.getName() == null) { - throw new PackagerException("ERR_NoJreInstallerName"); - } - - return generateBundle(bp.getBundleParamsAsMap()); - } catch (Exception e) { - if (Log.isVerbose()) { - throw e; - } else { - String msg1 = e.getMessage(); - Log.error(msg1); - if (e.getCause() != null && e.getCause() != e) { - String msg2 = e.getCause().getMessage(); - if (!msg1.contains(msg2)) { - Log.error(msg2); - } - } - return false; - } - } - } - - private void validateArguments() throws PackagerException { - CLIOptions mode = allOptions.get(0); - boolean imageOnly = (mode == CLIOptions.CREATE_APP_IMAGE); - boolean hasAppImage = allOptions.contains( - CLIOptions.PREDEFINED_APP_IMAGE); - boolean hasRuntime = allOptions.contains( - CLIOptions.PREDEFINED_RUNTIME_IMAGE); - boolean installerOnly = !imageOnly && hasAppImage; - boolean runtimeInstall = !imageOnly && hasRuntime && !hasAppImage && - !hasMainModule && !hasMainJar; - - for (CLIOptions option : allOptions) { - if (!ValidOptions.checkIfSupported(option)) { - // includes option valid only on different platform - throw new PackagerException("ERR_UnsupportedOption", - option.getIdWithPrefix()); - } - if (imageOnly) { - if (!ValidOptions.checkIfImageSupported(option)) { - throw new PackagerException("ERR_NotImageOption", - option.getIdWithPrefix()); - } - } else if (installerOnly || runtimeInstall) { - if (!ValidOptions.checkIfInstallerSupported(option)) { - String key = runtimeInstaller ? - "ERR_NoInstallerEntryPoint" : "ERR_NotInstallerOption"; - throw new PackagerException(key, option.getIdWithPrefix()); - } - } - } - if (installerOnly && hasRuntime) { - // note --runtime-image is only for image or runtime installer. - throw new PackagerException("ERR_NotInstallerOption", - CLIOptions.PREDEFINED_RUNTIME_IMAGE.getIdWithPrefix()); - } - if (hasMainJar && hasMainModule) { - throw new PackagerException("ERR_BothMainJarAndModule"); - } - if (imageOnly && !hasMainJar && !hasMainModule) { - throw new PackagerException("ERR_NoEntryPoint"); - } - } - - private List getPlatformBundlers() { - - if (platformBundlers != null) { - return platformBundlers; - } - - platformBundlers = new ArrayList<>(); - for (jdk.jpackage.internal.Bundler bundler : - Bundlers.createBundlersInstance().getBundlers( - bundleType.toString())) { - if (hasTargetFormat && deployParams.getTargetFormat() != null && - !deployParams.getTargetFormat().equalsIgnoreCase( - bundler.getID())) { - continue; - } - if (bundler.supported(runtimeInstaller)) { - platformBundlers.add(bundler); - } - } - - return platformBundlers; - } - - private boolean generateBundle(Map params) - throws PackagerException { - - boolean bundleCreated = false; - - // the temp-root needs to be fetched from the params early, - // to prevent each copy of the params (such as may be used for - // additional launchers) from generating a separate temp-root when - // the default is used (the default is a new temp directory) - // The bundler.cleanup() below would not otherwise be able to - // clean these extra (and unneeded) temp directories. - StandardBundlerParam.TEMP_ROOT.fetchFrom(params); - List bundlers = getPlatformBundlers(); - if (bundlers.isEmpty()) { - throw new PackagerException("ERR_InvalidInstallerType", - deployParams.getTargetFormat()); - } - PackagerException pe = null; - for (jdk.jpackage.internal.Bundler bundler : bundlers) { - Map localParams = new HashMap<>(params); - try { - if (bundler.validate(localParams)) { - File result = - bundler.execute(localParams, deployParams.outdir); - if (!userProvidedBuildRoot) { - bundler.cleanup(localParams); - } - if (result == null) { - throw new PackagerException("MSG_BundlerFailed", - bundler.getID(), bundler.getName()); - } - bundleCreated = true; // at least one bundle was created - } - Log.verbose(MessageFormat.format( - I18N.getString("message.bundle-created"), - bundler.getName())); - } catch (UnsupportedPlatformException upe) { - Log.debug(upe); - if (pe == null) { - pe = new PackagerException(upe, - "MSG_BundlerPlatformException", bundler.getName()); - } - } catch (ConfigException e) { - Log.debug(e); - if (pe == null) { - pe = (e.getAdvice() != null) ? - new PackagerException(e, - "MSG_BundlerConfigException", - bundler.getName(), e.getMessage(), e.getAdvice()) : - new PackagerException(e, - "MSG_BundlerConfigExceptionNoAdvice", - bundler.getName(), e.getMessage()); - } - } catch (RuntimeException re) { - Log.debug(re); - if (pe == null) { - pe = new PackagerException(re, - "MSG_BundlerRuntimeException", - bundler.getName(), re.toString()); - } - } finally { - if (userProvidedBuildRoot) { - Log.verbose(MessageFormat.format( - I18N.getString("message.debug-working-directory"), - (new File(buildRoot)).getAbsolutePath())); - } - } - } - if (pe != null) { - // throw packager exception only after trying all bundlers - throw pe; - } - return bundleCreated; - } - - private void addResources(DeployParams deployParams, - String inputdir) throws PackagerException { - - if (inputdir == null || inputdir.isEmpty()) { - return; - } - - File baseDir = new File(inputdir); - - if (!baseDir.isDirectory()) { - throw new PackagerException("ERR_InputNotDirectory", inputdir); - } - if (!baseDir.canRead()) { - throw new PackagerException("ERR_CannotReadInputDir", inputdir); - } - - List fileNames; - fileNames = new ArrayList<>(); - try (Stream files = Files.list(baseDir.toPath())) { - files.forEach(file -> fileNames.add( - file.getFileName().toString())); - } catch (IOException e) { - Log.error("Unable to add resources: " + e.getMessage()); - } - fileNames.forEach(file -> deployParams.addResource(baseDir, file)); - - deployParams.setClasspath(); - } - - static boolean isCLIOption(String arg) { - return toCLIOption(arg) != null; - } - - static CLIOptions toCLIOption(String arg) { - CLIOptions option; - if ((option = argIds.get(arg)) == null) { - option = argShortIds.get(arg); - } - return option; - } - - static Map getArgumentMap(String inputString) { - Map map = new HashMap<>(); - List list = getArgumentList(inputString); - for (String pair : list) { - int equals = pair.indexOf("="); - if (equals != -1) { - String key = pair.substring(0, equals); - String value = pair.substring(equals+1, pair.length()); - map.put(key, value); - } - } - return map; - } - - static Map getPropertiesFromFile(String filename) { - Map map = new HashMap<>(); - // load properties file - File file = new File(filename); - Properties properties = new Properties(); - try (FileInputStream in = new FileInputStream(file)) { - properties.load(in); - } catch (IOException e) { - Log.error("Exception: " + e.getMessage()); - } - - for (final String name: properties.stringPropertyNames()) { - map.put(name, properties.getProperty(name)); - } - - return map; - } - - static List getArgumentList(String inputString) { - List list = new ArrayList<>(); - if (inputString == null || inputString.isEmpty()) { - return list; - } - - // The "pattern" regexp attempts to abide to the rule that - // strings are delimited by whitespace unless surrounded by - // quotes, then it is anything (including spaces) in the quotes. - Matcher m = pattern.matcher(inputString); - while (m.find()) { - String s = inputString.substring(m.start(), m.end()).trim(); - // Ensure we do not have an empty string. trim() will take care of - // whitespace only strings. The regex preserves quotes and escaped - // chars so we need to clean them before adding to the List - if (!s.isEmpty()) { - list.add(unquoteIfNeeded(s)); - } - } - return list; - } - - private static String unquoteIfNeeded(String in) { - if (in == null) { - return null; - } - - if (in.isEmpty()) { - return ""; - } - - // Use code points to preserve non-ASCII chars - StringBuilder sb = new StringBuilder(); - int codeLen = in.codePointCount(0, in.length()); - int quoteChar = -1; - for (int i = 0; i < codeLen; i++) { - int code = in.codePointAt(i); - if (code == '"' || code == '\'') { - // If quote is escaped make sure to copy it - if (i > 0 && in.codePointAt(i - 1) == '\\') { - sb.deleteCharAt(sb.length() - 1); - sb.appendCodePoint(code); - continue; - } - if (quoteChar != -1) { - if (code == quoteChar) { - // close quote, skip char - quoteChar = -1; - } else { - sb.appendCodePoint(code); - } - } else { - // opening quote, skip char - quoteChar = code; - } - } else { - sb.appendCodePoint(code); - } - } - return sb.toString(); - } - - private String getMainClassFromManifest() { - if (mainJarPath == null || - input == null ) { - return null; - } - - JarFile jf; - try { - File file = new File(input, mainJarPath); - if (!file.exists()) { - return null; - } - jf = new JarFile(file); - Manifest m = jf.getManifest(); - Attributes attrs = (m != null) ? m.getMainAttributes() : null; - if (attrs != null) { - return attrs.getValue(Attributes.Name.MAIN_CLASS); - } - } catch (IOException ignore) {} - return null; - } - -} --- /dev/null 2019-11-18 20:51:32.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/Arguments.java 2019-11-18 20:51:27.114307900 -0500 @@ -0,0 +1,802 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.stream.Stream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Arguments + * + * This class encapsulates and processes the command line arguments, + * in effect, implementing all the work of jpackage tool. + * + * The primary entry point, processArguments(): + * Processes and validates command line arguments, constructing DeployParams. + * Validates the DeployParams, and generate the BundleParams. + * Generates List of Bundlers from BundleParams valid for this platform. + * Executes each Bundler in the list. + */ +public class Arguments { + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MainResources"); + + private static final String FA_EXTENSIONS = "extension"; + private static final String FA_CONTENT_TYPE = "mime-type"; + private static final String FA_DESCRIPTION = "description"; + private static final String FA_ICON = "icon"; + + // regexp for parsing args (for example, for additional launchers) + private static Pattern pattern = Pattern.compile( + "(?:(?:([\"'])(?:\\\\\\1|.)*?(?:\\1|$))|(?:\\\\[\"'\\s]|[^\\s]))++"); + + private DeployParams deployParams = null; + + private int pos = 0; + private List argList = null; + + private List allOptions = null; + + private String input = null; + private String output = null; + + private boolean hasMainJar = false; + private boolean hasMainClass = false; + private boolean hasMainModule = false; + public boolean userProvidedBuildRoot = false; + + private String buildRoot = null; + private String mainJarPath = null; + + private static boolean runtimeInstaller = false; + + private List addLaunchers = null; + + private static Map argIds = new HashMap<>(); + private static Map argShortIds = new HashMap<>(); + + static { + // init maps for parsing arguments + (EnumSet.allOf(CLIOptions.class)).forEach(option -> { + argIds.put(option.getIdWithPrefix(), option); + if (option.getShortIdWithPrefix() != null) { + argShortIds.put(option.getShortIdWithPrefix(), option); + } + }); + } + + public Arguments(String[] args) { + argList = new ArrayList(args.length); + for (String arg : args) { + argList.add(arg); + } + Log.verbose ("\njpackage argument list: \n" + argList + "\n"); + pos = 0; + + deployParams = new DeployParams(); + + allOptions = new ArrayList<>(); + + addLaunchers = new ArrayList<>(); + + output = Paths.get("").toAbsolutePath().toString(); + deployParams.setOutput(new File(output)); + } + + // CLIOptions is public for DeployParamsTest + public enum CLIOptions { + PACKAGE_TYPE("type", "t", OptionCategories.PROPERTY, () -> { + context().deployParams.setTargetFormat(popArg()); + }), + + INPUT ("input", "i", OptionCategories.PROPERTY, () -> { + context().input = popArg(); + setOptionValue("input", context().input); + }), + + OUTPUT ("dest", "d", OptionCategories.PROPERTY, () -> { + context().output = popArg(); + context().deployParams.setOutput(new File(context().output)); + }), + + DESCRIPTION ("description", OptionCategories.PROPERTY), + + VENDOR ("vendor", OptionCategories.PROPERTY), + + APPCLASS ("main-class", OptionCategories.PROPERTY, () -> { + context().hasMainClass = true; + setOptionValue("main-class", popArg()); + }), + + NAME ("name", "n", OptionCategories.PROPERTY), + + VERBOSE ("verbose", OptionCategories.PROPERTY, () -> { + setOptionValue("verbose", true); + Log.setVerbose(); + }), + + RESOURCE_DIR("resource-dir", + OptionCategories.PROPERTY, () -> { + String resourceDir = popArg(); + setOptionValue("resource-dir", resourceDir); + }), + + ARGUMENTS ("arguments", OptionCategories.PROPERTY, () -> { + List arguments = getArgumentList(popArg()); + setOptionValue("arguments", arguments); + }), + + ICON ("icon", OptionCategories.PROPERTY), + + COPYRIGHT ("copyright", OptionCategories.PROPERTY), + + LICENSE_FILE ("license-file", OptionCategories.PROPERTY), + + VERSION ("app-version", OptionCategories.PROPERTY), + + RELEASE ("linux-app-release", OptionCategories.PROPERTY), + + JAVA_OPTIONS ("java-options", OptionCategories.PROPERTY, () -> { + List args = getArgumentList(popArg()); + args.forEach(a -> setOptionValue("java-options", a)); + }), + + FILE_ASSOCIATIONS ("file-associations", + OptionCategories.PROPERTY, () -> { + Map args = new HashMap<>(); + + // load .properties file + Map initialMap = getPropertiesFromFile(popArg()); + + String ext = initialMap.get(FA_EXTENSIONS); + if (ext != null) { + args.put(StandardBundlerParam.FA_EXTENSIONS.getID(), ext); + } + + String type = initialMap.get(FA_CONTENT_TYPE); + if (type != null) { + args.put(StandardBundlerParam.FA_CONTENT_TYPE.getID(), type); + } + + String desc = initialMap.get(FA_DESCRIPTION); + if (desc != null) { + args.put(StandardBundlerParam.FA_DESCRIPTION.getID(), desc); + } + + String icon = initialMap.get(FA_ICON); + if (icon != null) { + args.put(StandardBundlerParam.FA_ICON.getID(), icon); + } + + ArrayList> associationList = + new ArrayList>(); + + associationList.add(args); + + // check that we really add _another_ value to the list + setOptionValue("file-associations", associationList); + + }), + + ADD_LAUNCHER ("add-launcher", + OptionCategories.PROPERTY, () -> { + String spec = popArg(); + String name = null; + String filename = spec; + if (spec.contains("=")) { + String[] values = spec.split("=", 2); + name = values[0]; + filename = values[1]; + } + context().addLaunchers.add( + new AddLauncherArguments(name, filename)); + }), + + TEMP_ROOT ("temp", OptionCategories.PROPERTY, () -> { + context().buildRoot = popArg(); + context().userProvidedBuildRoot = true; + setOptionValue("temp", context().buildRoot); + }), + + INSTALL_DIR ("install-dir", OptionCategories.PROPERTY), + + PREDEFINED_APP_IMAGE ("app-image", OptionCategories.PROPERTY), + + PREDEFINED_RUNTIME_IMAGE ("runtime-image", OptionCategories.PROPERTY), + + MAIN_JAR ("main-jar", OptionCategories.PROPERTY, () -> { + context().mainJarPath = popArg(); + context().hasMainJar = true; + setOptionValue("main-jar", context().mainJarPath); + }), + + MODULE ("module", "m", OptionCategories.MODULAR, () -> { + context().hasMainModule = true; + setOptionValue("module", popArg()); + }), + + ADD_MODULES ("add-modules", OptionCategories.MODULAR), + + MODULE_PATH ("module-path", "p", OptionCategories.MODULAR), + + BIND_SERVICES ("bind-services", OptionCategories.PROPERTY, () -> { + setOptionValue("bind-services", true); + }), + + MAC_SIGN ("mac-sign", "s", OptionCategories.PLATFORM_MAC, () -> { + setOptionValue("mac-sign", true); + }), + + MAC_BUNDLE_NAME ("mac-package-name", OptionCategories.PLATFORM_MAC), + + MAC_BUNDLE_IDENTIFIER("mac-package-identifier", + OptionCategories.PLATFORM_MAC), + + MAC_BUNDLE_SIGNING_PREFIX ("mac-package-signing-prefix", + OptionCategories.PLATFORM_MAC), + + MAC_SIGNING_KEY_NAME ("mac-signing-key-user-name", + OptionCategories.PLATFORM_MAC), + + MAC_SIGNING_KEYCHAIN ("mac-signing-keychain", + OptionCategories.PLATFORM_MAC), + + MAC_APP_STORE_ENTITLEMENTS ("mac-app-store-entitlements", + OptionCategories.PLATFORM_MAC), + + WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, () -> { + setOptionValue("win-menu", true); + }), + + WIN_MENU_GROUP ("win-menu-group", OptionCategories.PLATFORM_WIN), + + WIN_SHORTCUT_HINT ("win-shortcut", + OptionCategories.PLATFORM_WIN, () -> { + setOptionValue("win-shortcut", true); + }), + + WIN_PER_USER_INSTALLATION ("win-per-user-install", + OptionCategories.PLATFORM_WIN, () -> { + setOptionValue("win-per-user-install", false); + }), + + WIN_DIR_CHOOSER ("win-dir-chooser", + OptionCategories.PLATFORM_WIN, () -> { + setOptionValue("win-dir-chooser", true); + }), + + WIN_UPGRADE_UUID ("win-upgrade-uuid", + OptionCategories.PLATFORM_WIN), + + WIN_CONSOLE_HINT ("win-console", OptionCategories.PLATFORM_WIN, () -> { + setOptionValue("win-console", true); + }), + + LINUX_BUNDLE_NAME ("linux-package-name", + OptionCategories.PLATFORM_LINUX), + + LINUX_DEB_MAINTAINER ("linux-deb-maintainer", + OptionCategories.PLATFORM_LINUX), + + LINUX_CATEGORY ("linux-app-category", + OptionCategories.PLATFORM_LINUX), + + LINUX_RPM_LICENSE_TYPE ("linux-rpm-license-type", + OptionCategories.PLATFORM_LINUX), + + LINUX_PACKAGE_DEPENDENCIES ("linux-package-deps", + OptionCategories.PLATFORM_LINUX), + + LINUX_SHORTCUT_HINT ("linux-shortcut", + OptionCategories.PLATFORM_LINUX, () -> { + setOptionValue("linux-shortcut", true); + }), + + LINUX_MENU_GROUP ("linux-menu-group", OptionCategories.PLATFORM_LINUX); + + private final String id; + private final String shortId; + private final OptionCategories category; + private final ArgAction action; + private static Arguments argContext; + + private CLIOptions(String id, OptionCategories category) { + this(id, null, category, null); + } + + private CLIOptions(String id, String shortId, + OptionCategories category) { + this(id, shortId, category, null); + } + + private CLIOptions(String id, + OptionCategories category, ArgAction action) { + this(id, null, category, action); + } + + private CLIOptions(String id, String shortId, + OptionCategories category, ArgAction action) { + this.id = id; + this.shortId = shortId; + this.action = action; + this.category = category; + } + + static void setContext(Arguments context) { + argContext = context; + } + + public static Arguments context() { + if (argContext != null) { + return argContext; + } else { + throw new RuntimeException("Argument context is not set."); + } + } + + public String getId() { + return this.id; + } + + String getIdWithPrefix() { + return "--" + this.id; + } + + String getShortIdWithPrefix() { + return this.shortId == null ? null : "-" + this.shortId; + } + + void execute() { + if (action != null) { + action.execute(); + } else { + defaultAction(); + } + } + + private void defaultAction() { + context().deployParams.addBundleArgument(id, popArg()); + } + + private static void setOptionValue(String option, Object value) { + context().deployParams.addBundleArgument(option, value); + } + + private static String popArg() { + nextArg(); + return (context().pos >= context().argList.size()) ? + "" : context().argList.get(context().pos); + } + + private static String getArg() { + return (context().pos >= context().argList.size()) ? + "" : context().argList.get(context().pos); + } + + private static void nextArg() { + context().pos++; + } + + private static boolean hasNextArg() { + return context().pos < context().argList.size(); + } + } + + enum OptionCategories { + MODULAR, + PROPERTY, + PLATFORM_MAC, + PLATFORM_WIN, + PLATFORM_LINUX; + } + + public boolean processArguments() { + try { + + // init context of arguments + CLIOptions.setContext(this); + + // parse cmd line + String arg; + CLIOptions option; + for (; CLIOptions.hasNextArg(); CLIOptions.nextArg()) { + arg = CLIOptions.getArg(); + if ((option = toCLIOption(arg)) != null) { + // found a CLI option + allOptions.add(option); + option.execute(); + } else { + throw new PackagerException("ERR_InvalidOption", arg); + } + } + + if (hasMainJar && !hasMainClass) { + // try to get main-class from manifest + String mainClass = getMainClassFromManifest(); + if (mainClass != null) { + CLIOptions.setOptionValue( + CLIOptions.APPCLASS.getId(), mainClass); + } + } + + // display error for arguments that are not supported + // for current configuration. + + validateArguments(); + + addResources(deployParams, input, mainJarPath); + + List> launchersAsMap = + new ArrayList<>(); + + for (AddLauncherArguments sl : addLaunchers) { + launchersAsMap.add(sl.getLauncherMap()); + } + + deployParams.addBundleArgument( + StandardBundlerParam.ADD_LAUNCHERS.getID(), + launchersAsMap); + + // at this point deployParams should be already configured + + deployParams.validate(); + + BundleParams bp = deployParams.getBundleParams(); + + // validate name(s) + ArrayList usedNames = new ArrayList(); + usedNames.add(bp.getName()); // add main app name + + for (AddLauncherArguments sl : addLaunchers) { + Map slMap = sl.getLauncherMap(); + String slName = + (String) slMap.get(Arguments.CLIOptions.NAME.getId()); + if (slName == null) { + throw new PackagerException("ERR_NoAddLauncherName"); + } + // same rules apply to additional launcher names as app name + DeployParams.validateName(slName, false); + for (String usedName : usedNames) { + if (slName.equals(usedName)) { + throw new PackagerException("ERR_NoUniqueName"); + } + } + usedNames.add(slName); + } + if (runtimeInstaller && bp.getName() == null) { + throw new PackagerException("ERR_NoJreInstallerName"); + } + + generateBundle(bp.getBundleParamsAsMap()); + return true; + } catch (Exception e) { + if (Log.isVerbose()) { + Log.verbose(e); + } else { + String msg1 = e.getMessage(); + Log.error(msg1); + if (e.getCause() != null && e.getCause() != e) { + String msg2 = e.getCause().getMessage(); + if (msg2 != null && !msg1.contains(msg2)) { + Log.error(msg2); + } + } + } + return false; + } + } + + private void validateArguments() throws PackagerException { + String type = deployParams.getTargetFormat(); + String ptype = (type != null) ? type : "default"; + boolean imageOnly = deployParams.isTargetAppImage(); + boolean hasAppImage = allOptions.contains( + CLIOptions.PREDEFINED_APP_IMAGE); + boolean hasRuntime = allOptions.contains( + CLIOptions.PREDEFINED_RUNTIME_IMAGE); + boolean installerOnly = !imageOnly && hasAppImage; + runtimeInstaller = !imageOnly && hasRuntime && !hasAppImage && + !hasMainModule && !hasMainJar; + + for (CLIOptions option : allOptions) { + if (!ValidOptions.checkIfSupported(option)) { + // includes option valid only on different platform + throw new PackagerException("ERR_UnsupportedOption", + option.getIdWithPrefix()); + } + if (imageOnly) { + if (!ValidOptions.checkIfImageSupported(option)) { + throw new PackagerException("ERR_InvalidTypeOption", + option.getIdWithPrefix(), type); + } + } else if (installerOnly || runtimeInstaller) { + if (!ValidOptions.checkIfInstallerSupported(option)) { + if (runtimeInstaller) { + throw new PackagerException("ERR_NoInstallerEntryPoint", + option.getIdWithPrefix()); + } else { + throw new PackagerException("ERR_InvalidTypeOption", + option.getIdWithPrefix(), ptype); + } + } + } + } + if (installerOnly && hasRuntime) { + // note --runtime-image is only for image or runtime installer. + throw new PackagerException("ERR_InvalidTypeOption", + CLIOptions.PREDEFINED_RUNTIME_IMAGE.getIdWithPrefix(), + ptype); + } + if (hasMainJar && hasMainModule) { + throw new PackagerException("ERR_BothMainJarAndModule"); + } + if (imageOnly && !hasMainJar && !hasMainModule) { + throw new PackagerException("ERR_NoEntryPoint"); + } + } + + private jdk.incubator.jpackage.internal.Bundler getPlatformBundler() { + boolean appImage = deployParams.isTargetAppImage(); + String type = deployParams.getTargetFormat(); + String bundleType = (appImage ? "IMAGE" : "INSTALLER"); + + for (jdk.incubator.jpackage.internal.Bundler bundler : + Bundlers.createBundlersInstance().getBundlers(bundleType)) { + if (type == null) { + if (bundler.isDefault() + && bundler.supported(runtimeInstaller)) { + return bundler; + } + } else { + if ((appImage || type.equalsIgnoreCase(bundler.getID())) + && bundler.supported(runtimeInstaller)) { + return bundler; + } + } + } + return null; + } + + private void generateBundle(Map params) + throws PackagerException { + + boolean bundleCreated = false; + + // the temp dir needs to be fetched from the params early, + // to prevent each copy of the params (such as may be used for + // additional launchers) from generating a separate temp dir when + // the default is used (the default is a new temp directory) + // The bundler.cleanup() below would not otherwise be able to + // clean these extra (and unneeded) temp directories. + StandardBundlerParam.TEMP_ROOT.fetchFrom(params); + + // determine what bundler to run + jdk.incubator.jpackage.internal.Bundler bundler = getPlatformBundler(); + + if (bundler == null) { + throw new PackagerException("ERR_InvalidInstallerType", + deployParams.getTargetFormat()); + } + + Map localParams = new HashMap<>(params); + try { + bundler.validate(localParams); + File result = bundler.execute(localParams, deployParams.outdir); + if (result == null) { + throw new PackagerException("MSG_BundlerFailed", + bundler.getID(), bundler.getName()); + } + Log.verbose(MessageFormat.format( + I18N.getString("message.bundle-created"), + bundler.getName())); + } catch (ConfigException e) { + Log.verbose(e); + if (e.getAdvice() != null) { + throw new PackagerException(e, "MSG_BundlerConfigException", + bundler.getName(), e.getMessage(), e.getAdvice()); + } else { + throw new PackagerException(e, + "MSG_BundlerConfigExceptionNoAdvice", + bundler.getName(), e.getMessage()); + } + } catch (RuntimeException re) { + Log.verbose(re); + throw new PackagerException(re, "MSG_BundlerRuntimeException", + bundler.getName(), re.toString()); + } finally { + if (userProvidedBuildRoot) { + Log.verbose(MessageFormat.format( + I18N.getString("message.debug-working-directory"), + (new File(buildRoot)).getAbsolutePath())); + } else { + // always clean up the temporary directory created + // when --temp option not used. + bundler.cleanup(localParams); + } + } + } + + private void addResources(DeployParams deployParams, + String inputdir, String mainJar) throws PackagerException { + + if (inputdir == null || inputdir.isEmpty()) { + return; + } + + File baseDir = new File(inputdir); + + if (!baseDir.isDirectory()) { + throw new PackagerException("ERR_InputNotDirectory", inputdir); + } + if (!baseDir.canRead()) { + throw new PackagerException("ERR_CannotReadInputDir", inputdir); + } + + List fileNames; + fileNames = new ArrayList<>(); + try (Stream files = Files.list(baseDir.toPath())) { + files.forEach(file -> fileNames.add( + file.getFileName().toString())); + } catch (IOException e) { + Log.error("Unable to add resources: " + e.getMessage()); + } + fileNames.forEach(file -> deployParams.addResource(baseDir, file)); + + deployParams.setClasspath(mainJar); + } + + static CLIOptions toCLIOption(String arg) { + CLIOptions option; + if ((option = argIds.get(arg)) == null) { + option = argShortIds.get(arg); + } + return option; + } + + static Map getPropertiesFromFile(String filename) { + Map map = new HashMap<>(); + // load properties file + File file = new File(filename); + Properties properties = new Properties(); + try (FileInputStream in = new FileInputStream(file)) { + properties.load(in); + } catch (IOException e) { + Log.error("Exception: " + e.getMessage()); + } + + for (final String name: properties.stringPropertyNames()) { + map.put(name, properties.getProperty(name)); + } + + return map; + } + + static List getArgumentList(String inputString) { + List list = new ArrayList<>(); + if (inputString == null || inputString.isEmpty()) { + return list; + } + + // The "pattern" regexp attempts to abide to the rule that + // strings are delimited by whitespace unless surrounded by + // quotes, then it is anything (including spaces) in the quotes. + Matcher m = pattern.matcher(inputString); + while (m.find()) { + String s = inputString.substring(m.start(), m.end()).trim(); + // Ensure we do not have an empty string. trim() will take care of + // whitespace only strings. The regex preserves quotes and escaped + // chars so we need to clean them before adding to the List + if (!s.isEmpty()) { + list.add(unquoteIfNeeded(s)); + } + } + return list; + } + + private static String unquoteIfNeeded(String in) { + if (in == null) { + return null; + } + + if (in.isEmpty()) { + return ""; + } + + // Use code points to preserve non-ASCII chars + StringBuilder sb = new StringBuilder(); + int codeLen = in.codePointCount(0, in.length()); + int quoteChar = -1; + for (int i = 0; i < codeLen; i++) { + int code = in.codePointAt(i); + if (code == '"' || code == '\'') { + // If quote is escaped make sure to copy it + if (i > 0 && in.codePointAt(i - 1) == '\\') { + sb.deleteCharAt(sb.length() - 1); + sb.appendCodePoint(code); + continue; + } + if (quoteChar != -1) { + if (code == quoteChar) { + // close quote, skip char + quoteChar = -1; + } else { + sb.appendCodePoint(code); + } + } else { + // opening quote, skip char + quoteChar = code; + } + } else { + sb.appendCodePoint(code); + } + } + return sb.toString(); + } + + private String getMainClassFromManifest() { + if (mainJarPath == null || + input == null ) { + return null; + } + + JarFile jf; + try { + File file = new File(input, mainJarPath); + if (!file.exists()) { + return null; + } + jf = new JarFile(file); + Manifest m = jf.getManifest(); + Attributes attrs = (m != null) ? m.getMainAttributes() : null; + if (attrs != null) { + return attrs.getValue(Attributes.Name.MAIN_CLASS); + } + } catch (IOException ignore) {} + return null; + } + +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BasicBundlers.java 2019-11-18 20:51:51.950017300 -0500 +++ /dev/null 2019-11-18 20:51:53.000000000 -0500 @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.ServiceLoader; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * BasicBundlers - * - * A basic bundlers collection that loads the default bundlers. - * Loads the common bundlers. - *
    - *
  • Windows file image
  • - *
  • Mac .app
  • - *
  • Linux file image
  • - *
  • Windows MSI
  • - *
  • Windows EXE
  • - *
  • Mac DMG
  • - *
  • Mac PKG
  • - *
  • Linux DEB
  • - *
  • Linux RPM
  • - * - *
- */ -public class BasicBundlers implements Bundlers { - - boolean defaultsLoaded = false; - - private final Collection bundlers = new CopyOnWriteArrayList<>(); - - @Override - public Collection getBundlers() { - return Collections.unmodifiableCollection(bundlers); - } - - @Override - public Collection getBundlers(String type) { - if (type == null) return Collections.emptySet(); - switch (type) { - case "NONE": - return Collections.emptySet(); - case "ALL": - return getBundlers(); - default: - return Arrays.asList(getBundlers().stream() - .filter(b -> type.equalsIgnoreCase(b.getBundleType())) - .toArray(Bundler[]::new)); - } - } - - @Override - public void loadDefaultBundlers() { - // no-op. We now load all bundlers from module system. - } - - // Loads bundlers from the META-INF/services direct - @Override - public void loadBundlersFromServices(ClassLoader cl) { - ServiceLoader loader = ServiceLoader.load(Bundler.class, cl); - for (Bundler aLoader : loader) { - bundlers.add(aLoader); - } - } - - @Override - public void loadBundler(Bundler bundler) { - bundlers.add(bundler); - } -} --- /dev/null 2019-11-18 20:51:53.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/BasicBundlers.java 2019-11-18 20:51:48.616854400 -0500 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.ServiceLoader; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * BasicBundlers + * + * A basic bundlers collection that loads the default bundlers. + * Loads the common bundlers. + *
    + *
  • Windows file image
  • + *
  • Mac .app
  • + *
  • Linux file image
  • + *
  • Windows MSI
  • + *
  • Windows EXE
  • + *
  • Mac DMG
  • + *
  • Mac PKG
  • + *
  • Linux DEB
  • + *
  • Linux RPM
  • + * + *
+ */ +public class BasicBundlers implements Bundlers { + + boolean defaultsLoaded = false; + + private final Collection bundlers = new CopyOnWriteArrayList<>(); + + @Override + public Collection getBundlers() { + return Collections.unmodifiableCollection(bundlers); + } + + @Override + public Collection getBundlers(String type) { + if (type == null) return Collections.emptySet(); + switch (type) { + case "NONE": + return Collections.emptySet(); + case "ALL": + return getBundlers(); + default: + return Arrays.asList(getBundlers().stream() + .filter(b -> type.equalsIgnoreCase(b.getBundleType())) + .toArray(Bundler[]::new)); + } + } + + // Loads bundlers from the META-INF/services direct + @Override + public void loadBundlersFromServices(ClassLoader cl) { + ServiceLoader loader = ServiceLoader.load(Bundler.class, cl); + for (Bundler aLoader : loader) { + bundlers.add(aLoader); + } + } +} --- /dev/null 2019-11-18 20:52:12.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/BundleParams.java 2019-11-18 20:52:09.550811300 -0500 @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +public class BundleParams { + + final protected Map params; + + // RelativeFileSet + public static final String PARAM_APP_RESOURCES = "appResources"; + + // String - Icon file name + public static final String PARAM_ICON = "icon"; + + // String - Name of bundle file and native launcher + public static final String PARAM_NAME = "name"; + + // String - application vendor, used by most of the bundlers + public static final String PARAM_VENDOR = "vendor"; + + // String - email name and email, only used for debian */ + public static final String PARAM_EMAIL = "email"; + + // String - vendor , only used for debian */ + public static final String PARAM_MAINTAINER = "maintainer"; + + /* String - Copyright. Used on Mac */ + public static final String PARAM_COPYRIGHT = "copyright"; + + // String - GUID on windows for MSI, CFBundleIdentifier on Mac + // If not compatible with requirements then bundler either do not bundle + // or autogenerate + public static final String PARAM_IDENTIFIER = "identifier"; + + /* boolean - shortcut preferences */ + public static final String PARAM_SHORTCUT = "shortcutHint"; + // boolean - menu shortcut preference + public static final String PARAM_MENU = "menuHint"; + + // String - Application version. Format may differ for different bundlers + public static final String PARAM_VERSION = "appVersion"; + + // String - Application release. Used on Linux. + public static final String PARAM_RELEASE = "appRelease"; + + // String - Optional application description. Used by MSI and on Linux + public static final String PARAM_DESCRIPTION = "description"; + + // String - License type. Needed on Linux (rpm) + public static final String PARAM_LICENSE_TYPE = "licenseType"; + + // String - File with license. Format is OS/bundler specific + public static final String PARAM_LICENSE_FILE = "licenseFile"; + + // String Main application class. + // Not used directly but used to derive default values + public static final String PARAM_APPLICATION_CLASS = "applicationClass"; + + // boolean - Adds a dialog to let the user choose a directory + // where the product will be installed. + public static final String PARAM_INSTALLDIR_CHOOSER = "installdirChooser"; + + /** + * create a new bundle with all default values + */ + public BundleParams() { + params = new HashMap<>(); + } + + /** + * Create a bundle params with a copy of the params + * @param params map of initial parameters to be copied in. + */ + public BundleParams(Map params) { + this.params = new HashMap<>(params); + } + + public void addAllBundleParams(Map params) { + this.params.putAll(params); + } + + // NOTE: we do not care about application parameters here + // as they will be embeded into jar file manifest and + // java launcher will take care of them! + + public Map getBundleParamsAsMap() { + return new HashMap<>(params); + } + + public String getName() { + return APP_NAME.fetchFrom(params); + } + + public void setAppResourcesList( + List rfs) { + putUnlessNull(APP_RESOURCES_LIST.getID(), rfs); + } + + private void putUnlessNull(String param, Object value) { + if (value != null) { + params.put(param, value); + } + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundler.java 2019-11-18 20:52:24.139628900 -0500 +++ /dev/null 2019-11-18 20:52:25.000000000 -0500 @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.util.Collection; -import java.util.Map; - -/** - * Bundler - * - * The basic interface implemented by all Bundlers. - */ -public interface Bundler { - /** - * @return User Friendly name of this bundler. - */ - String getName(); - - /** - * @return A more verbose description of the bundler. - */ - String getDescription(); - - /** - * @return Command line identifier of the bundler. Should be unique. - */ - String getID(); - - /** - * @return The bundle type of the bundle that is created by this bundler. - */ - String getBundleType(); - - /** - * The parameters that this bundler uses to generate it's bundle. - * @return immutable collection - */ - Collection> getBundleParameters(); - - /** - * Determines if this bundler will execute with the given parameters. - * - * @param params The parameters to be validate. Validation may modify - * the map, so if you are going to be using the same map - * across multiple bundlers you should pass in a deep copy. - * @return true if valid - * @throws UnsupportedPlatformException If the bundler cannot run on this - * platform (i.e. creating mac apps on windows) - * @throws ConfigException If the configuration params are incorrect. The - * exception may contain advice on how to modify the params map - * to make it valid. - */ - public boolean validate(Map params) - throws UnsupportedPlatformException, ConfigException; - - /** - * Creates a bundle from existing content. - * - * If a call to {@link #validate(java.util.Map)} date} returns true with - * the parameters map, then you can expect a valid output. - * However if an exception was thrown out of validate or it returned - * false then you should not expect sensible results from this call. - * It may or may not return a value, and it may or may not throw an - * exception. But any output should not be considered valid or sane. - * - * @param params The parameters as specified by getBundleParameters. - * Keyed by the id from the ParamInfo. Execution may - * modify the map, so if you are going to be using the - * same map across multiple bundlers you should pass - * in a deep copy. - * @param outputParentDir - * The parent dir that the returned bundle will be placed in. - * @return The resulting bundled file - * - * For a bundler that produces a single artifact file this will be the - * location of that artifact (.exe file, .deb file, etc) - * - * For a bundler that produces a specific directory format output this will - * be the location of that specific directory (.app file, etc). - * - * For a bundler that produce multiple files, this will be a parent - * directory of those files (linux and windows images), whose name is not - * relevant to the result. - * - * @throws java.lang.IllegalArgumentException for any of the following - * reasons: - *
    - *
  • A required parameter is not found in the params list, for - * example missing the main class.
  • - *
  • A parameter has the wrong type of an object, for example a - * String where a File is required
  • - *
  • Bundler specific incompatibilities with the parameters, for - * example a bad version number format or an application id with - * forward slashes.
  • - *
- */ - public File execute(Map params, - File outputParentDir) throws PackagerException; - - /** - * Removes temporary files that are used for bundling. - */ - public void cleanup(Map params); - - /** - * Returns "true" if this bundler is supported on current platform. - */ - public boolean supported(boolean runtimeInstaller); -} --- /dev/null 2019-11-18 20:52:26.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/Bundler.java 2019-11-18 20:52:20.816414900 -0500 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.util.Collection; +import java.util.Map; + +/** + * Bundler + * + * The basic interface implemented by all Bundlers. + */ +public interface Bundler { + /** + * @return User Friendly name of this bundler. + */ + String getName(); + + /** + * @return Command line identifier of the bundler. Should be unique. + */ + String getID(); + + /** + * @return The bundle type of the bundle that is created by this bundler. + */ + String getBundleType(); + + /** + * Determines if this bundler will execute with the given parameters. + * + * @param params The parameters to be validate. Validation may modify + * the map, so if you are going to be using the same map + * across multiple bundlers you should pass in a deep copy. + * @return true if valid + * @throws ConfigException If the configuration params are incorrect. The + * exception may contain advice on how to modify the params map + * to make it valid. + */ + public boolean validate(Map params) + throws ConfigException; + + /** + * Creates a bundle from existing content. + * + * If a call to {@link #validate(java.util.Map)} date} returns true with + * the parameters map, then you can expect a valid output. + * However if an exception was thrown out of validate or it returned + * false then you should not expect sensible results from this call. + * It may or may not return a value, and it may or may not throw an + * exception. But any output should not be considered valid or sane. + * + * @param params The Bundle parameters, + * Keyed by the id from the ParamInfo. Execution may + * modify the map, so if you are going to be using the + * same map across multiple bundlers you should pass + * in a deep copy. + * @param outputParentDir + * The parent dir that the returned bundle will be placed in. + * @return The resulting bundled file + * + * For a bundler that produces a single artifact file this will be the + * location of that artifact (.exe file, .deb file, etc) + * + * For a bundler that produces a specific directory format output this will + * be the location of that specific directory (.app file, etc). + * + * For a bundler that produce multiple files, this will be a parent + * directory of those files (linux and windows images), whose name is not + * relevant to the result. + * + * @throws java.lang.IllegalArgumentException for any of the following + * reasons: + *
    + *
  • A required parameter is not found in the params list, for + * example missing the main class.
  • + *
  • A parameter has the wrong type of an object, for example a + * String where a File is required
  • + *
  • Bundler specific incompatibilities with the parameters, for + * example a bad version number format or an application id with + * forward slashes.
  • + *
+ */ + public File execute(Map params, + File outputParentDir) throws PackagerException; + + /** + * Removes temporary files that are used for bundling. + */ + public void cleanup(Map params); + + /** + * Returns "true" if this bundler is supported on current platform. + */ + public boolean supported(boolean runtimeInstaller); + + /** + * Returns "true" if this bundler is he default for the current platform. + */ + public boolean isDefault(); +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerParamInfo.java 2019-11-18 20:52:45.091185200 -0500 +++ /dev/null 2019-11-18 20:52:46.000000000 -0500 @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * BundlerParamInfo - * - * A BundlerParamInfo encapsulates an individual bundler parameter of type . - */ -class BundlerParamInfo { - - /** - * The command line and hashmap name of the parameter - */ - String id; - - /** - * Type of the parameter - */ - Class valueType; - - /** - * Indicates if value was set using default value function - */ - boolean isDefaultValue; - - /** - * If the value is not set, and no fallback value is found, - * the parameter uses the value returned by the producer. - */ - Function, T> defaultValueFunction; - - /** - * An optional string converter for command line arguments. - */ - BiFunction, T> stringConverter; - - String getID() { - return id; - } - - Class getValueType() { - return valueType; - } - - void setValueType(Class valueType) { - this.valueType = valueType; - } - - boolean getIsDefaultValue() { - return isDefaultValue; - } - - Function, T> getDefaultValueFunction() { - return defaultValueFunction; - } - - void setDefaultValueFunction( - Function, T> defaultValueFunction) { - this.defaultValueFunction = defaultValueFunction; - } - - BiFunction,T> - getStringConverter() { - return stringConverter; - } - - void setStringConverter(BiFunction, T> stringConverter) { - this.stringConverter = stringConverter; - } - - @SuppressWarnings("unchecked") - final T fetchFrom(Map params) { - return fetchFrom(params, true); - } - - @SuppressWarnings("unchecked") - final T fetchFrom(Map params, - boolean invokeDefault) { - Object o = params.get(getID()); - if (o instanceof String && getStringConverter() != null) { - return getStringConverter().apply((String)o, params); - } - - Class klass = getValueType(); - if (klass.isInstance(o)) { - return (T) o; - } - if (o != null) { - throw new IllegalArgumentException("Param " + getID() - + " should be of type " + getValueType() - + " but is a " + o.getClass()); - } - if (params.containsKey(getID())) { - // explicit nulls are allowed - return null; - } - - if (invokeDefault && (getDefaultValueFunction() != null)) { - T result = getDefaultValueFunction().apply(params); - if (result != null) { - params.put(getID(), result); - isDefaultValue = true; - } - return result; - } - - // ultimate fallback - return null; - } -} --- /dev/null 2019-11-18 20:52:46.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/BundlerParamInfo.java 2019-11-18 20:52:41.631548700 -0500 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * BundlerParamInfo + * + * A BundlerParamInfo encapsulates an individual bundler parameter of type . + */ +class BundlerParamInfo { + + /** + * The command line and hashmap name of the parameter + */ + String id; + + /** + * Type of the parameter + */ + Class valueType; + + /** + * Indicates if value was set using default value function + */ + boolean isDefaultValue; + + /** + * If the value is not set, and no fallback value is found, + * the parameter uses the value returned by the producer. + */ + Function, T> defaultValueFunction; + + /** + * An optional string converter for command line arguments. + */ + BiFunction, T> stringConverter; + + String getID() { + return id; + } + + Class getValueType() { + return valueType; + } + + boolean getIsDefaultValue() { + return isDefaultValue; + } + + Function, T> getDefaultValueFunction() { + return defaultValueFunction; + } + + BiFunction,T> + getStringConverter() { + return stringConverter; + } + + @SuppressWarnings("unchecked") + final T fetchFrom(Map params) { + return fetchFrom(params, true); + } + + @SuppressWarnings("unchecked") + final T fetchFrom(Map params, + boolean invokeDefault) { + Object o = params.get(getID()); + if (o instanceof String && getStringConverter() != null) { + return getStringConverter().apply((String)o, params); + } + + Class klass = getValueType(); + if (klass.isInstance(o)) { + return (T) o; + } + if (o != null) { + throw new IllegalArgumentException("Param " + getID() + + " should be of type " + getValueType() + + " but is a " + o.getClass()); + } + if (params.containsKey(getID())) { + // explicit nulls are allowed + return null; + } + + if (invokeDefault && (getDefaultValueFunction() != null)) { + T result = getDefaultValueFunction().apply(params); + if (result != null) { + params.put(getID(), result); + isDefaultValue = true; + } + return result; + } + + // ultimate fallback + return null; + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundlers.java 2019-11-18 20:53:06.403968000 -0500 +++ /dev/null 2019-11-18 20:53:08.000000000 -0500 @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.Collection; -import java.util.Iterator; -import java.util.ServiceLoader; - -/** - * Bundlers - * - * The interface implemented by BasicBundlers - */ -public interface Bundlers { - - /** - * This convenience method will call - * {@link #createBundlersInstance(ClassLoader)} - * with the classloader that this Bundlers is loaded from. - * - * @return an instance of Bundlers loaded and configured from - * the current ClassLoader. - */ - public static Bundlers createBundlersInstance() { - return createBundlersInstance(Bundlers.class.getClassLoader()); - } - - /** - * This convenience method will automatically load a Bundlers instance - * from either META-INF/services or the default - * {@link BasicBundlers} if none are found in - * the services meta-inf. - * - * After instantiating the bundlers instance it will load the default - * bundlers via {@link #loadDefaultBundlers()} as well as requesting - * the services loader to load any other bundelrs via - * {@link #loadBundlersFromServices(ClassLoader)}. - - * - * @param servicesClassLoader the classloader to search for - * META-INF/service registered bundlers - * @return an instance of Bundlers loaded and configured from - * the specified ClassLoader - */ - public static Bundlers createBundlersInstance( - ClassLoader servicesClassLoader) { - ServiceLoader bundlersLoader = - ServiceLoader.load(Bundlers.class, servicesClassLoader); - Bundlers bundlers = null; - Iterator iter = bundlersLoader.iterator(); - if (iter.hasNext()) { - bundlers = iter.next(); - } - if (bundlers == null) { - bundlers = new BasicBundlers(); - } - - bundlers.loadBundlersFromServices(servicesClassLoader); - return bundlers; - } - - /** - * Returns all of the preconfigured, requested, and manually - * configured bundlers loaded with this instance. - * - * @return a read-only collection of the requested bundlers - */ - Collection getBundlers(); - - /** - * Returns all of the preconfigured, requested, and manually - * configured bundlers loaded with this instance that are of - * a specific BundleType, such as disk images, installers, or - * remote installers. - * - * @return a read-only collection of the requested bundlers - */ - Collection getBundlers(String type); - - /** - * Loads the bundlers common to the JDK. A typical implementation - * would load: - *
    - *
  • Windows file image
  • - *
  • Mac .app
  • - *
  • Linux file image
  • - - *
  • Windows MSI
  • - *
  • Windows EXE
  • - *
  • Mac DMG
  • - *
  • Mac PKG
  • - *
  • Linux DEB
  • - *
  • Linux RPM
  • - * - *
- * - * This method is called from the - * {@link #createBundlersInstance(ClassLoader)} - * and {@link #createBundlersInstance()} methods. - * NOTE: Because of the module system this method is now not used. - */ - void loadDefaultBundlers(); - - /** - * Loads bundlers from the META-INF/services directly. - * - * This method is called from the - * {@link #createBundlersInstance(ClassLoader)} - * and {@link #createBundlersInstance()} methods. - */ - void loadBundlersFromServices(ClassLoader cl); - - /** - * Loads a specific bundler into the set of bundlers. - * Useful for a manually configured bundler. - * - * @param bundler the specific bundler to add - */ - void loadBundler(Bundler bundler); -} --- /dev/null 2019-11-18 20:53:08.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/Bundlers.java 2019-11-18 20:53:02.922018200 -0500 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.util.Collection; +import java.util.Iterator; +import java.util.ServiceLoader; + +/** + * Bundlers + * + * The interface implemented by BasicBundlers + */ +public interface Bundlers { + + /** + * This convenience method will call + * {@link #createBundlersInstance(ClassLoader)} + * with the classloader that this Bundlers is loaded from. + * + * @return an instance of Bundlers loaded and configured from + * the current ClassLoader. + */ + public static Bundlers createBundlersInstance() { + return createBundlersInstance(Bundlers.class.getClassLoader()); + } + + /** + * This convenience method will automatically load a Bundlers instance + * from either META-INF/services or the default + * {@link BasicBundlers} if none are found in + * the services meta-inf. + * + * After instantiating the bundlers instance it will load the default + * bundlers via {@link #loadDefaultBundlers()} as well as requesting + * the services loader to load any other bundelrs via + * {@link #loadBundlersFromServices(ClassLoader)}. + + * + * @param servicesClassLoader the classloader to search for + * META-INF/service registered bundlers + * @return an instance of Bundlers loaded and configured from + * the specified ClassLoader + */ + public static Bundlers createBundlersInstance( + ClassLoader servicesClassLoader) { + ServiceLoader bundlersLoader = + ServiceLoader.load(Bundlers.class, servicesClassLoader); + Bundlers bundlers = null; + Iterator iter = bundlersLoader.iterator(); + if (iter.hasNext()) { + bundlers = iter.next(); + } + if (bundlers == null) { + bundlers = new BasicBundlers(); + } + + bundlers.loadBundlersFromServices(servicesClassLoader); + return bundlers; + } + + /** + * Returns all of the preconfigured, requested, and manually + * configured bundlers loaded with this instance. + * + * @return a read-only collection of the requested bundlers + */ + Collection getBundlers(); + + /** + * Returns all of the preconfigured, requested, and manually + * configured bundlers loaded with this instance that are of + * a specific BundleType, such as disk images, installers, or + * remote installers. + * + * @return a read-only collection of the requested bundlers + */ + Collection getBundlers(String type); + + /** + * Loads bundlers from the META-INF/services directly. + * + * This method is called from the + * {@link #createBundlersInstance(ClassLoader)} + * and {@link #createBundlersInstance()} methods. + */ + void loadBundlersFromServices(ClassLoader cl); + +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CLIHelp.java 2019-11-18 20:53:27.745825900 -0500 +++ /dev/null 2019-11-18 20:53:29.000000000 -0500 @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.ResourceBundle; -import java.io.File; -import java.text.MessageFormat; - - -/** - * CLIHelp - * - * Generate and show the command line interface help message(s). - */ -public class CLIHelp { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.HelpResources"); - - // generates --help for jpackage's CLI - public static void showHelp(boolean noArgs) { - - if (noArgs) { - Log.info(I18N.getString("MSG_Help_no_args")); - } else { - Platform platform = (Log.isDebug()) ? - Platform.UNKNOWN : Platform.getPlatform(); - String types; - String pLaunchOptions; - String pInstallOptions; - String pInstallDir; - switch (platform) { - case MAC: - types = "{\"pkg\", \"dmg\"}"; - pLaunchOptions = I18N.getString("MSG_Help_mac_launcher"); - pInstallOptions = ""; - pInstallDir - = I18N.getString("MSG_Help_mac_linux_install_dir"); - break; - case LINUX: - types = "{\"rpm\", \"deb\"}"; - pLaunchOptions = ""; - pInstallOptions = I18N.getString("MSG_Help_linux_install"); - pInstallDir - = I18N.getString("MSG_Help_mac_linux_install_dir"); - break; - case WINDOWS: - types = "{\"exe\", \"msi\"}"; - pLaunchOptions = I18N.getString("MSG_Help_win_launcher"); - pInstallOptions = I18N.getString("MSG_Help_win_install"); - pInstallDir - = I18N.getString("MSG_Help_win_install_dir"); - break; - default: - types = - "{\"exe\", \"msi\", \"rpm\", \"deb\", \"pkg\", \"dmg\"}"; - pLaunchOptions = I18N.getString("MSG_Help_win_launcher") - + I18N.getString("MSG_Help_mac_launcher"); - pInstallOptions = I18N.getString("MSG_Help_win_install") - + I18N.getString("MSG_Help_linux_install"); - pInstallDir - = I18N.getString("MSG_Help_default_install_dir"); - break; - } - Log.info(MessageFormat.format(I18N.getString("MSG_Help"), - File.pathSeparator, types, pLaunchOptions, - pInstallOptions, pInstallDir)); - } - } -} --- /dev/null 2019-11-18 20:53:29.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/CLIHelp.java 2019-11-18 20:53:24.232309300 -0500 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.util.ResourceBundle; +import java.io.File; +import java.text.MessageFormat; + + +/** + * CLIHelp + * + * Generate and show the command line interface help message(s). + */ +public class CLIHelp { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.HelpResources"); + + // generates --help for jpackage's CLI + public static void showHelp(boolean noArgs) { + + if (noArgs) { + Log.info(I18N.getString("MSG_Help_no_args")); + } else { + Platform platform = (Log.isVerbose()) ? + Platform.UNKNOWN : Platform.getPlatform(); + String types; + String pLaunchOptions; + String pInstallOptions; + String pInstallDir; + switch (platform) { + case MAC: + types = "{\"app-image\", \"dmg\", \"pkg\"}"; + pLaunchOptions = I18N.getString("MSG_Help_mac_launcher"); + pInstallOptions = ""; + pInstallDir + = I18N.getString("MSG_Help_mac_linux_install_dir"); + break; + case LINUX: + types = "{\"app-image\", \"rpm\", \"deb\"}"; + pLaunchOptions = ""; + pInstallOptions = I18N.getString("MSG_Help_linux_install"); + pInstallDir + = I18N.getString("MSG_Help_mac_linux_install_dir"); + break; + case WINDOWS: + types = "{\"app-image\", \"exe\", \"msi\"}"; + pLaunchOptions = I18N.getString("MSG_Help_win_launcher"); + pInstallOptions = I18N.getString("MSG_Help_win_install"); + pInstallDir + = I18N.getString("MSG_Help_win_install_dir"); + break; + default: + types = "{\"app-image\", \"exe\", \"msi\", \"rpm\", \"deb\", \"pkg\", \"dmg\"}"; + pLaunchOptions = I18N.getString("MSG_Help_win_launcher") + + I18N.getString("MSG_Help_mac_launcher"); + pInstallOptions = I18N.getString("MSG_Help_win_install") + + I18N.getString("MSG_Help_linux_install"); + pInstallDir + = I18N.getString("MSG_Help_default_install_dir"); + break; + } + Log.info(MessageFormat.format(I18N.getString("MSG_Help"), + File.pathSeparator, types, pLaunchOptions, + pInstallOptions, pInstallDir)); + } + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ConfigException.java 2019-11-18 20:53:49.209945400 -0500 +++ /dev/null 2019-11-18 20:53:50.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -public class ConfigException extends Exception { - private static final long serialVersionUID = 1L; - final String advice; - - public ConfigException(String msg, String advice) { - super(msg); - this.advice = advice; - } - - public ConfigException(Exception cause) { - super(cause); - this.advice = null; - } - - public String getAdvice() { - return advice; - } -} --- /dev/null 2019-11-18 20:53:50.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/ConfigException.java 2019-11-18 20:53:45.909887200 -0500 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +public class ConfigException extends Exception { + private static final long serialVersionUID = 1L; + final String advice; + + public ConfigException(String msg, String advice) { + super(msg); + this.advice = advice; + } + + public ConfigException(String msg, String advice, Exception cause) { + super(msg, cause); + this.advice = advice; + } + + public ConfigException(Exception cause) { + super(cause); + this.advice = null; + } + + public String getAdvice() { + return advice; + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java 2019-11-18 20:54:10.012222100 -0500 +++ /dev/null 2019-11-18 20:54:11.000000000 -0500 @@ -1,581 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.InvalidPathException; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; - -/** - * DeployParams - * - * This class is generated and used in Arguments.processArguments() as - * intermediate step in generating the BundleParams and ultimately the Bundles - */ -public class DeployParams { - - final List resources = new ArrayList<>(); - - String id; - String vendor; - String email; - String description; - String licenseType; - String copyright; - String version; - Boolean systemWide; - Boolean serviceHint; - Boolean signBundle; - Boolean installdirChooser; - - String applicationClass; - - List params; - List arguments; //unnamed arguments - - // Java 9 modules support - String addModules = null; - String limitModules = null; - String modulePath = null; - String module = null; - String debugPort = null; - - File outdir = null; - - String appId = null; - - // list of jvm args - // (in theory string can contain spaces and need to be escaped - List jvmargs = new LinkedList<>(); - - // raw arguments to the bundler - Map bundlerArguments = new LinkedHashMap<>(); - - void setLicenseType(String licenseType) { - this.licenseType = licenseType; - } - - void setCopyright(String copyright) { - this.copyright = copyright; - } - - void setVersion(String version) { - this.version = version; - } - - void setSystemWide(Boolean systemWide) { - this.systemWide = systemWide; - } - - void setInstalldirChooser(Boolean installdirChooser) { - this.installdirChooser = installdirChooser; - } - - void setSignBundle(Boolean signBundle) { - this.signBundle = signBundle; - } - - void addJvmArg(String v) { - jvmargs.add(v); - } - - void setArguments(List args) { - this.arguments = args; - } - - List getArguments() { - return this.arguments; - } - - void addArgument(String arg) { - this.arguments.add(arg); - } - - void addAddModule(String value) { - if (addModules == null) { - addModules = value; - } - else { - addModules += "," + value; - } - } - - void addLimitModule(String value) { - if (limitModules == null) { - limitModules = value; - } - else { - limitModules += "," + value; - } - } - - String getModulePath() { - return this.modulePath; - } - - void setModulePath(String value) { - this.modulePath = value; - } - - void setModule(String value) { - this.module = value; - } - - void setDebug(String value) { - this.debugPort = value; - } - - void setDescription(String description) { - this.description = description; - } - - public void setAppId(String id) { - appId = id; - } - - void setParams(List params) { - this.params = params; - } - - void setVendor(String vendor) { - this.vendor = vendor; - } - - void setEmail(String email) { - this.email = email; - } - - void setApplicationClass(String applicationClass) { - this.applicationClass = applicationClass; - } - - File getOutput() { - return outdir; - } - - public void setOutput(File output) { - outdir = output; - } - - static class Template { - File in; - File out; - - Template(File in, File out) { - this.in = in; - this.out = out; - } - } - - // we need to expand as in some cases - // (most notably jpackage) - // we may get "." as filename and assumption is we include - // everything in the given folder - // (IOUtils.copyfiles() have recursive behavior) - List expandFileset(File root) { - List files = new LinkedList<>(); - if (!Files.isSymbolicLink(root.toPath())) { - if (root.isDirectory()) { - File[] children = root.listFiles(); - if (children != null) { - for (File f : children) { - files.addAll(expandFileset(f)); - } - } - } else { - files.add(root); - } - } - return files; - } - - public void addResource(File baseDir, String path) { - addResource(baseDir, new File(baseDir, path)); - } - - public void addResource(File baseDir, File file) { - // normalize initial file - // to strip things like "." in the path - // or it can confuse symlink detection logic - file = file.getAbsoluteFile(); - - if (baseDir == null) { - baseDir = file.getParentFile(); - } - resources.add(new RelativeFileSet( - baseDir, new LinkedHashSet<>(expandFileset(file)))); - } - - void setClasspath() { - String classpath = ""; - for (RelativeFileSet resource : resources) { - for (String file : resource.getIncludedFiles()) { - if (file.endsWith(".jar")) { - classpath += file + File.pathSeparator; - } - } - } - addBundleArgument( - StandardBundlerParam.CLASSPATH.getID(), classpath); - } - - private static File createFile(final File baseDir, final String path) { - final File testFile = new File(path); - return testFile.isAbsolute() ? - testFile : new File(baseDir == null ? - null : baseDir.getAbsolutePath(), path); - } - - static void validateName(String s, boolean forApp) - throws PackagerException { - - String exceptionKey = forApp ? - "ERR_InvalidAppName" : "ERR_InvalidSLName"; - - if (s == null) { - if (forApp) { - return; - } else { - throw new PackagerException(exceptionKey, s); - } - } - if (s.length() == 0 || s.charAt(s.length() - 1) == '\\') { - throw new PackagerException(exceptionKey, s); - } - try { - // name must be valid path element for this file system - Path p = (new File(s)).toPath(); - // and it must be a single name element in a path - if (p.getNameCount() != 1) { - throw new PackagerException(exceptionKey, s); - } - } catch (InvalidPathException ipe) { - throw new PackagerException(ipe, exceptionKey, s); - } - - for (int i = 0; i < s.length(); i++) { - char a = s.charAt(i); - // We check for ASCII codes first which we accept. If check fails, - // check if it is acceptable extended ASCII or unicode character. - if (a < ' ' || a > '~') { - // Accept anything else including special chars like copyright - // symbols. Note: space will be included by ASCII check above, - // but other whitespace like tabs or new line will be rejected. - if (Character.isISOControl(a) || - Character.isWhitespace(a)) { - throw new PackagerException(exceptionKey, s); - } - } else if (a == '"' || a == '%') { - throw new PackagerException(exceptionKey, s); - } - } - } - - public void validate() throws PackagerException { - if (outdir == null) { - throw new PackagerException("ERR_MissingArgument", "--output"); - } - - boolean hasModule = (bundlerArguments.get( - Arguments.CLIOptions.MODULE.getId()) != null); - boolean hasAppImage = (bundlerArguments.get( - Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId()) != null); - boolean hasClass = (bundlerArguments.get( - Arguments.CLIOptions.APPCLASS.getId()) != null); - boolean hasMain = (bundlerArguments.get( - Arguments.CLIOptions.MAIN_JAR.getId()) != null); - boolean hasRuntimeImage = (bundlerArguments.get( - Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId()) != null); - boolean hasInput = (bundlerArguments.get( - Arguments.CLIOptions.INPUT.getId()) != null); - boolean hasModulePath = (bundlerArguments.get( - Arguments.CLIOptions.MODULE_PATH.getId()) != null); - boolean runtimeInstaller = (BundlerType.INSTALLER == getBundleType()) && - !hasAppImage && !hasModule && !hasMain && hasRuntimeImage; - - if (getBundleType() == BundlerType.IMAGE) { - // Module application requires --runtime-image or --module-path - if (hasModule) { - if (!hasModulePath && !hasRuntimeImage) { - throw new PackagerException("ERR_MissingArgument", - "--runtime-image or --module-path"); - } - } else { - if (!hasInput) { - throw new PackagerException( - "ERR_MissingArgument", "--input"); - } - } - } else if (getBundleType() == BundlerType.INSTALLER) { - if (!runtimeInstaller) { - if (hasModule) { - if (!hasModulePath && !hasRuntimeImage && !hasAppImage) { - throw new PackagerException("ERR_MissingArgument", - "--runtime-image, --module-path or --app-image"); - } - } else { - if (!hasInput && !hasAppImage) { - throw new PackagerException("ERR_MissingArgument", - "--input or --app-image"); - } - } - } - } - - // if bundling non-modular image, or installer without app-image - // then we need some resources and a main class - if (!hasModule && !hasAppImage && !runtimeInstaller) { - if (resources.isEmpty()) { - throw new PackagerException("ERR_MissingAppResources"); - } - if (!hasMain) { - throw new PackagerException("ERR_MissingArgument", - "--main-jar"); - } - } - - String name = (String)bundlerArguments.get( - Arguments.CLIOptions.NAME.getId()); - validateName(name, true); - - // Validate app image if set - String appImage = (String)bundlerArguments.get( - Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId()); - if (appImage != null) { - File appImageDir = new File(appImage); - if (!appImageDir.exists() || appImageDir.list().length == 0) { - throw new PackagerException("ERR_AppImageNotExist", appImage); - } - } - - // Validate temp-root - String root = (String)bundlerArguments.get( - Arguments.CLIOptions.TEMP_ROOT.getId()); - if (root != null) { - String [] contents = (new File(root)).list(); - - if (contents != null && contents.length > 0) { - throw new PackagerException("ERR_BuildRootInvalid", root); - } - } - - // Validate license file if set - String license = (String)bundlerArguments.get( - Arguments.CLIOptions.LICENSE_FILE.getId()); - if (license != null) { - File licenseFile = new File(license); - if (!licenseFile.exists()) { - throw new PackagerException("ERR_LicenseFileNotExit"); - } - } - } - - boolean validateForBundle() { - boolean result = false; - - // Success - if (((applicationClass != null && !applicationClass.isEmpty()) || - (module != null && !module.isEmpty()))) { - result = true; - } - - return result; - } - - BundlerType bundleType = BundlerType.NONE; - String targetFormat = null; //means any - - void setBundleType(BundlerType type) { - bundleType = type; - } - - BundlerType getBundleType() { - return bundleType; - } - - void setTargetFormat(String t) { - targetFormat = t; - } - - String getTargetFormat() { - return targetFormat; - } - - private String getArch() { - String arch = System.getProperty("os.arch").toLowerCase(); - - if ("x86".equals(arch) || "i386".equals(arch) || "i486".equals(arch) - || "i586".equals(arch) || "i686".equals(arch)) { - arch = "x86"; - } else if ("x86_64".equals(arch) || "amd64".equals("arch")) { - arch = "x86_64"; - } - - return arch; - } - - static final Set multi_args = new TreeSet<>(Arrays.asList( - StandardBundlerParam.JAVA_OPTIONS.getID(), - StandardBundlerParam.ARGUMENTS.getID(), - StandardBundlerParam.MODULE_PATH.getID(), - StandardBundlerParam.ADD_MODULES.getID(), - StandardBundlerParam.LIMIT_MODULES.getID(), - StandardBundlerParam.FILE_ASSOCIATIONS.getID() - )); - - @SuppressWarnings("unchecked") - public void addBundleArgument(String key, Object value) { - // special hack for multi-line arguments - if (multi_args.contains(key)) { - Object existingValue = bundlerArguments.get(key); - if (existingValue instanceof String && value instanceof String) { - String delim = "\n\n"; - if (key.equals(StandardBundlerParam.MODULE_PATH.getID())) { - delim = File.pathSeparator; - } else if (key.equals( - StandardBundlerParam.ADD_MODULES.getID())) { - delim = ","; - } - bundlerArguments.put(key, existingValue + delim + value); - } else if (existingValue instanceof List && value instanceof List) { - ((List)existingValue).addAll((List)value); - } else if (existingValue instanceof Map && - value instanceof String && ((String)value).contains("=")) { - String[] mapValues = ((String)value).split("=", 2); - ((Map)existingValue).put(mapValues[0], mapValues[1]); - } else { - bundlerArguments.put(key, value); - } - } else { - bundlerArguments.put(key, value); - } - } - - BundleParams getBundleParams() { - BundleParams bundleParams = new BundleParams(); - - // construct app resources relative to output folder! - bundleParams.setAppResourcesList(resources); - - bundleParams.setApplicationClass(applicationClass); - bundleParams.setAppVersion(version); - bundleParams.setType(bundleType); - bundleParams.setBundleFormat(targetFormat); - bundleParams.setVendor(vendor); - bundleParams.setEmail(email); - bundleParams.setInstalldirChooser(installdirChooser); - bundleParams.setCopyright(copyright); - bundleParams.setDescription(description); - - bundleParams.setJvmargs(jvmargs); - bundleParams.setArguments(arguments); - - if (addModules != null && !addModules.isEmpty()) { - bundleParams.setAddModules(addModules); - } - - if (limitModules != null && !limitModules.isEmpty()) { - bundleParams.setLimitModules(limitModules); - } - - if (modulePath != null && !modulePath.isEmpty()) { - bundleParams.setModulePath(modulePath); - } - - if (module != null && !module.isEmpty()) { - bundleParams.setMainModule(module); - } - - if (debugPort != null && !debugPort.isEmpty()) { - bundleParams.setDebug(debugPort); - } - - Map paramsMap = new TreeMap<>(); - if (params != null) { - for (Param p : params) { - paramsMap.put(p.name, p.value); - } - } - - Map unescapedHtmlParams = new TreeMap<>(); - Map escapedHtmlParams = new TreeMap<>(); - - // check for collisions - TreeSet keys = new TreeSet<>(bundlerArguments.keySet()); - keys.retainAll(bundleParams.getBundleParamsAsMap().keySet()); - - if (!keys.isEmpty()) { - throw new RuntimeException("Deploy Params and Bundler Arguments " - + "overlap in the following values:" + keys.toString()); - } - - bundleParams.addAllBundleParams(bundlerArguments); - - return bundleParams; - } - - Map getBundlerArguments() { - return this.bundlerArguments; - } - - void putUnlessNull(String param, Object value) { - if (value != null) { - bundlerArguments.put(param, value); - } - } - - void putUnlessNullOrEmpty(String param, Map value) { - if (value != null && !value.isEmpty()) { - bundlerArguments.put(param, value); - } - } - - void putUnlessNullOrEmpty(String param, Collection value) { - if (value != null && !value.isEmpty()) { - bundlerArguments.put(param, value); - } - } - - @Override - public String toString() { - return "DeployParams {" + "output: " + outdir - + " resources: {" + resources + "}}"; - } - -} --- /dev/null 2019-11-18 20:54:11.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/DeployParams.java 2019-11-18 20:54:06.660030700 -0500 @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.InvalidPathException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * DeployParams + * + * This class is generated and used in Arguments.processArguments() as + * intermediate step in generating the BundleParams and ultimately the Bundles + */ +public class DeployParams { + + final List resources = new ArrayList<>(); + + String targetFormat = null; // means default type for this platform + + File outdir = null; + + // raw arguments to the bundler + Map bundlerArguments = new LinkedHashMap<>(); + + public void setOutput(File output) { + outdir = output; + } + + static class Template { + File in; + File out; + + Template(File in, File out) { + this.in = in; + this.out = out; + } + } + + // we need to expand as in some cases + // (most notably jpackage) + // we may get "." as filename and assumption is we include + // everything in the given folder + // (IOUtils.copyfiles() have recursive behavior) + List expandFileset(File root) { + List files = new LinkedList<>(); + if (!Files.isSymbolicLink(root.toPath())) { + if (root.isDirectory()) { + File[] children = root.listFiles(); + if (children != null) { + for (File f : children) { + files.addAll(expandFileset(f)); + } + } + } else { + files.add(root); + } + } + return files; + } + + public void addResource(File baseDir, String path) { + addResource(baseDir, new File(baseDir, path)); + } + + public void addResource(File baseDir, File file) { + // normalize initial file + // to strip things like "." in the path + // or it can confuse symlink detection logic + file = file.getAbsoluteFile(); + + if (baseDir == null) { + baseDir = file.getParentFile(); + } + resources.add(new RelativeFileSet( + baseDir, new LinkedHashSet<>(expandFileset(file)))); + } + + void setClasspath(String mainJarPath) { + String classpath; + // we want main jar first on the classpath + if (mainJarPath != null) { + classpath = mainJarPath + File.pathSeparator; + } else { + classpath = ""; + } + for (RelativeFileSet resource : resources) { + for (String file : resource.getIncludedFiles()) { + if (file.endsWith(".jar")) { + if (!file.equals(mainJarPath)) { + classpath += file + File.pathSeparator; + } + } + } + } + addBundleArgument( + StandardBundlerParam.CLASSPATH.getID(), classpath); + } + + static void validateName(String s, boolean forApp) + throws PackagerException { + + String exceptionKey = forApp ? + "ERR_InvalidAppName" : "ERR_InvalidSLName"; + + if (s == null) { + if (forApp) { + return; + } else { + throw new PackagerException(exceptionKey, s); + } + } + if (s.length() == 0 || s.charAt(s.length() - 1) == '\\') { + throw new PackagerException(exceptionKey, s); + } + try { + // name must be valid path element for this file system + Path p = (new File(s)).toPath(); + // and it must be a single name element in a path + if (p.getNameCount() != 1) { + throw new PackagerException(exceptionKey, s); + } + } catch (InvalidPathException ipe) { + throw new PackagerException(ipe, exceptionKey, s); + } + + for (int i = 0; i < s.length(); i++) { + char a = s.charAt(i); + // We check for ASCII codes first which we accept. If check fails, + // check if it is acceptable extended ASCII or unicode character. + if (a < ' ' || a > '~') { + // Accept anything else including special chars like copyright + // symbols. Note: space will be included by ASCII check above, + // but other whitespace like tabs or new line will be rejected. + if (Character.isISOControl(a) || + Character.isWhitespace(a)) { + throw new PackagerException(exceptionKey, s); + } + } else if (a == '"' || a == '%') { + throw new PackagerException(exceptionKey, s); + } + } + } + + public void validate() throws PackagerException { + boolean hasModule = (bundlerArguments.get( + Arguments.CLIOptions.MODULE.getId()) != null); + boolean hasAppImage = (bundlerArguments.get( + Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId()) != null); + boolean hasClass = (bundlerArguments.get( + Arguments.CLIOptions.APPCLASS.getId()) != null); + boolean hasMain = (bundlerArguments.get( + Arguments.CLIOptions.MAIN_JAR.getId()) != null); + boolean hasRuntimeImage = (bundlerArguments.get( + Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId()) != null); + boolean hasInput = (bundlerArguments.get( + Arguments.CLIOptions.INPUT.getId()) != null); + boolean hasModulePath = (bundlerArguments.get( + Arguments.CLIOptions.MODULE_PATH.getId()) != null); + boolean runtimeInstaller = !isTargetAppImage() && + !hasAppImage && !hasModule && !hasMain && hasRuntimeImage; + + if (isTargetAppImage()) { + // Module application requires --runtime-image or --module-path + if (hasModule) { + if (!hasModulePath && !hasRuntimeImage) { + throw new PackagerException("ERR_MissingArgument", + "--runtime-image or --module-path"); + } + } else { + if (!hasInput) { + throw new PackagerException( + "ERR_MissingArgument", "--input"); + } + } + } else { + if (!runtimeInstaller) { + if (hasModule) { + if (!hasModulePath && !hasRuntimeImage && !hasAppImage) { + throw new PackagerException("ERR_MissingArgument", + "--runtime-image, --module-path or --app-image"); + } + } else { + if (!hasInput && !hasAppImage) { + throw new PackagerException("ERR_MissingArgument", + "--input or --app-image"); + } + } + } + } + + // if bundling non-modular image, or installer without app-image + // then we need some resources and a main class + if (!hasModule && !hasAppImage && !runtimeInstaller) { + if (resources.isEmpty()) { + throw new PackagerException("ERR_MissingAppResources"); + } + if (!hasMain) { + throw new PackagerException("ERR_MissingArgument", + "--main-jar"); + } + } + + String name = (String)bundlerArguments.get( + Arguments.CLIOptions.NAME.getId()); + validateName(name, true); + + // Validate app image if set + String appImage = (String)bundlerArguments.get( + Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId()); + if (appImage != null) { + File appImageDir = new File(appImage); + if (!appImageDir.exists() || appImageDir.list().length == 0) { + throw new PackagerException("ERR_AppImageNotExist", appImage); + } + } + + // Validate temp dir + String root = (String)bundlerArguments.get( + Arguments.CLIOptions.TEMP_ROOT.getId()); + if (root != null) { + String [] contents = (new File(root)).list(); + + if (contents != null && contents.length > 0) { + throw new PackagerException("ERR_BuildRootInvalid", root); + } + } + + // Validate license file if set + String license = (String)bundlerArguments.get( + Arguments.CLIOptions.LICENSE_FILE.getId()); + if (license != null) { + File licenseFile = new File(license); + if (!licenseFile.exists()) { + throw new PackagerException("ERR_LicenseFileNotExit"); + } + } + } + + void setTargetFormat(String t) { + targetFormat = t; + } + + String getTargetFormat() { + return targetFormat; + } + + boolean isTargetAppImage() { + return ("app-image".equals(targetFormat)); + } + + private static final Set multi_args = new TreeSet<>(Arrays.asList( + StandardBundlerParam.JAVA_OPTIONS.getID(), + StandardBundlerParam.ARGUMENTS.getID(), + StandardBundlerParam.MODULE_PATH.getID(), + StandardBundlerParam.ADD_MODULES.getID(), + StandardBundlerParam.LIMIT_MODULES.getID(), + StandardBundlerParam.FILE_ASSOCIATIONS.getID() + )); + + @SuppressWarnings("unchecked") + public void addBundleArgument(String key, Object value) { + // special hack for multi-line arguments + if (multi_args.contains(key)) { + Object existingValue = bundlerArguments.get(key); + if (existingValue instanceof String && value instanceof String) { + String delim = "\n\n"; + if (key.equals(StandardBundlerParam.MODULE_PATH.getID())) { + delim = File.pathSeparator; + } else if (key.equals( + StandardBundlerParam.ADD_MODULES.getID())) { + delim = ","; + } + bundlerArguments.put(key, existingValue + delim + value); + } else if (existingValue instanceof List && value instanceof List) { + ((List)existingValue).addAll((List)value); + } else if (existingValue instanceof Map && + value instanceof String && ((String)value).contains("=")) { + String[] mapValues = ((String)value).split("=", 2); + ((Map)existingValue).put(mapValues[0], mapValues[1]); + } else { + bundlerArguments.put(key, value); + } + } else { + bundlerArguments.put(key, value); + } + } + + BundleParams getBundleParams() { + BundleParams bundleParams = new BundleParams(); + + // construct app resources relative to destination folder! + bundleParams.setAppResourcesList(resources); + + Map unescapedHtmlParams = new TreeMap<>(); + Map escapedHtmlParams = new TreeMap<>(); + + // check for collisions + TreeSet keys = new TreeSet<>(bundlerArguments.keySet()); + keys.retainAll(bundleParams.getBundleParamsAsMap().keySet()); + + if (!keys.isEmpty()) { + throw new RuntimeException("Deploy Params and Bundler Arguments " + + "overlap in the following values:" + keys.toString()); + } + + bundleParams.addAllBundleParams(bundlerArguments); + + return bundleParams; + } + + @Override + public String toString() { + return "DeployParams {" + "output: " + outdir + + " resources: {" + resources + "}}"; + } + +} --- /dev/null 2019-11-18 20:54:30.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/DottedVersion.java 2019-11-18 20:54:27.421238500 -0500 @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * Dotted numeric version string. + * E.g.: 1.0.37, 10, 0.5 + */ +class DottedVersion implements Comparable { + + public DottedVersion(String version) { + greedy = true; + components = parseVersionString(version, greedy); + value = version; + } + + private DottedVersion(String version, boolean greedy) { + this.greedy = greedy; + components = parseVersionString(version, greedy); + value = version; + } + + public static DottedVersion greedy(String version) { + return new DottedVersion(version); + } + + public static DottedVersion lazy(String version) { + return new DottedVersion(version, false); + } + + @Override + public int compareTo(String o) { + int result = 0; + int[] otherComponents = parseVersionString(o, greedy); + for (int i = 0; i < Math.min(components.length, otherComponents.length) + && result == 0; ++i) { + result = components[i] - otherComponents[i]; + } + + if (result == 0) { + result = components.length - otherComponents.length; + } + + return result; + } + + private static int[] parseVersionString(String version, boolean greedy) { + Objects.requireNonNull(version); + if (version.isEmpty()) { + if (!greedy) { + return new int[] {0}; + } + throw new IllegalArgumentException("Version may not be empty string"); + } + + int lastNotZeroIdx = -1; + List components = new ArrayList<>(); + for (var component : version.split("\\.", -1)) { + if (component.isEmpty()) { + if (!greedy) { + break; + } + + throw new IllegalArgumentException(String.format( + "Version [%s] contains a zero lenght component", version)); + } + + if (!DIGITS.matcher(component).matches()) { + // Catch "+N" and "-N" cases. + if (!greedy) { + break; + } + + throw new IllegalArgumentException(String.format( + "Version [%s] contains invalid component [%s]", version, + component)); + } + + final int num; + try { + num = Integer.parseInt(component); + } catch (NumberFormatException ex) { + if (!greedy) { + break; + } + + throw ex; + } + + if (num != 0) { + lastNotZeroIdx = components.size(); + } + components.add(num); + } + + if (lastNotZeroIdx + 1 != components.size()) { + // Strip trailing zeros. + components = components.subList(0, lastNotZeroIdx + 1); + } + + if (components.isEmpty()) { + components.add(0); + } + return components.stream().mapToInt(Integer::intValue).toArray(); + } + + @Override + public String toString() { + return value; + } + + final private int[] components; + final private String value; + final private boolean greedy; + + private static final Pattern DIGITS = Pattern.compile("\\d+"); +} --- /dev/null 2019-11-18 20:54:41.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/Executor.java 2019-11-18 20:54:38.431077300 -0500 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +final public class Executor { + + Executor() { + } + + Executor setOutputConsumer(Consumer> v) { + outputConsumer = v; + return this; + } + + Executor saveOutput(boolean v) { + saveOutput = v; + return this; + } + + Executor setProcessBuilder(ProcessBuilder v) { + pb = v; + return this; + } + + Executor setCommandLine(String... cmdline) { + return setProcessBuilder(new ProcessBuilder(cmdline)); + } + + List getOutput() { + return output; + } + + Executor executeExpectSuccess() throws IOException { + int ret = execute(); + if (0 != ret) { + throw new IOException( + String.format("Command %s exited with %d code", + createLogMessage(pb), ret)); + } + return this; + } + + int execute() throws IOException { + output = null; + + boolean needProcessOutput = outputConsumer != null || Log.isVerbose() || saveOutput; + if (needProcessOutput) { + pb.redirectErrorStream(true); + } else { + // We are not going to read process output, so need to notify + // ProcessBuilder about this. Otherwise some processes might just + // hang up (`ldconfig -p`). + pb.redirectError(ProcessBuilder.Redirect.DISCARD); + pb.redirectOutput(ProcessBuilder.Redirect.DISCARD); + } + + Log.verbose(String.format("Running %s", createLogMessage(pb))); + Process p = pb.start(); + + if (needProcessOutput) { + try (var br = new BufferedReader(new InputStreamReader( + p.getInputStream()))) { + final List savedOutput; + // Need to save output if explicitely requested (saveOutput=true) or + // if will be used used by multiple consumers + if ((outputConsumer != null && Log.isVerbose()) || saveOutput) { + savedOutput = br.lines().collect(Collectors.toList()); + if (saveOutput) { + output = savedOutput; + } + } else { + savedOutput = null; + } + + Supplier> outputStream = () -> { + if (savedOutput != null) { + return savedOutput.stream(); + } + return br.lines(); + }; + + if (Log.isVerbose()) { + outputStream.get().forEach(Log::verbose); + } + + if (outputConsumer != null) { + outputConsumer.accept(outputStream.get()); + } + + if (savedOutput == null) { + // For some processes on Linux if the output stream + // of the process is opened but not consumed, the process + // would exit with code 141. + // It turned out that reading just a single line of process + // output fixes the problem, but let's process + // all of the output, just in case. + br.lines().forEach(x -> {}); + } + } + } + + try { + return p.waitFor(); + } catch (InterruptedException ex) { + Log.verbose(ex); + throw new RuntimeException(ex); + } + } + + static Executor of(String... cmdline) { + return new Executor().setCommandLine(cmdline); + } + + static Executor of(ProcessBuilder pb) { + return new Executor().setProcessBuilder(pb); + } + + private static String createLogMessage(ProcessBuilder pb) { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("%s", pb.command())); + if (pb.directory() != null) { + sb.append(String.format("in %s", pb.directory().getAbsolutePath())); + } + return sb.toString(); + } + + private ProcessBuilder pb; + private boolean saveOutput; + private List output; + private Consumer> outputConsumer; +} --- /dev/null 2019-11-18 20:54:52.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/FileAssociation.java 2019-11-18 20:54:49.282604600 -0500 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.nio.file.Path; +import java.util.*; +import java.util.stream.Collectors; +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +final class FileAssociation { + void verify() { + if (extensions.isEmpty()) { + Log.error(I18N.getString( + "message.creating-association-with-null-extension")); + } + } + + static List fetchFrom(Map params) { + String launcherName = APP_NAME.fetchFrom(params); + + return FILE_ASSOCIATIONS.fetchFrom(params).stream().filter( + Objects::nonNull).map(fa -> { + FileAssociation assoc = new FileAssociation(); + + assoc.launcherPath = Path.of(launcherName); + assoc.description = FA_DESCRIPTION.fetchFrom(fa); + assoc.extensions = Optional.ofNullable( + FA_EXTENSIONS.fetchFrom(fa)).orElse(Collections.emptyList()); + assoc.mimeTypes = Optional.ofNullable( + FA_CONTENT_TYPE.fetchFrom(fa)).orElse(Collections.emptyList()); + + File icon = FA_ICON.fetchFrom(fa); + if (icon != null) { + assoc.iconPath = icon.toPath(); + } + + return assoc; + }).collect(Collectors.toList()); + } + + Path launcherPath; + Path iconPath; + List mimeTypes; + List extensions; + String description; +} --- old/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-11-18 20:55:03.787502700 -0500 +++ /dev/null 2019-11-18 20:55:05.000000000 -0500 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include - -extern "C" { - - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved) { - return true; - } -} \ No newline at end of file --- /dev/null 2019-11-18 20:55:05.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/I18N.java 2019-11-18 20:55:00.344589800 -0500 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.util.ResourceBundle; + +class I18N { + + static String getString(String key) { + if (PLATFORM.containsKey(key)) { + return PLATFORM.getString(key); + } + return SHARED.getString(key); + } + + private static final ResourceBundle SHARED = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MainResources"); + + private static final ResourceBundle PLATFORM; + + static { + if (Platform.isLinux()) { + PLATFORM = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.LinuxResources"); + } else if (Platform.isWindows()) { + PLATFORM = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.WinResources"); + } else if (Platform.isMac()) { + PLATFORM = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MacResources"); + } else { + throw new IllegalStateException("Unknwon platform"); + } + } +} --- /dev/null 2019-11-18 20:55:25.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/IOUtils.java 2019-11-18 20:55:21.704766700 -0500 @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.*; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.nio.channels.FileChannel; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.*; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +/** + * IOUtils + * + * A collection of static utility methods. + */ +public class IOUtils { + + public static void deleteRecursive(File path) throws IOException { + if (!path.exists()) { + return; + } + Path directory = path.toPath(); + Files.walkFileTree(directory, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, + BasicFileAttributes attr) throws IOException { + if (Platform.getPlatform() == Platform.WINDOWS) { + Files.setAttribute(file, "dos:readonly", false); + } + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, + BasicFileAttributes attr) throws IOException { + if (Platform.getPlatform() == Platform.WINDOWS) { + Files.setAttribute(dir, "dos:readonly", false); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException e) + throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } + + public static void copyRecursive(Path src, Path dest) throws IOException { + copyRecursive(src, dest, List.of()); + } + + public static void copyRecursive(Path src, Path dest, + final List excludes) throws IOException { + Files.walkFileTree(src, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(final Path dir, + final BasicFileAttributes attrs) throws IOException { + if (excludes.contains(dir.toFile().getName())) { + return FileVisitResult.SKIP_SUBTREE; + } else { + Files.createDirectories(dest.resolve(src.relativize(dir))); + return FileVisitResult.CONTINUE; + } + } + + @Override + public FileVisitResult visitFile(final Path file, + final BasicFileAttributes attrs) throws IOException { + if (!excludes.contains(file.toFile().getName())) { + Files.copy(file, dest.resolve(src.relativize(file))); + } + return FileVisitResult.CONTINUE; + } + }); + } + + public static void copyFile(File sourceFile, File destFile) + throws IOException { + destFile.getParentFile().mkdirs(); + + //recreate the file as existing copy may have weird permissions + destFile.delete(); + destFile.createNewFile(); + + try (FileChannel source = new FileInputStream(sourceFile).getChannel(); + FileChannel destination = + new FileOutputStream(destFile).getChannel()) { + + if (destination != null && source != null) { + destination.transferFrom(source, 0, source.size()); + } + } + + //preserve executable bit! + if (sourceFile.canExecute()) { + destFile.setExecutable(true, false); + } + if (!sourceFile.canWrite()) { + destFile.setReadOnly(); + } + destFile.setReadable(true, false); + } + + // run "launcher paramfile" in the directory where paramfile is kept + public static void run(String launcher, File paramFile) + throws IOException { + if (paramFile != null && paramFile.exists()) { + ProcessBuilder pb = + new ProcessBuilder(launcher, paramFile.getName()); + pb = pb.directory(paramFile.getParentFile()); + exec(pb); + } + } + + public static void exec(ProcessBuilder pb) + throws IOException { + exec(pb, false, null); + } + + static void exec(ProcessBuilder pb, boolean testForPresenseOnly, + PrintStream consumer) throws IOException { + List output = new ArrayList<>(); + Executor exec = Executor.of(pb).setOutputConsumer(lines -> { + lines.forEach(output::add); + if (consumer != null) { + output.forEach(consumer::println); + } + }); + + if (testForPresenseOnly) { + exec.execute(); + } else { + exec.executeExpectSuccess(); + } + } + + public static int getProcessOutput(List result, String... args) + throws IOException, InterruptedException { + + ProcessBuilder pb = new ProcessBuilder(args); + + final Process p = pb.start(); + + List list = new ArrayList<>(); + + final BufferedReader in = + new BufferedReader(new InputStreamReader(p.getInputStream())); + final BufferedReader err = + new BufferedReader(new InputStreamReader(p.getErrorStream())); + + Thread t = new Thread(() -> { + try { + String line; + while ((line = in.readLine()) != null) { + list.add(line); + } + } catch (IOException ioe) { + Log.verbose(ioe); + } + + try { + String line; + while ((line = err.readLine()) != null) { + Log.error(line); + } + } catch (IOException ioe) { + Log.verbose(ioe); + } + }); + t.setDaemon(true); + t.start(); + + int ret = p.waitFor(); + + result.clear(); + result.addAll(list); + + return ret; + } + + static void writableOutputDir(Path outdir) throws PackagerException { + File file = outdir.toFile(); + + if (!file.isDirectory() && !file.mkdirs()) { + throw new PackagerException("error.cannot-create-output-dir", + file.getAbsolutePath()); + } + if (!file.canWrite()) { + throw new PackagerException("error.cannot-write-to-output-dir", + file.getAbsolutePath()); + } + } + + public static Path replaceSuffix(Path path, String suffix) { + Path parent = path.getParent(); + String filename = path.getFileName().toString().replaceAll("\\.[^.]*$", "") + + Optional.ofNullable(suffix).orElse(""); + return parent != null ? parent.resolve(filename) : Path.of(filename); + } + + public static Path addSuffix(Path path, String suffix) { + Path parent = path.getParent(); + String filename = path.getFileName().toString() + suffix; + return parent != null ? parent.resolve(filename) : Path.of(filename); + } + + public static String getSuffix(Path path) { + String filename = replaceSuffix(path.getFileName(), null).toString(); + return path.getFileName().toString().substring(filename.length()); + } + + @FunctionalInterface + public static interface XmlConsumer { + void accept(XMLStreamWriter xml) throws IOException, XMLStreamException; + } + + public static void createXml(Path dstFile, XmlConsumer xmlConsumer) throws + IOException { + XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance(); + try (Writer w = Files.newBufferedWriter(dstFile)) { + // Wrap with pretty print proxy + XMLStreamWriter xml = (XMLStreamWriter) Proxy.newProxyInstance( + XMLStreamWriter.class.getClassLoader(), new Class[]{ + XMLStreamWriter.class}, new PrettyPrintHandler( + xmlFactory.createXMLStreamWriter(w))); + + xml.writeStartDocument(); + xmlConsumer.accept(xml); + xml.writeEndDocument(); + xml.flush(); + xml.close(); + } catch (XMLStreamException ex) { + throw new IOException(ex); + } catch (IOException ex) { + throw ex; + } + } + + private static class PrettyPrintHandler implements InvocationHandler { + + PrettyPrintHandler(XMLStreamWriter target) { + this.target = target; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws + Throwable { + switch (method.getName()) { + case "writeStartElement": + // update state of parent node + if (depth > 0) { + hasChildElement.put(depth - 1, true); + } + // reset state of current node + hasChildElement.put(depth, false); + // indent for current depth + target.writeCharacters(EOL); + target.writeCharacters(repeat(depth, INDENT)); + depth++; + break; + case "writeEndElement": + depth--; + if (hasChildElement.get(depth) == true) { + target.writeCharacters(EOL); + target.writeCharacters(repeat(depth, INDENT)); + } + break; + case "writeProcessingInstruction": + case "writeEmptyElement": + // update state of parent node + if (depth > 0) { + hasChildElement.put(depth - 1, true); + } + // indent for current depth + target.writeCharacters(EOL); + target.writeCharacters(repeat(depth, INDENT)); + break; + default: + break; + } + method.invoke(target, args); + return null; + } + + private static String repeat(int d, String s) { + StringBuilder sb = new StringBuilder(); + while (d-- > 0) { + sb.append(s); + } + return sb.toString(); + } + + private final XMLStreamWriter target; + private int depth = 0; + private final Map hasChildElement = new HashMap<>(); + private static final String INDENT = " "; + private static final String EOL = "\n"; + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkBundlerHelper.java 2019-11-18 20:55:36.339768700 -0500 +++ /dev/null 2019-11-18 20:55:37.000000000 -0500 @@ -1,464 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.ResourceBundle; -import java.util.Set; -import java.util.Optional; -import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.regex.Matcher; -import java.util.spi.ToolProvider; -import java.lang.module.Configuration; -import java.lang.module.ResolvedModule; -import java.lang.module.ModuleDescriptor; -import java.lang.module.ModuleFinder; -import java.lang.module.ModuleReference; - -final class JLinkBundlerHelper { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); - private static final String JRE_MODULES_FILENAME = - "jdk/jpackage/internal/resources/jre.list"; - private static final String SERVER_JRE_MODULES_FILENAME = - "jdk/jpackage/internal/resources/jre.module.list"; - - static final ToolProvider JLINK_TOOL = - ToolProvider.findFirst("jlink").orElseThrow(); - - private JLinkBundlerHelper() {} - - @SuppressWarnings("unchecked") - static final BundlerParamInfo DEBUG = - new StandardBundlerParam<>( - "-J-Xdebug", - Integer.class, - p -> null, - (s, p) -> { - return Integer.valueOf(s); - }); - - static String listOfPathToString(List value) { - String result = ""; - - for (Path path : value) { - if (result.length() > 0) { - result += File.pathSeparator; - } - - result += path.toString(); - } - - return result; - } - - static String setOfStringToString(Set value) { - String result = ""; - - for (String element : value) { - if (result.length() > 0) { - result += ","; - } - - result += element; - } - - return result; - } - - static File getMainJar(Map params) { - File result = null; - RelativeFileSet fileset = - StandardBundlerParam.MAIN_JAR.fetchFrom(params); - - if (fileset != null) { - String filename = fileset.getIncludedFiles().iterator().next(); - result = fileset.getBaseDirectory().toPath(). - resolve(filename).toFile(); - - if (result == null || !result.exists()) { - String srcdir = - StandardBundlerParam.SOURCE_DIR.fetchFrom(params); - - if (srcdir != null) { - result = new File(srcdir + File.separator + filename); - } - } - } - - return result; - } - - static String getMainClass(Map params) { - String result = ""; - String mainModule = StandardBundlerParam.MODULE.fetchFrom(params); - if (mainModule != null) { - int index = mainModule.indexOf("/"); - if (index > 0) { - result = mainModule.substring(index + 1); - } - } else { - RelativeFileSet fileset = - StandardBundlerParam.MAIN_JAR.fetchFrom(params); - if (fileset != null) { - result = StandardBundlerParam.MAIN_CLASS.fetchFrom(params); - } else { - // possibly app-image - } - } - - return result; - } - - static String getMainModule(Map params) { - String result = null; - String mainModule = StandardBundlerParam.MODULE.fetchFrom(params); - - if (mainModule != null) { - int index = mainModule.indexOf("/"); - - if (index > 0) { - result = mainModule.substring(0, index); - } else { - result = mainModule; - } - } - - return result; - } - - private static Set getValidModules(List modulePath, - Set addModules, Set limitModules) { - ModuleHelper moduleHelper = new ModuleHelper( - modulePath, addModules, limitModules); - return removeInvalidModules(modulePath, moduleHelper.modules()); - } - - static void execute(Map params, - AbstractAppImageBuilder imageBuilder) - throws IOException, Exception { - List modulePath = - StandardBundlerParam.MODULE_PATH.fetchFrom(params); - Set addModules = - StandardBundlerParam.ADD_MODULES.fetchFrom(params); - Set limitModules = - StandardBundlerParam.LIMIT_MODULES.fetchFrom(params); - Path outputDir = imageBuilder.getRoot(); - String excludeFileList = imageBuilder.getExcludeFileList(); - File mainJar = getMainJar(params); - ModFile.ModType mainJarType = ModFile.ModType.Unknown; - - if (mainJar != null) { - mainJarType = new ModFile(mainJar).getModType(); - } else if (StandardBundlerParam.MODULE.fetchFrom(params) == null) { - // user specified only main class, all jars will be on the classpath - mainJarType = ModFile.ModType.UnnamedJar; - } - - boolean bindServices = addModules.isEmpty(); - - // Modules - String mainModule = getMainModule(params); - if (mainModule == null) { - if (mainJarType == ModFile.ModType.UnnamedJar) { - if (addModules.isEmpty()) { - // The default for an unnamed jar is ALL_DEFAULT - addModules.add(ModuleHelper.ALL_DEFAULT); - } - } else if (mainJarType == ModFile.ModType.Unknown || - mainJarType == ModFile.ModType.ModularJar) { - addModules.add(ModuleHelper.ALL_DEFAULT); - } - } - - Set validModules = - getValidModules(modulePath, addModules, limitModules); - - if (mainModule != null) { - validModules.add(mainModule); - } - - Log.verbose(MessageFormat.format( - I18N.getString("message.modules"), validModules.toString())); - - runJLink(outputDir, modulePath, validModules, limitModules, - excludeFileList, new HashMap(), bindServices); - - imageBuilder.prepareApplicationFiles(); - } - - - // Returns the path to the JDK modules in the user defined module path. - static Path findPathOfModule( List modulePath, String moduleName) { - - for (Path path : modulePath) { - Path moduleNamePath = path.resolve(moduleName); - - if (Files.exists(moduleNamePath)) { - return path; - } - } - - return null; - } - - /* - * Returns the set of modules that would be visible by default for - * a non-modular-aware application consisting of the given elements. - */ - private static Set getDefaultModules( - Path[] paths, String[] addModules) { - - // the modules in the run-time image that export an API - Stream systemRoots = ModuleFinder.ofSystem().findAll().stream() - .map(ModuleReference::descriptor) - .filter(descriptor -> exportsAPI(descriptor)) - .map(ModuleDescriptor::name); - - Set roots; - if (addModules == null || addModules.length == 0) { - roots = systemRoots.collect(Collectors.toSet()); - } else { - var extraRoots = Stream.of(addModules); - roots = Stream.concat(systemRoots, - extraRoots).collect(Collectors.toSet()); - } - - ModuleFinder finder = ModuleFinder.ofSystem(); - if (paths != null && paths.length > 0) { - finder = ModuleFinder.compose(finder, ModuleFinder.of(paths)); - } - return Configuration.empty() - .resolveAndBind(finder, ModuleFinder.of(), roots) - .modules() - .stream() - .map(ResolvedModule::name) - .collect(Collectors.toSet()); - } - - /* - * Returns true if the given module exports an API to all module. - */ - private static boolean exportsAPI(ModuleDescriptor descriptor) { - return descriptor.exports() - .stream() - .filter(e -> !e.isQualified()) - .findAny() - .isPresent(); - } - - private static Set removeInvalidModules( - List modulePath, Set modules) { - Set result = new LinkedHashSet(); - ModuleManager mm = new ModuleManager(modulePath); - List lmodfiles = - mm.getModules(EnumSet.of(ModuleManager.SearchType.ModularJar, - ModuleManager.SearchType.Jmod, - ModuleManager.SearchType.ExplodedModule)); - - HashMap validModules = new HashMap<>(); - - for (ModFile modFile : lmodfiles) { - validModules.put(modFile.getModName(), modFile); - } - - for (String name : modules) { - if (validModules.containsKey(name)) { - result.add(name); - } else { - Log.error(MessageFormat.format( - I18N.getString("warning.module.does.not.exist"), name)); - } - } - - return result; - } - - private static class ModuleHelper { - // The token for "all modules on the module path". - private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; - - // The token for "all valid runtime modules". - static final String ALL_DEFAULT = "ALL-DEFAULT"; - - private final Set modules = new HashSet<>(); - private enum Macros {None, AllModulePath, AllRuntime} - - ModuleHelper(List paths, Set addModules, - Set limitModules) { - boolean addAllModulePath = false; - boolean addDefaultMods = false; - - for (Iterator iterator = addModules.iterator(); - iterator.hasNext();) { - String module = iterator.next(); - - switch (module) { - case ALL_MODULE_PATH: - iterator.remove(); - addAllModulePath = true; - break; - case ALL_DEFAULT: - iterator.remove(); - addDefaultMods = true; - break; - default: - this.modules.add(module); - } - } - - if (addAllModulePath) { - this.modules.addAll(getModuleNamesFromPath(paths)); - } else if (addDefaultMods) { - this.modules.addAll(getDefaultModules( - paths.toArray(new Path[0]), - addModules.toArray(new String[0]))); - } - } - - Set modules() { - return modules; - } - - private static Set getModuleNamesFromPath(List Value) { - Set result = new LinkedHashSet(); - ModuleManager mm = new ModuleManager(Value); - List modFiles = mm.getModules( - EnumSet.of(ModuleManager.SearchType.ModularJar, - ModuleManager.SearchType.Jmod, - ModuleManager.SearchType.ExplodedModule)); - - for (ModFile modFile : modFiles) { - result.add(modFile.getModName()); - } - return result; - } - } - - private static void runJLink(Path output, List modulePath, - Set modules, Set limitModules, String excludes, - HashMap user, boolean bindServices) - throws IOException { - - // This is just to ensure jlink is given a non-existant directory - // The passed in output path should be non-existant or empty directory - IOUtils.deleteRecursive(output.toFile()); - - ArrayList args = new ArrayList(); - args.add("--output"); - args.add(output.toString()); - if (modulePath != null && !modulePath.isEmpty()) { - args.add("--module-path"); - args.add(getPathList(modulePath)); - } - if (modules != null && !modules.isEmpty()) { - args.add("--add-modules"); - args.add(getStringList(modules)); - } - if (limitModules != null && !limitModules.isEmpty()) { - args.add("--limit-modules"); - args.add(getStringList(limitModules)); - } - if (excludes != null) { - args.add("--exclude-files"); - args.add(excludes); - } - if (user != null && !user.isEmpty()) { - for (Map.Entry entry : user.entrySet()) { - args.add(entry.getKey()); - args.add(entry.getValue()); - } - } else { - args.add("--strip-native-commands"); - args.add("--strip-debug"); - args.add("--no-man-pages"); - args.add("--no-header-files"); - if (bindServices) { - args.add("--bind-services"); - } - } - - StringWriter writer = new StringWriter(); - PrintWriter pw = new PrintWriter(writer); - - Log.verbose("jlink arguments: " + args); - int retVal = JLINK_TOOL.run(pw, pw, args.toArray(new String[0])); - String jlinkOut = writer.toString(); - - if (retVal != 0) { - throw new IOException("jlink failed with: " + jlinkOut); - } else if (jlinkOut.length() > 0) { - Log.verbose("jlink output: " + jlinkOut); - } - } - - private static String getPathList(List pathList) { - String ret = null; - for (Path p : pathList) { - String s = Matcher.quoteReplacement(p.toString()); - if (ret == null) { - ret = s; - } else { - ret += File.pathSeparator + s; - } - } - return ret; - } - - private static String getStringList(Set strings) { - String ret = null; - for (String s : strings) { - if (ret == null) { - ret = s; - } else { - ret += "," + s; - } - } - return (ret == null) ? null : Matcher.quoteReplacement(ret); - } -} --- /dev/null 2019-11-18 20:55:38.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/JLinkBundlerHelper.java 2019-11-18 20:55:32.832036900 -0500 @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.Optional; +import java.util.Arrays; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.regex.Matcher; +import java.util.spi.ToolProvider; +import java.util.jar.JarFile; +import java.lang.module.Configuration; +import java.lang.module.ResolvedModule; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import jdk.internal.module.ModulePath; + + +final class JLinkBundlerHelper { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MainResources"); + + static final ToolProvider JLINK_TOOL = + ToolProvider.findFirst("jlink").orElseThrow(); + + static File getMainJar(Map params) { + File result = null; + RelativeFileSet fileset = + StandardBundlerParam.MAIN_JAR.fetchFrom(params); + + if (fileset != null) { + String filename = fileset.getIncludedFiles().iterator().next(); + result = fileset.getBaseDirectory().toPath(). + resolve(filename).toFile(); + + if (result == null || !result.exists()) { + String srcdir = + StandardBundlerParam.SOURCE_DIR.fetchFrom(params); + + if (srcdir != null) { + result = new File(srcdir + File.separator + filename); + } + } + } + + return result; + } + + static String getMainClassFromModule(Map params) { + String mainModule = StandardBundlerParam.MODULE.fetchFrom(params); + if (mainModule != null) { + + int index = mainModule.indexOf("/"); + if (index > 0) { + return mainModule.substring(index + 1); + } else { + ModuleDescriptor descriptor = + JLinkBundlerHelper.getMainModuleDescription(params); + if (descriptor != null) { + Optional mainClass = descriptor.mainClass(); + if (mainClass.isPresent()) { + Log.verbose(MessageFormat.format(I18N.getString( + "message.module-class"), + mainClass.get(), + JLinkBundlerHelper.getMainModule(params))); + return mainClass.get(); + } + } + } + } + return null; + } + + static String getMainModule(Map params) { + String result = null; + String mainModule = StandardBundlerParam.MODULE.fetchFrom(params); + + if (mainModule != null) { + int index = mainModule.indexOf("/"); + + if (index > 0) { + result = mainModule.substring(0, index); + } else { + result = mainModule; + } + } + + return result; + } + + static void execute(Map params, + AbstractAppImageBuilder imageBuilder) + throws IOException, Exception { + + List modulePath = + StandardBundlerParam.MODULE_PATH.fetchFrom(params); + Set addModules = + StandardBundlerParam.ADD_MODULES.fetchFrom(params); + Set limitModules = + StandardBundlerParam.LIMIT_MODULES.fetchFrom(params); + Path outputDir = imageBuilder.getRuntimeRoot(); + File mainJar = getMainJar(params); + ModFile.ModType mainJarType = ModFile.ModType.Unknown; + + if (mainJar != null) { + mainJarType = new ModFile(mainJar).getModType(); + } else if (StandardBundlerParam.MODULE.fetchFrom(params) == null) { + // user specified only main class, all jars will be on the classpath + mainJarType = ModFile.ModType.UnnamedJar; + } + + boolean bindServices = + StandardBundlerParam.BIND_SERVICES.fetchFrom(params); + + // Modules + String mainModule = getMainModule(params); + if (mainModule == null) { + if (mainJarType == ModFile.ModType.UnnamedJar) { + if (addModules.isEmpty()) { + // The default for an unnamed jar is ALL_DEFAULT + addModules.add(ModuleHelper.ALL_DEFAULT); + } + } else if (mainJarType == ModFile.ModType.Unknown || + mainJarType == ModFile.ModType.ModularJar) { + addModules.add(ModuleHelper.ALL_DEFAULT); + } + } + + Set modules = new ModuleHelper( + modulePath, addModules, limitModules).modules(); + + if (mainModule != null) { + modules.add(mainModule); + } + + runJLink(outputDir, modulePath, modules, limitModules, + new HashMap(), bindServices); + + imageBuilder.prepareApplicationFiles(params); + } + + + // Returns the path to the JDK modules in the user defined module path. + static Path findPathOfModule( List modulePath, String moduleName) { + + for (Path path : modulePath) { + Path moduleNamePath = path.resolve(moduleName); + + if (Files.exists(moduleNamePath)) { + return path; + } + } + + return null; + } + + static ModuleDescriptor getMainModuleDescription(Map params) { + boolean hasModule = params.containsKey(StandardBundlerParam.MODULE.getID()); + if (hasModule) { + List modulePath = StandardBundlerParam.MODULE_PATH.fetchFrom(params); + if (!modulePath.isEmpty()) { + ModuleFinder finder = ModuleFinder.of(modulePath.toArray(new Path[0])); + String mainModule = JLinkBundlerHelper.getMainModule(params); + Optional omref = finder.find(mainModule); + if (omref.isPresent()) { + return omref.get().descriptor(); + } + } + } + + return null; + } + + /* + * Returns the set of modules that would be visible by default for + * a non-modular-aware application consisting of the given elements. + */ + private static Set getDefaultModules( + Collection paths, Collection addModules) { + + // the modules in the run-time image that export an API + Stream systemRoots = ModuleFinder.ofSystem().findAll().stream() + .map(ModuleReference::descriptor) + .filter(JLinkBundlerHelper::exportsAPI) + .map(ModuleDescriptor::name); + + Set roots = Stream.concat(systemRoots, + addModules.stream()).collect(Collectors.toSet()); + + ModuleFinder finder = createModuleFinder(paths); + + return Configuration.empty() + .resolveAndBind(finder, ModuleFinder.of(), roots) + .modules() + .stream() + .map(ResolvedModule::name) + .collect(Collectors.toSet()); + } + + /* + * Returns true if the given module exports an API to all module. + */ + private static boolean exportsAPI(ModuleDescriptor descriptor) { + return descriptor.exports() + .stream() + .anyMatch(e -> !e.isQualified()); + } + + private static ModuleFinder createModuleFinder(Collection modulePath) { + return ModuleFinder.compose( + ModulePath.of(JarFile.runtimeVersion(), true, + modulePath.toArray(Path[]::new)), + ModuleFinder.ofSystem()); + } + + private static class ModuleHelper { + // The token for "all modules on the module path". + private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; + + // The token for "all valid runtime modules". + static final String ALL_DEFAULT = "ALL-DEFAULT"; + + private final Set modules = new HashSet<>(); + ModuleHelper(List paths, Set addModules, + Set limitModules) { + boolean addAllModulePath = false; + boolean addDefaultMods = false; + + for (Iterator iterator = addModules.iterator(); + iterator.hasNext();) { + String module = iterator.next(); + + switch (module) { + case ALL_MODULE_PATH: + iterator.remove(); + addAllModulePath = true; + break; + case ALL_DEFAULT: + iterator.remove(); + addDefaultMods = true; + break; + default: + this.modules.add(module); + } + } + + if (addAllModulePath) { + this.modules.addAll(getModuleNamesFromPath(paths)); + } else if (addDefaultMods) { + this.modules.addAll(getDefaultModules( + paths, addModules)); + } + } + + Set modules() { + return modules; + } + + private static Set getModuleNamesFromPath(List paths) { + + return createModuleFinder(paths) + .findAll() + .stream() + .map(ModuleReference::descriptor) + .map(ModuleDescriptor::name) + .collect(Collectors.toSet()); + } + } + + private static void runJLink(Path output, List modulePath, + Set modules, Set limitModules, + HashMap user, boolean bindServices) + throws PackagerException { + + // This is just to ensure jlink is given a non-existant directory + // The passed in output path should be non-existant or empty directory + try { + IOUtils.deleteRecursive(output.toFile()); + } catch (IOException ioe) { + throw new PackagerException(ioe); + } + + ArrayList args = new ArrayList(); + args.add("--output"); + args.add(output.toString()); + if (modulePath != null && !modulePath.isEmpty()) { + args.add("--module-path"); + args.add(getPathList(modulePath)); + } + if (modules != null && !modules.isEmpty()) { + args.add("--add-modules"); + args.add(getStringList(modules)); + } + if (limitModules != null && !limitModules.isEmpty()) { + args.add("--limit-modules"); + args.add(getStringList(limitModules)); + } + if (user != null && !user.isEmpty()) { + for (Map.Entry entry : user.entrySet()) { + args.add(entry.getKey()); + args.add(entry.getValue()); + } + } else { + args.add("--strip-native-commands"); + args.add("--strip-debug"); + args.add("--no-man-pages"); + args.add("--no-header-files"); + if (bindServices) { + args.add("--bind-services"); + } + } + + StringWriter writer = new StringWriter(); + PrintWriter pw = new PrintWriter(writer); + + Log.verbose("jlink arguments: " + args); + int retVal = JLINK_TOOL.run(pw, pw, args.toArray(new String[0])); + String jlinkOut = writer.toString(); + + if (retVal != 0) { + throw new PackagerException("error.jlink.failed" , jlinkOut); + } else if (jlinkOut.length() > 0) { + Log.verbose("jlink output: " + jlinkOut); + } + } + + private static String getPathList(List pathList) { + String ret = null; + for (Path p : pathList) { + String s = Matcher.quoteReplacement(p.toString()); + if (ret == null) { + ret = s; + } else { + ret += File.pathSeparator + s; + } + } + return ret; + } + + private static String getStringList(Set strings) { + String ret = null; + for (String s : strings) { + if (ret == null) { + ret = s; + } else { + ret += "," + s; + } + } + return (ret == null) ? null : Matcher.quoteReplacement(ret); + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JPackageToolProvider.java 2019-11-18 20:55:57.747588300 -0500 +++ /dev/null 2019-11-18 20:55:59.000000000 -0500 @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.PrintWriter; -import java.util.spi.ToolProvider; - -/** - * JPackageToolProvider - * - * This is the ToolProvider implementation exported - * to java.util.spi.ToolProvider and ultimately javax.tools.ToolProvider - */ -public class JPackageToolProvider implements ToolProvider { - - public String name() { - return "jpackage"; - } - - public synchronized int run( - PrintWriter out, PrintWriter err, String... args) { - try { - return jdk.jpackage.main.Main.run(out, err, args); - } catch (Exception ignored) { - return -1; - } - } -} --- /dev/null 2019-11-18 20:55:59.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/JPackageToolProvider.java 2019-11-18 20:55:54.272669500 -0500 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.PrintWriter; +import java.util.spi.ToolProvider; + +/** + * JPackageToolProvider + * + * This is the ToolProvider implementation exported + * to java.util.spi.ToolProvider and ultimately javax.tools.ToolProvider + */ +public class JPackageToolProvider implements ToolProvider { + + public String name() { + return "jpackage"; + } + + public synchronized int run( + PrintWriter out, PrintWriter err, String... args) { + try { + return new jdk.incubator.jpackage.main.Main().execute(out, err, args); + } catch (RuntimeException re) { + Log.error(re.getMessage()); + Log.verbose(re); + return 1; + } + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java 2019-11-18 20:56:18.700493800 -0500 +++ /dev/null 2019-11-18 20:56:20.000000000 -0500 @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.io.PrintWriter; - -/** - * Log - * - * General purpose logging mechanism. - */ -public class Log { - public static class Logger { - private boolean verbose = false; - private PrintWriter out = null; - private PrintWriter err = null; - - public Logger(boolean v) { - verbose = v; - } - - public void setVerbose(boolean v) { - verbose = v; - } - - public boolean isVerbose() { - return verbose; - } - - public void setPrintWriter(PrintWriter out, PrintWriter err) { - this.out = out; - this.err = err; - } - - public void flush() { - if (out != null) { - out.flush(); - } - - if (err != null) { - err.flush(); - } - } - - public void info(String msg) { - if (out != null) { - out.println(msg); - } else { - System.out.println(msg); - } - } - - public void error(String msg) { - if (err != null) { - err.println(msg); - } else { - System.err.println(msg); - } - } - - public void verbose(Throwable t) { - if (out != null && (Log.debug || verbose)) { - t.printStackTrace(out); - } else if (Log.debug || verbose) { - t.printStackTrace(System.out); - } - } - - public void verbose(String msg) { - if (out != null && (Log.debug || verbose)) { - out.println(msg); - } else if (Log.debug || verbose) { - System.out.println(msg); - } - } - - public void debug(String msg) { - if (out != null && Log.debug) { - out.println(msg); - } else if (Log.debug) { - System.out.println(msg); - } - } - } - - private static Logger delegate = null; - private static boolean debug = - "true".equals(System.getenv("JPACKAGE_DEBUG")); - - public static void setLogger(Logger l) { - delegate = l; - if (l == null) { - delegate = new Logger(false); - } - } - - public static Logger getLogger() { - return delegate; - } - - public static void flush() { - if (delegate != null) { - delegate.flush(); - } - } - - public static void info(String msg) { - if (delegate != null) { - delegate.info(msg); - } - } - - public static void error(String msg) { - if (delegate != null) { - delegate.error(msg); - } - } - - public static void setVerbose(boolean v) { - if (delegate != null) { - delegate.setVerbose(v); - } - } - - public static boolean isVerbose() { - if (delegate != null) { - return delegate.isVerbose(); - } - - return false; // Off by default - } - - public static void verbose(String msg) { - if (delegate != null) { - delegate.verbose(msg); - } - } - - public static void verbose(Throwable t) { - if (delegate != null) { - delegate.verbose(t); - } - } - - public static void debug(String msg) { - if (delegate != null) { - delegate.debug(msg); - } - } - - public static void debug(Throwable t) { - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - try (PrintStream ps = new PrintStream(baos)) { - t.printStackTrace(ps); - } - debug(baos.toString()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public static boolean isDebug() { - return debug; - } - - public static void setDebug(boolean debug) { - Log.debug = debug; - } -} --- /dev/null 2019-11-18 20:56:20.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/Log.java 2019-11-18 20:56:15.226090600 -0500 @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * Log + * + * General purpose logging mechanism. + */ +public class Log { + public static class Logger { + private boolean verbose = false; + private PrintWriter out = null; + private PrintWriter err = null; + + // verbose defaults to true unless environment variable JPACKAGE_DEBUG + // is set to true. + // Then it is only set to true by using --verbose jpackage option + + public Logger() { + verbose = ("true".equals(System.getenv("JPACKAGE_DEBUG"))); + } + + public void setVerbose() { + verbose = true; + } + + public boolean isVerbose() { + return verbose; + } + + public void setPrintWriter(PrintWriter out, PrintWriter err) { + this.out = out; + this.err = err; + } + + public void flush() { + if (out != null) { + out.flush(); + } + + if (err != null) { + err.flush(); + } + } + + public void info(String msg) { + if (out != null) { + out.println(msg); + } else { + System.out.println(msg); + } + } + + public void error(String msg) { + if (err != null) { + err.println(msg); + } else { + System.err.println(msg); + } + } + + public void verbose(Throwable t) { + if (out != null && verbose) { + t.printStackTrace(out); + } else if (verbose) { + t.printStackTrace(System.out); + } + } + + public void verbose(String msg) { + if (out != null && verbose) { + out.println(msg); + } else if (verbose) { + System.out.println(msg); + } + } + } + + private static Logger delegate = null; + + public static void setLogger(Logger logger) { + delegate = (logger != null) ? logger : new Logger(); + } + + public static void flush() { + if (delegate != null) { + delegate.flush(); + } + } + + public static void info(String msg) { + if (delegate != null) { + delegate.info(msg); + } + } + + public static void error(String msg) { + if (delegate != null) { + delegate.error(msg); + } + } + + public static void setVerbose() { + if (delegate != null) { + delegate.setVerbose(); + } + } + + public static boolean isVerbose() { + return (delegate != null) ? delegate.isVerbose() : false; + } + + public static void verbose(String msg) { + if (delegate != null) { + delegate.verbose(msg); + } + } + + public static void verbose(Throwable t) { + if (delegate != null) { + delegate.verbose(t); + } + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModFile.java 2019-11-18 20:56:40.242818400 -0500 +++ /dev/null 2019-11-18 20:56:41.000000000 -0500 @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -final class ModFile { - private final String filename; - private final ModType moduleType; - - enum JarType {All, UnnamedJar, ModularJar} - enum ModType { - Unknown, UnnamedJar, ModularJar, Jmod, ExplodedModule} - - ModFile(File aFile) { - super(); - filename = aFile.getPath(); - moduleType = getModType(aFile); - } - - String getModName() { - File file = new File(getFileName()); - // do not try to remove extension for directories - return moduleType == ModType.ExplodedModule ? - file.getName() : getFileWithoutExtension(file.getName()); - } - - String getFileName() { - return filename; - } - - ModType getModType() { - return moduleType; - } - - private static ModType getModType(File aFile) { - ModType result = ModType.Unknown; - String filename = aFile.getAbsolutePath(); - - if (aFile.isFile()) { - if (filename.endsWith(".jmod")) { - result = ModType.Jmod; - } - else if (filename.endsWith(".jar")) { - JarType status = isModularJar(filename); - - if (status == JarType.ModularJar) { - result = ModType.ModularJar; - } - else if (status == JarType.UnnamedJar) { - result = ModType.UnnamedJar; - } - } - } - else if (aFile.isDirectory()) { - File moduleInfo = new File( - filename + File.separator + "module-info.class"); - - if (moduleInfo.exists()) { - result = ModType.ExplodedModule; - } - } - - return result; - } - - private static JarType isModularJar(String FileName) { - JarType result = JarType.All; - - try { - ZipInputStream zip = - new ZipInputStream(new FileInputStream(FileName)); - result = JarType.UnnamedJar; - - try { - for (ZipEntry entry = zip.getNextEntry(); entry != null; - entry = zip.getNextEntry()) { - if (entry.getName().matches("module-info.class")) { - result = JarType.ModularJar; - break; - } - } - - zip.close(); - } catch (IOException ex) { - } - } catch (FileNotFoundException e) { - } - - return result; - } - - private static String getFileWithoutExtension(String FileName) { - return FileName.replaceFirst("[.][^.]+$", ""); - } -} --- /dev/null 2019-11-18 20:56:42.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/ModFile.java 2019-11-18 20:56:36.821154000 -0500 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +final class ModFile { + private final String filename; + private final ModType moduleType; + + enum JarType {All, UnnamedJar, ModularJar} + enum ModType { + Unknown, UnnamedJar, ModularJar, Jmod, ExplodedModule} + + ModFile(File aFile) { + super(); + filename = aFile.getPath(); + moduleType = getModType(aFile); + } + + String getModName() { + File file = new File(getFileName()); + // do not try to remove extension for directories + return moduleType == ModType.ExplodedModule ? + file.getName() : getFileWithoutExtension(file.getName()); + } + + String getFileName() { + return filename; + } + + ModType getModType() { + return moduleType; + } + + private static ModType getModType(File aFile) { + ModType result = ModType.Unknown; + String filename = aFile.getAbsolutePath(); + + if (aFile.isFile()) { + if (filename.endsWith(".jmod")) { + result = ModType.Jmod; + } + else if (filename.endsWith(".jar")) { + JarType status = isModularJar(filename); + + if (status == JarType.ModularJar) { + result = ModType.ModularJar; + } + else if (status == JarType.UnnamedJar) { + result = ModType.UnnamedJar; + } + } + } + else if (aFile.isDirectory()) { + File moduleInfo = new File( + filename + File.separator + "module-info.class"); + + if (moduleInfo.exists()) { + result = ModType.ExplodedModule; + } + } + + return result; + } + + private static JarType isModularJar(String FileName) { + JarType result = JarType.All; + + try (ZipInputStream zip = + new ZipInputStream(new FileInputStream(FileName))) { + result = JarType.UnnamedJar; + + for (ZipEntry entry = zip.getNextEntry(); entry != null; + entry = zip.getNextEntry()) { + if (entry.getName().matches("module-info.class")) { + result = JarType.ModularJar; + break; + } + } + } catch (IOException ex) { + } + + return result; + } + + private static String getFileWithoutExtension(String FileName) { + return FileName.replaceFirst("[.][^.]+$", ""); + } +} --- /dev/null 2019-11-18 20:57:01.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/OverridableResource.java 2019-11-18 20:56:58.473248700 -0500 @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import static jdk.incubator.jpackage.internal.StandardBundlerParam.RESOURCE_DIR; +import jdk.incubator.jpackage.internal.resources.ResourceLocator; + +/** + * Resource file that may have the default value supplied by jpackage. It can be + * overridden by a file from resource directory set with {@code --resource-dir} + * jpackage parameter. + * + * Resource has default name and public name. Default name is the name of a file + * in {@code jdk.incubator.jpackage.internal.resources} package that provides the default + * value of the resource. + * + * Public name is a path relative to resource directory to a file with custom + * value of the resource. + * + * Use #setPublicName to set the public name. + * + * If #setPublicName was not called, name of file passed in #saveToFile function + * will be used as a public name. + * + * Use #setExternal to set arbitrary file as a source of resource. If non-null + * value was passed in #setExternal call that value will be used as a path to file + * to copy in the destination file passed in #saveToFile function call. + */ +final class OverridableResource { + + OverridableResource(String defaultName) { + this.defaultName = defaultName; + } + + OverridableResource setSubstitutionData(Map v) { + if (v != null) { + // Disconnect `v` + substitutionData = new HashMap<>(v); + } else { + substitutionData = null; + } + return this; + } + + OverridableResource setCategory(String v) { + category = v; + return this; + } + + OverridableResource setResourceDir(Path v) { + resourceDir = v; + return this; + } + + OverridableResource setResourceDir(File v) { + return setResourceDir(toPath(v)); + } + + /** + * Set name of file to look for in resource dir. + * + * @return this + */ + OverridableResource setPublicName(Path v) { + publicName = v; + return this; + } + + OverridableResource setPublicName(String v) { + return setPublicName(Path.of(v)); + } + + OverridableResource setExternal(Path v) { + externalPath = v; + return this; + } + + OverridableResource setExternal(File v) { + return setExternal(toPath(v)); + } + + void saveToFile(Path dest) throws IOException { + final String printableCategory; + if (category != null) { + printableCategory = String.format("[%s]", category); + } else { + printableCategory = ""; + } + + if (externalPath != null && externalPath.toFile().exists()) { + Log.verbose(MessageFormat.format(I18N.getString( + "message.using-custom-resource-from-file"), + printableCategory, + externalPath.toAbsolutePath().normalize())); + + try (InputStream in = Files.newInputStream(externalPath)) { + processResourceStream(in, dest); + } + return; + } + + final Path resourceName = Optional.ofNullable(publicName).orElse( + dest.getFileName()); + + if (resourceDir != null) { + final Path customResource = resourceDir.resolve(resourceName); + if (customResource.toFile().exists()) { + Log.verbose(MessageFormat.format(I18N.getString( + "message.using-custom-resource"), printableCategory, + resourceDir.normalize().toAbsolutePath().relativize( + customResource.normalize().toAbsolutePath()))); + + try (InputStream in = Files.newInputStream(customResource)) { + processResourceStream(in, dest); + } + return; + } + } + + if (defaultName != null) { + Log.verbose(MessageFormat.format( + I18N.getString("message.using-default-resource"), + defaultName, printableCategory, resourceName)); + + try (InputStream in = readDefault(defaultName)) { + processResourceStream(in, dest); + } + } + } + + void saveToFile(File dest) throws IOException { + saveToFile(dest.toPath()); + } + + static InputStream readDefault(String resourceName) { + return ResourceLocator.class.getResourceAsStream(resourceName); + } + + static OverridableResource createResource(String defaultName, + Map params) { + return new OverridableResource(defaultName).setResourceDir( + RESOURCE_DIR.fetchFrom(params)); + } + + private static List substitute(Stream lines, + Map substitutionData) { + return lines.map(line -> { + String result = line; + for (var entry : substitutionData.entrySet()) { + result = result.replace(entry.getKey(), Optional.ofNullable( + entry.getValue()).orElse("")); + } + return result; + }).collect(Collectors.toList()); + } + + private static Path toPath(File v) { + if (v != null) { + return v.toPath(); + } + return null; + } + + private void processResourceStream(InputStream rawResource, Path dest) + throws IOException { + if (substitutionData == null) { + Files.createDirectories(dest.getParent()); + Files.copy(rawResource, dest, StandardCopyOption.REPLACE_EXISTING); + } else { + // Utf8 in and out + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(rawResource, StandardCharsets.UTF_8))) { + Files.createDirectories(dest.getParent()); + Files.write(dest, substitute(reader.lines(), substitutionData)); + } + } + } + + private Map substitutionData; + private String category; + private Path resourceDir; + private Path publicName; + private Path externalPath; + private final String defaultName; +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagerException.java 2019-11-18 20:57:13.355250900 -0500 +++ /dev/null 2019-11-18 20:57:14.000000000 -0500 @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.text.MessageFormat; -import java.util.ResourceBundle; - -public class PackagerException extends Exception { - private static final long serialVersionUID = 1L; - private static final ResourceBundle bundle = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); - - public PackagerException(Throwable cause) { - super(cause); - } - - public PackagerException(String key, Throwable cause) { - super(bundle.getString(key), cause); - } - - public PackagerException(String key) { - super(bundle.getString(key)); - } - - public PackagerException(String key, String ... arguments) { - super(MessageFormat.format( - bundle.getString(key), (Object[]) arguments)); - } - - public PackagerException( - Throwable cause, String key, String ... arguments) { - super(MessageFormat.format(bundle.getString(key), - (Object[]) arguments), cause); - } - -} --- /dev/null 2019-11-18 20:57:15.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/PackagerException.java 2019-11-18 20:57:09.862951700 -0500 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.text.MessageFormat; +import java.util.ResourceBundle; + +public class PackagerException extends Exception { + private static final long serialVersionUID = 1L; + private static final ResourceBundle bundle = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MainResources"); + + public PackagerException(Throwable cause) { + super(cause); + } + + public PackagerException(String key, Throwable cause) { + super(bundle.getString(key), cause); + } + + public PackagerException(String key) { + super(bundle.getString(key)); + } + + public PackagerException(String key, String ... arguments) { + super(MessageFormat.format( + bundle.getString(key), (Object[]) arguments)); + } + + public PackagerException( + Throwable cause, String key, String ... arguments) { + super(MessageFormat.format(bundle.getString(key), + (Object[]) arguments), cause); + } + +} --- /dev/null 2019-11-18 20:57:34.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/PathGroup.java 2019-11-18 20:57:31.162642600 -0500 @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +/** + * Group of paths. + * Each path in the group is assigned a unique id. + */ +final class PathGroup { + PathGroup(Map paths) { + entries = new HashMap<>(paths); + } + + Path getPath(Object id) { + if (id == null) { + throw new NullPointerException(); + } + return entries.get(id); + } + + void setPath(Object id, Path path) { + if (path != null) { + entries.put(id, path); + } else { + entries.remove(id); + } + } + + /** + * All configured entries. + */ + List paths() { + return entries.values().stream().collect(Collectors.toList()); + } + + /** + * Root entries. + */ + List roots() { + // Sort by the number of path components in ascending order. + List> sorted = normalizedPaths().stream().sorted( + (a, b) -> a.getKey().getNameCount() - b.getKey().getNameCount()).collect( + Collectors.toList()); + + // Returns `true` if `a` is a parent of `b` + BiFunction, Map.Entry, Boolean> isParentOrSelf = (a, b) -> { + return a == b || b.getKey().startsWith(a.getKey()); + }; + + return sorted.stream().filter( + v -> v == sorted.stream().sequential().filter( + v2 -> isParentOrSelf.apply(v2, v)).findFirst().get()).map( + v -> v.getValue()).collect(Collectors.toList()); + } + + long sizeInBytes() throws IOException { + long reply = 0; + for (Path dir : roots().stream().filter(f -> Files.isDirectory(f)).collect( + Collectors.toList())) { + try (Stream stream = Files.walk(dir)) { + reply += stream.filter(p -> Files.isRegularFile(p)).mapToLong( + f -> f.toFile().length()).sum(); + } + } + return reply; + } + + PathGroup resolveAt(Path root) { + return new PathGroup(entries.entrySet().stream().collect( + Collectors.toMap(e -> e.getKey(), + e -> root.resolve(e.getValue())))); + } + + void copy(PathGroup dst) throws IOException { + copy(this, dst, null, false); + } + + void move(PathGroup dst) throws IOException { + copy(this, dst, null, true); + } + + void transform(PathGroup dst, TransformHandler handler) throws IOException { + copy(this, dst, handler, false); + } + + static interface Facade { + PathGroup pathGroup(); + + default Collection paths() { + return pathGroup().paths(); + } + + default List roots() { + return pathGroup().roots(); + } + + default long sizeInBytes() throws IOException { + return pathGroup().sizeInBytes(); + } + + T resolveAt(Path root); + + default void copy(Facade dst) throws IOException { + pathGroup().copy(dst.pathGroup()); + } + + default void move(Facade dst) throws IOException { + pathGroup().move(dst.pathGroup()); + } + + default void transform(Facade dst, TransformHandler handler) throws + IOException { + pathGroup().transform(dst.pathGroup(), handler); + } + } + + static interface TransformHandler { + public void copyFile(Path src, Path dst) throws IOException; + public void createDirectory(Path dir) throws IOException; + } + + private static void copy(PathGroup src, PathGroup dst, + TransformHandler handler, boolean move) throws IOException { + List> copyItems = new ArrayList<>(); + List excludeItems = new ArrayList<>(); + + for (var id: src.entries.keySet()) { + Path srcPath = src.entries.get(id); + if (dst.entries.containsKey(id)) { + copyItems.add(Map.entry(srcPath, dst.entries.get(id))); + } else { + excludeItems.add(srcPath); + } + } + + copy(move, copyItems, excludeItems, handler); + } + + private static void copy(boolean move, List> entries, + List excludePaths, TransformHandler handler) throws + IOException { + + if (handler == null) { + handler = new TransformHandler() { + @Override + public void copyFile(Path src, Path dst) throws IOException { + Files.createDirectories(dst.getParent()); + if (move) { + Files.move(src, dst); + } else { + Files.copy(src, dst); + } + } + + @Override + public void createDirectory(Path dir) throws IOException { + Files.createDirectories(dir); + } + }; + } + + // destination -> source file mapping + Map actions = new HashMap<>(); + for (var action: entries) { + Path src = action.getKey(); + Path dst = action.getValue(); + if (src.toFile().isDirectory()) { + try (Stream stream = Files.walk(src)) { + stream.sequential().forEach(path -> actions.put(dst.resolve( + src.relativize(path)).normalize(), path)); + } + } else { + actions.put(dst.normalize(), src); + } + } + + for (var action : actions.entrySet()) { + Path dst = action.getKey(); + Path src = action.getValue(); + + if (excludePaths.stream().anyMatch(src::startsWith)) { + continue; + } + + if (src.equals(dst) || !src.toFile().exists()) { + continue; + } + + if (src.toFile().isDirectory()) { + handler.createDirectory(dst); + } else { + handler.copyFile(src, dst); + } + } + + if (move) { + // Delete source dirs. + for (var entry: entries) { + File srcFile = entry.getKey().toFile(); + if (srcFile.isDirectory()) { + IOUtils.deleteRecursive(srcFile); + } + } + } + } + + private static Map.Entry normalizedPath(Path v) { + final Path normalized; + if (!v.isAbsolute()) { + normalized = Path.of("./").resolve(v.normalize()); + } else { + normalized = v.normalize(); + } + + return Map.entry(normalized, v); + } + + private List> normalizedPaths() { + return entries.values().stream().map(PathGroup::normalizedPath).collect( + Collectors.toList()); + } + + private final Map entries; +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Platform.java 2019-11-18 20:57:45.632939500 -0500 +++ /dev/null 2019-11-18 20:57:47.000000000 -0500 @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.regex.Pattern; - -/** - * Platform - * - * Use Platform to detect the operating system - * that is currently running. - * - * Example: - * - * Platform platform = Platform.getPlatform(); - * - * switch(platform) { - * case Platform.MAC: { - * // Do something - * break; - * } - * case Platform.WINDOWS: - * case Platform.LINUX: { - * // Do something else - * } - * } - * - */ -enum Platform {UNKNOWN, WINDOWS, LINUX, MAC; - private static final Platform platform; - private static final int majorVersion; - private static final int minorVersion; - - static { - String os = System.getProperty("os.name").toLowerCase(); - - if (os.indexOf("win") >= 0) { - platform = Platform.WINDOWS; - } - else if (os.indexOf("nix") >= 0 || os.indexOf("nux") >= 0) { - platform = Platform.LINUX; - } - else if (os.indexOf("mac") >= 0) { - platform = Platform.MAC; - } - else { - platform = Platform.UNKNOWN; - } - - String version = System.getProperty("os.version").toString(); - String[] parts = version.split(Pattern.quote(".")); - - if (parts.length > 0) { - majorVersion = Integer.parseInt(parts[0]); - - if (parts.length > 1) { - minorVersion = Integer.parseInt(parts[1]); - } - else { - minorVersion = -1; - } - } - else { - majorVersion = -1; - minorVersion = -1; - } - } - - private Platform() {} - - static Platform getPlatform() { - return platform; - } - - static int getMajorVersion() { - return majorVersion; - } - - static int getMinorVersion() { - return minorVersion; - } -} --- /dev/null 2019-11-18 20:57:47.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/Platform.java 2019-11-18 20:57:42.177998400 -0500 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.util.regex.Pattern; + +/** + * Platform + * + * Use Platform to detect the operating system + * that is currently running. + * + * Example: + * + * Platform platform = Platform.getPlatform(); + * + * switch(platform) { + * case Platform.MAC: { + * // Do something + * break; + * } + * case Platform.WINDOWS: + * case Platform.LINUX: { + * // Do something else + * } + * } + * + */ +enum Platform {UNKNOWN, WINDOWS, LINUX, MAC; + private static final Platform platform; + private static final int majorVersion; + private static final int minorVersion; + + static { + String os = System.getProperty("os.name").toLowerCase(); + + if (os.indexOf("win") >= 0) { + platform = Platform.WINDOWS; + } + else if (os.indexOf("nix") >= 0 || os.indexOf("nux") >= 0) { + platform = Platform.LINUX; + } + else if (os.indexOf("mac") >= 0) { + platform = Platform.MAC; + } + else { + platform = Platform.UNKNOWN; + } + + String version = System.getProperty("os.version").toString(); + String[] parts = version.split(Pattern.quote(".")); + + if (parts.length > 0) { + majorVersion = Integer.parseInt(parts[0]); + + if (parts.length > 1) { + minorVersion = Integer.parseInt(parts[1]); + } + else { + minorVersion = -1; + } + } + else { + majorVersion = -1; + minorVersion = -1; + } + } + + private Platform() {} + + static Platform getPlatform() { + return platform; + } + + static int getMajorVersion() { + return majorVersion; + } + + static int getMinorVersion() { + return minorVersion; + } + + static boolean isWindows() { + return getPlatform() == WINDOWS; + } + + static boolean isMac() { + return getPlatform() == MAC; + } + + static boolean isLinux() { + return getPlatform() == LINUX; + } + + static RuntimeException throwUnknownPlatformError() { + throw new IllegalArgumentException("Unknown platform"); + } +} --- old/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-11-18 20:58:07.537983000 -0500 +++ /dev/null 2019-11-18 20:58:08.000000000 -0500 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include - -extern "C" { - - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved) { - return true; - } -} \ No newline at end of file --- /dev/null 2019-11-18 20:58:09.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/PlatformPackage.java 2019-11-18 20:58:04.040854700 -0500 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.nio.file.Path; + +/** + * + * Platform package of an application. + */ +interface PlatformPackage { + /** + * Platform-specific package name. + */ + String name(); + + /** + * Root directory where sources for packaging tool should be stored + */ + Path sourceRoot(); + + /** + * Source application layout from which to build the package. + */ + ApplicationLayout sourceApplicationLayout(); + + /** + * Application layout of the installed package. + */ + ApplicationLayout installedApplicationLayout(); +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RelativeFileSet.java 2019-11-18 20:58:28.841807400 -0500 +++ /dev/null 2019-11-18 20:58:30.000000000 -0500 @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * RelativeFileSet - * - * A class encapsulating a directory and a set of files within it. - */ -class RelativeFileSet { - - private File basedir; - private Set files = new LinkedHashSet<>(); - - RelativeFileSet(RelativeFileSet copy) { - basedir = copy.basedir; - files = new LinkedHashSet<>(copy.files); - } - - RelativeFileSet(File base, Collection files) { - basedir = base; - String baseAbsolute = basedir.getAbsolutePath(); - for (File f: files) { - String absolute = f.getAbsolutePath(); - if (!absolute.startsWith(baseAbsolute)) { - throw new RuntimeException("File " + f.getAbsolutePath() + - " does not belong to " + baseAbsolute); - } - if (!absolute.equals(baseAbsolute)) { - // possible in jpackage case - this.files.add(absolute.substring(baseAbsolute.length()+1)); - } - } - } - - void upshift() { - String root = basedir.getName(); - basedir = basedir.getParentFile(); - Set newFiles = new LinkedHashSet<>(); - for (String s : files) { - newFiles.add(root + File.separator + s); - } - files = newFiles; - } - - RelativeFileSet(File base, Set files) { - this(base, (Collection) files); - } - - boolean contains(String[] requiredFiles) { - boolean result = true; - - for(String fname: requiredFiles) { - if (!files.contains(fname)) { - Log.debug(" RelativeFileSet does not contain [" + fname + "]"); - result = false; - } - } - - return result; - } - - boolean contains(String requiredFile) { - if (files.contains(requiredFile)) { - return true; - } else { - Log.debug("RelativeFileSet does not contain [" +requiredFile+ "]"); - return false; - } - } - - File getBaseDirectory() { - return basedir; - } - - Set getIncludedFiles() { - return files; - } - - @Override - public String toString() { - if (files.size() == 1) { - return "" + basedir + File.pathSeparator + files; - } - return "RelativeFileSet {basedir:" + basedir - + ", files: {" + files + "}"; - } - -} --- /dev/null 2019-11-18 20:58:30.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/RelativeFileSet.java 2019-11-18 20:58:25.298532600 -0500 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * RelativeFileSet + * + * A class encapsulating a directory and a set of files within it. + */ +class RelativeFileSet { + + private File basedir; + private Set files = new LinkedHashSet<>(); + + RelativeFileSet(File base, Collection files) { + basedir = base; + String baseAbsolute = basedir.getAbsolutePath(); + for (File f: files) { + String absolute = f.getAbsolutePath(); + if (!absolute.startsWith(baseAbsolute)) { + throw new RuntimeException("File " + f.getAbsolutePath() + + " does not belong to " + baseAbsolute); + } + if (!absolute.equals(baseAbsolute)) { + // possible in jpackage case + this.files.add(absolute.substring(baseAbsolute.length()+1)); + } + } + } + + RelativeFileSet(File base, Set files) { + this(base, (Collection) files); + } + + File getBaseDirectory() { + return basedir; + } + + Set getIncludedFiles() { + return files; + } + + @Override + public String toString() { + if (files.size() == 1) { + return "" + basedir + File.pathSeparator + files; + } + return "RelativeFileSet {basedir:" + basedir + + ", files: {" + files + "}"; + } + +} --- /dev/null 2019-11-18 20:58:50.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/ScriptRunner.java 2019-11-18 20:58:46.794775400 -0500 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; +import static jdk.incubator.jpackage.internal.StandardBundlerParam.APP_NAME; +import static jdk.incubator.jpackage.internal.StandardBundlerParam.CONFIG_ROOT; + +/** + * Runs custom script from resource directory. + */ +class ScriptRunner { + ScriptRunner() { + environment = new ProcessBuilder().environment(); + } + + ScriptRunner setResourceCategoryId(String v) { + resourceCategoryId = v; + return this; + } + + ScriptRunner setDirectory(Path v) { + directory = v; + return this; + } + + ScriptRunner setScriptNameSuffix(String v) { + scriptNameSuffix = v; + return this; + } + + ScriptRunner addEnvironment(Map v) { + environment.putAll(v); + return this; + } + + ScriptRunner setEnvironmentVariable(String envVarName, String envVarValue) { + Objects.requireNonNull(envVarName); + if (envVarValue == null) { + environment.remove(envVarName); + } else { + environment.put(envVarName, envVarValue); + } + return this; + } + + public void run(Map params) throws IOException { + String scriptName = String.format("%s-%s%s", APP_NAME.fetchFrom(params), + scriptNameSuffix, scriptSuffix()); + Path scriptPath = CONFIG_ROOT.fetchFrom(params).toPath().resolve( + scriptName); + createResource(null, params) + .setCategory(I18N.getString(resourceCategoryId)) + .saveToFile(scriptPath); + if (!Files.exists(scriptPath)) { + return; + } + + ProcessBuilder pb = new ProcessBuilder(shell(), + scriptPath.toAbsolutePath().toString()); + Map workEnvironment = pb.environment(); + workEnvironment.clear(); + workEnvironment.putAll(environment); + + if (directory != null) { + pb.directory(directory.toFile()); + } + + Executor.of(pb).executeExpectSuccess(); + } + + private static String shell() { + if (Platform.isWindows()) { + return "cscript"; + } + return Optional.ofNullable(System.getenv("SHELL")).orElseGet(() -> "sh"); + } + + private static String scriptSuffix() { + if (Platform.isWindows()) { + return ".wsf"; + } + return ".sh"; + } + + private String scriptNameSuffix; + private String resourceCategoryId; + private Path directory; + private Map environment; +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java 2019-11-18 20:59:01.092370000 -0500 +++ /dev/null 2019-11-18 20:59:02.000000000 -0500 @@ -1,771 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import jdk.jpackage.internal.BundleParams; -import jdk.jpackage.internal.AbstractAppImageBuilder; - -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.ResourceBundle; -import java.util.Set; -import java.util.HashSet; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -/** - * StandardBundlerParam - * - * A parameter to a bundler. - * - * Also contains static definitions of all of the common bundler parameters. - * (additional platform specific and mode specific bundler parameters - * are defined in each of the specific bundlers) - * - * Also contains static methods that operate on maps of parameters. - */ -class StandardBundlerParam extends BundlerParamInfo { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); - private static final String JAVABASEJMOD = "java.base.jmod"; - - StandardBundlerParam(String id, Class valueType, - Function, T> defaultValueFunction, - BiFunction, T> stringConverter) - { - this.id = id; - this.valueType = valueType; - this.defaultValueFunction = defaultValueFunction; - this.stringConverter = stringConverter; - } - - static final StandardBundlerParam APP_RESOURCES = - new StandardBundlerParam<>( - BundleParams.PARAM_APP_RESOURCES, - RelativeFileSet.class, - null, // no default. Required parameter - null // no string translation, - // tool must provide complex type - ); - - @SuppressWarnings("unchecked") - static final - StandardBundlerParam> APP_RESOURCES_LIST = - new StandardBundlerParam<>( - BundleParams.PARAM_APP_RESOURCES + "List", - (Class>) (Object) List.class, - // Default is appResources, as a single item list - p -> new ArrayList<>(Collections.singletonList( - APP_RESOURCES.fetchFrom(p))), - StandardBundlerParam::createAppResourcesListFromString - ); - - static final StandardBundlerParam SOURCE_DIR = - new StandardBundlerParam<>( - Arguments.CLIOptions.INPUT.getId(), - String.class, - p -> null, - (s, p) -> { - String value = String.valueOf(s); - if (value.charAt(value.length() - 1) == - File.separatorChar) { - return value.substring(0, value.length() - 1); - } - else { - return value; - } - } - ); - - // note that each bundler is likely to replace this one with - // their own converter - static final StandardBundlerParam MAIN_JAR = - new StandardBundlerParam<>( - Arguments.CLIOptions.MAIN_JAR.getId(), - RelativeFileSet.class, - params -> { - extractMainClassInfoFromAppResources(params); - return (RelativeFileSet) params.get("mainJar"); - }, - (s, p) -> getMainJar(s, p) - ); - - // TODO: test CLASSPATH jar manifest Attributet - static final StandardBundlerParam CLASSPATH = - new StandardBundlerParam<>( - "classpath", - String.class, - params -> { - extractMainClassInfoFromAppResources(params); - String cp = (String) params.get("classpath"); - return cp == null ? "" : cp; - }, - (s, p) -> s.replace(File.pathSeparator, " ") - ); - - static final StandardBundlerParam MAIN_CLASS = - new StandardBundlerParam<>( - Arguments.CLIOptions.APPCLASS.getId(), - String.class, - params -> { - if (isRuntimeInstaller(params)) { - return null; - } - extractMainClassInfoFromAppResources(params); - String s = (String) params.get( - BundleParams.PARAM_APPLICATION_CLASS); - if (s == null) { - s = JLinkBundlerHelper.getMainClass(params); - } - return s; - }, - (s, p) -> s - ); - - static final StandardBundlerParam PREDEFINED_RUNTIME_IMAGE = - new StandardBundlerParam<>( - Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(), - File.class, - params -> null, - (s, p) -> new File(s) - ); - - static final StandardBundlerParam APP_NAME = - new StandardBundlerParam<>( - Arguments.CLIOptions.NAME.getId(), - String.class, - params -> { - String s = MAIN_CLASS.fetchFrom(params); - if (s != null) { - int idx = s.lastIndexOf("."); - if (idx >= 0) { - return s.substring(idx+1); - } - return s; - } else if (isRuntimeInstaller(params)) { - File f = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params); - if (f != null) { - return f.getName(); - } - } - return null; - }, - (s, p) -> s - ); - - static final StandardBundlerParam ICON = - new StandardBundlerParam<>( - Arguments.CLIOptions.ICON.getId(), - File.class, - params -> null, - (s, p) -> new File(s) - ); - - static final StandardBundlerParam VENDOR = - new StandardBundlerParam<>( - Arguments.CLIOptions.VENDOR.getId(), - String.class, - params -> I18N.getString("param.vendor.default"), - (s, p) -> s - ); - - static final StandardBundlerParam DESCRIPTION = - new StandardBundlerParam<>( - Arguments.CLIOptions.DESCRIPTION.getId(), - String.class, - params -> params.containsKey(APP_NAME.getID()) - ? APP_NAME.fetchFrom(params) - : I18N.getString("param.description.default"), - (s, p) -> s - ); - - static final StandardBundlerParam COPYRIGHT = - new StandardBundlerParam<>( - Arguments.CLIOptions.COPYRIGHT.getId(), - String.class, - params -> MessageFormat.format(I18N.getString( - "param.copyright.default"), new Date()), - (s, p) -> s - ); - - @SuppressWarnings("unchecked") - static final StandardBundlerParam> ARGUMENTS = - new StandardBundlerParam<>( - Arguments.CLIOptions.ARGUMENTS.getId(), - (Class>) (Object) List.class, - params -> Collections.emptyList(), - (s, p) -> splitStringWithEscapes(s) - ); - - @SuppressWarnings("unchecked") - static final StandardBundlerParam> JAVA_OPTIONS = - new StandardBundlerParam<>( - Arguments.CLIOptions.JAVA_OPTIONS.getId(), - (Class>) (Object) List.class, - params -> Collections.emptyList(), - (s, p) -> Arrays.asList(s.split("\n\n")) - ); - - // note that each bundler is likely to replace this one with - // their own converter - static final StandardBundlerParam VERSION = - new StandardBundlerParam<>( - Arguments.CLIOptions.VERSION.getId(), - String.class, - params -> I18N.getString("param.version.default"), - (s, p) -> s - ); - - @SuppressWarnings("unchecked") - public static final StandardBundlerParam LICENSE_FILE = - new StandardBundlerParam<>( - Arguments.CLIOptions.LICENSE_FILE.getId(), - String.class, - params -> null, - (s, p) -> s - ); - - static final StandardBundlerParam TEMP_ROOT = - new StandardBundlerParam<>( - Arguments.CLIOptions.TEMP_ROOT.getId(), - File.class, - params -> { - try { - return Files.createTempDirectory( - "jdk.jpackage").toFile(); - } catch (IOException ioe) { - return null; - } - }, - (s, p) -> new File(s) - ); - - public static final StandardBundlerParam CONFIG_ROOT = - new StandardBundlerParam<>( - "configRoot", - File.class, - params -> { - File root = - new File(TEMP_ROOT.fetchFrom(params), "config"); - root.mkdirs(); - return root; - }, - (s, p) -> null - ); - - static final StandardBundlerParam IDENTIFIER = - new StandardBundlerParam<>( - Arguments.CLIOptions.IDENTIFIER.getId(), - String.class, - params -> { - String s = MAIN_CLASS.fetchFrom(params); - if (s == null) return null; - - int idx = s.lastIndexOf("."); - if (idx >= 1) { - return s.substring(0, idx); - } - return s; - }, - (s, p) -> s - ); - - static final StandardBundlerParam VERBOSE = - new StandardBundlerParam<>( - Arguments.CLIOptions.VERBOSE.getId(), - Boolean.class, - params -> false, - // valueOf(null) is false, and we actually do want null - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? - true : Boolean.valueOf(s) - ); - - static final StandardBundlerParam RESOURCE_DIR = - new StandardBundlerParam<>( - Arguments.CLIOptions.RESOURCE_DIR.getId(), - File.class, - params -> null, - (s, p) -> new File(s) - ); - - static final BundlerParamInfo INSTALL_DIR = - new StandardBundlerParam<>( - Arguments.CLIOptions.INSTALL_DIR.getId(), - String.class, - params -> null, - (s, p) -> s - ); - - static final StandardBundlerParam PREDEFINED_APP_IMAGE = - new StandardBundlerParam<>( - Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId(), - File.class, - params -> null, - (s, p) -> new File(s)); - - @SuppressWarnings("unchecked") - static final StandardBundlerParam>> ADD_LAUNCHERS = - new StandardBundlerParam<>( - Arguments.CLIOptions.ADD_LAUNCHER.getId(), - (Class>>) (Object) - List.class, - params -> new ArrayList<>(1), - // valueOf(null) is false, and we actually do want null - (s, p) -> null - ); - - @SuppressWarnings("unchecked") - static final StandardBundlerParam - >> FILE_ASSOCIATIONS = - new StandardBundlerParam<>( - Arguments.CLIOptions.FILE_ASSOCIATIONS.getId(), - (Class>>) (Object) - List.class, - params -> new ArrayList<>(1), - // valueOf(null) is false, and we actually do want null - (s, p) -> null - ); - - @SuppressWarnings("unchecked") - static final StandardBundlerParam> FA_EXTENSIONS = - new StandardBundlerParam<>( - "fileAssociation.extension", - (Class>) (Object) List.class, - params -> null, // null means not matched to an extension - (s, p) -> Arrays.asList(s.split("(,|\\s)+")) - ); - - @SuppressWarnings("unchecked") - static final StandardBundlerParam> FA_CONTENT_TYPE = - new StandardBundlerParam<>( - "fileAssociation.contentType", - (Class>) (Object) List.class, - params -> null, - // null means not matched to a content/mime type - (s, p) -> Arrays.asList(s.split("(,|\\s)+")) - ); - - static final StandardBundlerParam FA_DESCRIPTION = - new StandardBundlerParam<>( - "fileAssociation.description", - String.class, - params -> APP_NAME.fetchFrom(params) + " File", - null - ); - - static final StandardBundlerParam FA_ICON = - new StandardBundlerParam<>( - "fileAssociation.icon", - File.class, - ICON::fetchFrom, - (s, p) -> new File(s) - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> MODULE_PATH = - new StandardBundlerParam<>( - Arguments.CLIOptions.MODULE_PATH.getId(), - (Class>) (Object)List.class, - p -> { return getDefaultModulePath(); }, - (s, p) -> { - List modulePath = Arrays.asList(s - .split(File.pathSeparator)).stream() - .map(ss -> new File(ss).toPath()) - .collect(Collectors.toList()); - Path javaBasePath = null; - if (modulePath != null) { - javaBasePath = JLinkBundlerHelper - .findPathOfModule(modulePath, JAVABASEJMOD); - } else { - modulePath = new ArrayList(); - } - - // Add the default JDK module path to the module path. - if (javaBasePath == null) { - List jdkModulePath = getDefaultModulePath(); - - if (jdkModulePath != null) { - modulePath.addAll(jdkModulePath); - javaBasePath = - JLinkBundlerHelper.findPathOfModule( - modulePath, JAVABASEJMOD); - } - } - - if (javaBasePath == null || - !Files.exists(javaBasePath)) { - Log.error(String.format(I18N.getString( - "warning.no.jdk.modules.found"))); - } - - return modulePath; - }); - - static final BundlerParamInfo MODULE = - new StandardBundlerParam<>( - Arguments.CLIOptions.MODULE.getId(), - String.class, - p -> null, - (s, p) -> { - return String.valueOf(s); - }); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> ADD_MODULES = - new StandardBundlerParam<>( - Arguments.CLIOptions.ADD_MODULES.getId(), - (Class>) (Object) Set.class, - p -> new LinkedHashSet(), - (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(","))) - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> LIMIT_MODULES = - new StandardBundlerParam<>( - "limit-modules", - (Class>) (Object) Set.class, - p -> new LinkedHashSet(), - (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(","))) - ); - - static boolean isRuntimeInstaller(Map p) { - if (p.containsKey(MODULE.getID()) || - p.containsKey(MAIN_JAR.getID()) || - p.containsKey(PREDEFINED_APP_IMAGE.getID())) { - return false; // we are building or are given an application - } - // runtime installer requires --runtime-image, if this is false - // here then we should have thrown error validating args. - return p.containsKey(PREDEFINED_RUNTIME_IMAGE.getID()); - } - - static File getPredefinedAppImage(Map p) { - File applicationImage = null; - if (PREDEFINED_APP_IMAGE.fetchFrom(p) != null) { - applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(p); - Log.debug("Using App Image from " + applicationImage); - if (!applicationImage.exists()) { - throw new RuntimeException( - MessageFormat.format(I18N.getString( - "message.app-image-dir-does-not-exist"), - PREDEFINED_APP_IMAGE.getID(), - applicationImage.toString())); - } - } - return applicationImage; - } - - static void copyPredefinedRuntimeImage( - Map p, - AbstractAppImageBuilder appBuilder) - throws IOException , ConfigException { - File image = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); - if (!image.exists()) { - throw new ConfigException( - MessageFormat.format(I18N.getString( - "message.runtime-image-dir-does-not-exist"), - PREDEFINED_RUNTIME_IMAGE.getID(), - image.toString()), - MessageFormat.format(I18N.getString( - "message.runtime-image-dir-does-not-exist.advice"), - PREDEFINED_RUNTIME_IMAGE.getID())); - } - // copy whole runtime, need to skip jmods and src.zip - final List excludes = Arrays.asList("jmods", "src.zip"); - IOUtils.copyRecursive(image.toPath(), appBuilder.getRoot(), excludes); - - // if module-path given - copy modules to appDir/mods - List modulePath = - StandardBundlerParam.MODULE_PATH.fetchFrom(p); - List defaultModulePath = getDefaultModulePath(); - Path dest = appBuilder.getAppModsDir(); - - if (dest != null) { - for (Path mp : modulePath) { - if (!defaultModulePath.contains(mp)) { - Files.createDirectories(dest); - IOUtils.copyRecursive(mp, dest); - } - } - } - - appBuilder.prepareApplicationFiles(); - } - - static void extractMainClassInfoFromAppResources( - Map params) { - boolean hasMainClass = params.containsKey(MAIN_CLASS.getID()); - boolean hasMainJar = params.containsKey(MAIN_JAR.getID()); - boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID()); - boolean hasModule = params.containsKey(MODULE.getID()); - - if (hasMainClass && hasMainJar && hasMainJarClassPath || hasModule || - isRuntimeInstaller(params)) { - return; - } - - // it's a pair. - // The [0] is the srcdir [1] is the file relative to sourcedir - List filesToCheck = new ArrayList<>(); - - if (hasMainJar) { - RelativeFileSet rfs = MAIN_JAR.fetchFrom(params); - for (String s : rfs.getIncludedFiles()) { - filesToCheck.add( - new String[] {rfs.getBaseDirectory().toString(), s}); - } - } else if (hasMainJarClassPath) { - for (String s : CLASSPATH.fetchFrom(params).split("\\s+")) { - if (APP_RESOURCES.fetchFrom(params) != null) { - filesToCheck.add( - new String[] {APP_RESOURCES.fetchFrom(params) - .getBaseDirectory().toString(), s}); - } - } - } else { - List rfsl = APP_RESOURCES_LIST.fetchFrom(params); - if (rfsl == null || rfsl.isEmpty()) { - return; - } - for (RelativeFileSet rfs : rfsl) { - if (rfs == null) continue; - - for (String s : rfs.getIncludedFiles()) { - filesToCheck.add( - new String[]{rfs.getBaseDirectory().toString(), s}); - } - } - } - - // presume the set iterates in-order - for (String[] fnames : filesToCheck) { - try { - // only sniff jars - if (!fnames[1].toLowerCase().endsWith(".jar")) continue; - - File file = new File(fnames[0], fnames[1]); - // that actually exist - if (!file.exists()) continue; - - try (JarFile jf = new JarFile(file)) { - Manifest m = jf.getManifest(); - Attributes attrs = (m != null) ? - m.getMainAttributes() : null; - - if (attrs != null) { - if (!hasMainJar) { - if (fnames[0] == null) { - fnames[0] = file.getParentFile().toString(); - } - params.put(MAIN_JAR.getID(), new RelativeFileSet( - new File(fnames[0]), - new LinkedHashSet<>(Collections - .singletonList(file)))); - } - if (!hasMainJarClassPath) { - String cp = - attrs.getValue(Attributes.Name.CLASS_PATH); - params.put(CLASSPATH.getID(), - cp == null ? "" : cp); - } - break; - } - } - } catch (IOException ignore) { - ignore.printStackTrace(); - } - } - } - - static void validateMainClassInfoFromAppResources( - Map params) throws ConfigException { - boolean hasMainClass = params.containsKey(MAIN_CLASS.getID()); - boolean hasMainJar = params.containsKey(MAIN_JAR.getID()); - boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID()); - boolean hasModule = params.containsKey(MODULE.getID()); - boolean hasAppImage = params.containsKey(PREDEFINED_APP_IMAGE.getID()); - - if (hasMainClass && hasMainJar && hasMainJarClassPath || - hasModule || hasAppImage || isRuntimeInstaller(params)) { - return; - } - - extractMainClassInfoFromAppResources(params); - - if (!params.containsKey(MAIN_CLASS.getID())) { - if (hasMainJar) { - throw new ConfigException( - MessageFormat.format(I18N.getString( - "error.no-main-class-with-main-jar"), - MAIN_JAR.fetchFrom(params)), - MessageFormat.format(I18N.getString( - "error.no-main-class-with-main-jar.advice"), - MAIN_JAR.fetchFrom(params))); - } else { - throw new ConfigException( - I18N.getString("error.no-main-class"), - I18N.getString("error.no-main-class.advice")); - } - } - } - - private static List splitStringWithEscapes(String s) { - List l = new ArrayList<>(); - StringBuilder current = new StringBuilder(); - boolean quoted = false; - boolean escaped = false; - for (char c : s.toCharArray()) { - if (escaped) { - current.append(c); - } else if ('"' == c) { - quoted = !quoted; - } else if (!quoted && Character.isWhitespace(c)) { - l.add(current.toString()); - current = new StringBuilder(); - } else { - current.append(c); - } - } - l.add(current.toString()); - return l; - } - - private static List - createAppResourcesListFromString(String s, - Map objectObjectMap) { - List result = new ArrayList<>(); - for (String path : s.split("[:;]")) { - File f = new File(path); - if (f.getName().equals("*") || path.endsWith("/") || - path.endsWith("\\")) { - if (f.getName().equals("*")) { - f = f.getParentFile(); - } - Set theFiles = new HashSet<>(); - try { - Files.walk(f.toPath()) - .filter(Files::isRegularFile) - .forEach(p -> theFiles.add(p.toFile())); - } catch (IOException e) { - e.printStackTrace(); - } - result.add(new RelativeFileSet(f, theFiles)); - } else { - result.add(new RelativeFileSet(f.getParentFile(), - Collections.singleton(f))); - } - } - return result; - } - - private static RelativeFileSet getMainJar( - String mainJarValue, Map params) { - for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(params)) { - File appResourcesRoot = rfs.getBaseDirectory(); - File mainJarFile = new File(appResourcesRoot, mainJarValue); - - if (mainJarFile.exists()) { - return new RelativeFileSet(appResourcesRoot, - new LinkedHashSet<>(Collections.singletonList( - mainJarFile))); - } - mainJarFile = new File(mainJarValue); - if (mainJarFile.exists()) { - // absolute path for main-jar may fail is not legal - // below contains explicit error message. - } else { - List modulePath = MODULE_PATH.fetchFrom(params); - modulePath.removeAll(getDefaultModulePath()); - if (!modulePath.isEmpty()) { - Path modularJarPath = JLinkBundlerHelper.findPathOfModule( - modulePath, mainJarValue); - if (modularJarPath != null && - Files.exists(modularJarPath)) { - return new RelativeFileSet(appResourcesRoot, - new LinkedHashSet<>(Collections.singletonList( - modularJarPath.toFile()))); - } - } - } - } - - throw new IllegalArgumentException( - new ConfigException(MessageFormat.format(I18N.getString( - "error.main-jar-does-not-exist"), - mainJarValue), I18N.getString( - "error.main-jar-does-not-exist.advice"))); - } - - static List getDefaultModulePath() { - List result = new ArrayList(); - Path jdkModulePath = Paths.get( - System.getProperty("java.home"), "jmods").toAbsolutePath(); - - if (jdkModulePath != null && Files.exists(jdkModulePath)) { - result.add(jdkModulePath); - } - else { - // On a developer build the JDK Home isn't where we expect it - // relative to the jmods directory. Do some extra - // processing to find it. - Map env = System.getenv(); - - if (env.containsKey("JDK_HOME")) { - jdkModulePath = Paths.get(env.get("JDK_HOME"), - ".." + File.separator + "images" - + File.separator + "jmods").toAbsolutePath(); - - if (jdkModulePath != null && Files.exists(jdkModulePath)) { - result.add(jdkModulePath); - } - } - } - - return result; - } -} --- /dev/null 2019-11-18 20:59:02.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/StandardBundlerParam.java 2019-11-18 20:58:57.610642700 -0500 @@ -0,0 +1,790 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.IOException; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleDescriptor.Version; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.HashSet; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * StandardBundlerParam + * + * A parameter to a bundler. + * + * Also contains static definitions of all of the common bundler parameters. + * (additional platform specific and mode specific bundler parameters + * are defined in each of the specific bundlers) + * + * Also contains static methods that operate on maps of parameters. + */ +class StandardBundlerParam extends BundlerParamInfo { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MainResources"); + private static final String JAVABASEJMOD = "java.base.jmod"; + private final static String DEFAULT_VERSION = "1.0"; + private final static String DEFAULT_RELEASE = "1"; + + StandardBundlerParam(String id, Class valueType, + Function, T> defaultValueFunction, + BiFunction, T> stringConverter) + { + this.id = id; + this.valueType = valueType; + this.defaultValueFunction = defaultValueFunction; + this.stringConverter = stringConverter; + } + + static final StandardBundlerParam APP_RESOURCES = + new StandardBundlerParam<>( + BundleParams.PARAM_APP_RESOURCES, + RelativeFileSet.class, + null, // no default. Required parameter + null // no string translation, + // tool must provide complex type + ); + + @SuppressWarnings("unchecked") + static final + StandardBundlerParam> APP_RESOURCES_LIST = + new StandardBundlerParam<>( + BundleParams.PARAM_APP_RESOURCES + "List", + (Class>) (Object) List.class, + // Default is appResources, as a single item list + p -> new ArrayList<>(Collections.singletonList( + APP_RESOURCES.fetchFrom(p))), + StandardBundlerParam::createAppResourcesListFromString + ); + + static final StandardBundlerParam SOURCE_DIR = + new StandardBundlerParam<>( + Arguments.CLIOptions.INPUT.getId(), + String.class, + p -> null, + (s, p) -> { + String value = String.valueOf(s); + if (value.charAt(value.length() - 1) == + File.separatorChar) { + return value.substring(0, value.length() - 1); + } + else { + return value; + } + } + ); + + // note that each bundler is likely to replace this one with + // their own converter + static final StandardBundlerParam MAIN_JAR = + new StandardBundlerParam<>( + Arguments.CLIOptions.MAIN_JAR.getId(), + RelativeFileSet.class, + params -> { + extractMainClassInfoFromAppResources(params); + return (RelativeFileSet) params.get("mainJar"); + }, + (s, p) -> getMainJar(s, p) + ); + + static final StandardBundlerParam CLASSPATH = + new StandardBundlerParam<>( + "classpath", + String.class, + params -> { + extractMainClassInfoFromAppResources(params); + String cp = (String) params.get("classpath"); + return cp == null ? "" : cp; + }, + (s, p) -> s + ); + + static final StandardBundlerParam MAIN_CLASS = + new StandardBundlerParam<>( + Arguments.CLIOptions.APPCLASS.getId(), + String.class, + params -> { + if (isRuntimeInstaller(params)) { + return null; + } + extractMainClassInfoFromAppResources(params); + String s = (String) params.get( + BundleParams.PARAM_APPLICATION_CLASS); + if (s == null) { + s = JLinkBundlerHelper.getMainClassFromModule( + params); + } + return s; + }, + (s, p) -> s + ); + + static final StandardBundlerParam PREDEFINED_RUNTIME_IMAGE = + new StandardBundlerParam<>( + Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(), + File.class, + params -> null, + (s, p) -> new File(s) + ); + + static final StandardBundlerParam APP_NAME = + new StandardBundlerParam<>( + Arguments.CLIOptions.NAME.getId(), + String.class, + params -> { + String s = MAIN_CLASS.fetchFrom(params); + if (s != null) { + int idx = s.lastIndexOf("."); + if (idx >= 0) { + return s.substring(idx+1); + } + return s; + } else if (isRuntimeInstaller(params)) { + File f = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params); + if (f != null) { + return f.getName(); + } + } + return null; + }, + (s, p) -> s + ); + + static final StandardBundlerParam ICON = + new StandardBundlerParam<>( + Arguments.CLIOptions.ICON.getId(), + File.class, + params -> null, + (s, p) -> new File(s) + ); + + static final StandardBundlerParam VENDOR = + new StandardBundlerParam<>( + Arguments.CLIOptions.VENDOR.getId(), + String.class, + params -> I18N.getString("param.vendor.default"), + (s, p) -> s + ); + + static final StandardBundlerParam DESCRIPTION = + new StandardBundlerParam<>( + Arguments.CLIOptions.DESCRIPTION.getId(), + String.class, + params -> params.containsKey(APP_NAME.getID()) + ? APP_NAME.fetchFrom(params) + : I18N.getString("param.description.default"), + (s, p) -> s + ); + + static final StandardBundlerParam COPYRIGHT = + new StandardBundlerParam<>( + Arguments.CLIOptions.COPYRIGHT.getId(), + String.class, + params -> MessageFormat.format(I18N.getString( + "param.copyright.default"), new Date()), + (s, p) -> s + ); + + @SuppressWarnings("unchecked") + static final StandardBundlerParam> ARGUMENTS = + new StandardBundlerParam<>( + Arguments.CLIOptions.ARGUMENTS.getId(), + (Class>) (Object) List.class, + params -> Collections.emptyList(), + (s, p) -> null + ); + + @SuppressWarnings("unchecked") + static final StandardBundlerParam> JAVA_OPTIONS = + new StandardBundlerParam<>( + Arguments.CLIOptions.JAVA_OPTIONS.getId(), + (Class>) (Object) List.class, + params -> Collections.emptyList(), + (s, p) -> Arrays.asList(s.split("\n\n")) + ); + + // note that each bundler is likely to replace this one with + // their own converter + static final StandardBundlerParam VERSION = + new StandardBundlerParam<>( + Arguments.CLIOptions.VERSION.getId(), + String.class, + params -> getDefaultAppVersion(params), + (s, p) -> s + ); + + static final StandardBundlerParam RELEASE = + new StandardBundlerParam<>( + Arguments.CLIOptions.RELEASE.getId(), + String.class, + params -> DEFAULT_RELEASE, + (s, p) -> s + ); + + @SuppressWarnings("unchecked") + public static final StandardBundlerParam LICENSE_FILE = + new StandardBundlerParam<>( + Arguments.CLIOptions.LICENSE_FILE.getId(), + String.class, + params -> null, + (s, p) -> s + ); + + static final StandardBundlerParam TEMP_ROOT = + new StandardBundlerParam<>( + Arguments.CLIOptions.TEMP_ROOT.getId(), + File.class, + params -> { + try { + return Files.createTempDirectory( + "jdk.incubator.jpackage").toFile(); + } catch (IOException ioe) { + return null; + } + }, + (s, p) -> new File(s) + ); + + public static final StandardBundlerParam CONFIG_ROOT = + new StandardBundlerParam<>( + "configRoot", + File.class, + params -> { + File root = + new File(TEMP_ROOT.fetchFrom(params), "config"); + root.mkdirs(); + return root; + }, + (s, p) -> null + ); + + static final StandardBundlerParam IDENTIFIER = + new StandardBundlerParam<>( + "identifier.default", + String.class, + params -> { + String s = MAIN_CLASS.fetchFrom(params); + if (s == null) return null; + + int idx = s.lastIndexOf("."); + if (idx >= 1) { + return s.substring(0, idx); + } + return s; + }, + (s, p) -> s + ); + + static final StandardBundlerParam BIND_SERVICES = + new StandardBundlerParam<>( + Arguments.CLIOptions.BIND_SERVICES.getId(), + Boolean.class, + params -> false, + (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? + true : Boolean.valueOf(s) + ); + + + static final StandardBundlerParam VERBOSE = + new StandardBundlerParam<>( + Arguments.CLIOptions.VERBOSE.getId(), + Boolean.class, + params -> false, + // valueOf(null) is false, and we actually do want null + (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? + true : Boolean.valueOf(s) + ); + + static final StandardBundlerParam RESOURCE_DIR = + new StandardBundlerParam<>( + Arguments.CLIOptions.RESOURCE_DIR.getId(), + File.class, + params -> null, + (s, p) -> new File(s) + ); + + static final BundlerParamInfo INSTALL_DIR = + new StandardBundlerParam<>( + Arguments.CLIOptions.INSTALL_DIR.getId(), + String.class, + params -> null, + (s, p) -> s + ); + + static final StandardBundlerParam PREDEFINED_APP_IMAGE = + new StandardBundlerParam<>( + Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId(), + File.class, + params -> null, + (s, p) -> new File(s)); + + @SuppressWarnings("unchecked") + static final StandardBundlerParam>> ADD_LAUNCHERS = + new StandardBundlerParam<>( + Arguments.CLIOptions.ADD_LAUNCHER.getId(), + (Class>>) (Object) + List.class, + params -> new ArrayList<>(1), + // valueOf(null) is false, and we actually do want null + (s, p) -> null + ); + + @SuppressWarnings("unchecked") + static final StandardBundlerParam + >> FILE_ASSOCIATIONS = + new StandardBundlerParam<>( + Arguments.CLIOptions.FILE_ASSOCIATIONS.getId(), + (Class>>) (Object) + List.class, + params -> new ArrayList<>(1), + // valueOf(null) is false, and we actually do want null + (s, p) -> null + ); + + @SuppressWarnings("unchecked") + static final StandardBundlerParam> FA_EXTENSIONS = + new StandardBundlerParam<>( + "fileAssociation.extension", + (Class>) (Object) List.class, + params -> null, // null means not matched to an extension + (s, p) -> Arrays.asList(s.split("(,|\\s)+")) + ); + + @SuppressWarnings("unchecked") + static final StandardBundlerParam> FA_CONTENT_TYPE = + new StandardBundlerParam<>( + "fileAssociation.contentType", + (Class>) (Object) List.class, + params -> null, + // null means not matched to a content/mime type + (s, p) -> Arrays.asList(s.split("(,|\\s)+")) + ); + + static final StandardBundlerParam FA_DESCRIPTION = + new StandardBundlerParam<>( + "fileAssociation.description", + String.class, + params -> APP_NAME.fetchFrom(params) + " File", + null + ); + + static final StandardBundlerParam FA_ICON = + new StandardBundlerParam<>( + "fileAssociation.icon", + File.class, + ICON::fetchFrom, + (s, p) -> new File(s) + ); + + @SuppressWarnings("unchecked") + static final BundlerParamInfo> MODULE_PATH = + new StandardBundlerParam<>( + Arguments.CLIOptions.MODULE_PATH.getId(), + (Class>) (Object)List.class, + p -> { return getDefaultModulePath(); }, + (s, p) -> { + List modulePath = Arrays.asList(s + .split(File.pathSeparator)).stream() + .map(ss -> new File(ss).toPath()) + .collect(Collectors.toList()); + Path javaBasePath = null; + if (modulePath != null) { + javaBasePath = JLinkBundlerHelper + .findPathOfModule(modulePath, JAVABASEJMOD); + } else { + modulePath = new ArrayList(); + } + + // Add the default JDK module path to the module path. + if (javaBasePath == null) { + List jdkModulePath = getDefaultModulePath(); + + if (jdkModulePath != null) { + modulePath.addAll(jdkModulePath); + javaBasePath = + JLinkBundlerHelper.findPathOfModule( + modulePath, JAVABASEJMOD); + } + } + + if (javaBasePath == null || + !Files.exists(javaBasePath)) { + Log.error(String.format(I18N.getString( + "warning.no.jdk.modules.found"))); + } + + return modulePath; + }); + + static final BundlerParamInfo MODULE = + new StandardBundlerParam<>( + Arguments.CLIOptions.MODULE.getId(), + String.class, + p -> null, + (s, p) -> { + return String.valueOf(s); + }); + + @SuppressWarnings("unchecked") + static final BundlerParamInfo> ADD_MODULES = + new StandardBundlerParam<>( + Arguments.CLIOptions.ADD_MODULES.getId(), + (Class>) (Object) Set.class, + p -> new LinkedHashSet(), + (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(","))) + ); + + @SuppressWarnings("unchecked") + static final BundlerParamInfo> LIMIT_MODULES = + new StandardBundlerParam<>( + "limit-modules", + (Class>) (Object) Set.class, + p -> new LinkedHashSet(), + (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(","))) + ); + + static boolean isRuntimeInstaller(Map params) { + if (params.containsKey(MODULE.getID()) || + params.containsKey(MAIN_JAR.getID()) || + params.containsKey(PREDEFINED_APP_IMAGE.getID())) { + return false; // we are building or are given an application + } + // runtime installer requires --runtime-image, if this is false + // here then we should have thrown error validating args. + return params.containsKey(PREDEFINED_RUNTIME_IMAGE.getID()); + } + + static File getPredefinedAppImage(Map params) { + File applicationImage = null; + if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) { + applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params); + if (!applicationImage.exists()) { + throw new RuntimeException( + MessageFormat.format(I18N.getString( + "message.app-image-dir-does-not-exist"), + PREDEFINED_APP_IMAGE.getID(), + applicationImage.toString())); + } + } + return applicationImage; + } + + static void copyPredefinedRuntimeImage( + Map params, + AbstractAppImageBuilder appBuilder) + throws IOException , ConfigException { + File topImage = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params); + if (!topImage.exists()) { + throw new ConfigException( + MessageFormat.format(I18N.getString( + "message.runtime-image-dir-does-not-exist"), + PREDEFINED_RUNTIME_IMAGE.getID(), + topImage.toString()), + MessageFormat.format(I18N.getString( + "message.runtime-image-dir-does-not-exist.advice"), + PREDEFINED_RUNTIME_IMAGE.getID())); + } + File image = appBuilder.getRuntimeImageDir(topImage); + // copy whole runtime, need to skip jmods and src.zip + final List excludes = Arrays.asList("jmods", "src.zip"); + IOUtils.copyRecursive(image.toPath(), appBuilder.getRuntimeRoot(), excludes); + + // if module-path given - copy modules to appDir/mods + List modulePath = + StandardBundlerParam.MODULE_PATH.fetchFrom(params); + List defaultModulePath = getDefaultModulePath(); + Path dest = appBuilder.getAppModsDir(); + + if (dest != null) { + for (Path mp : modulePath) { + if (!defaultModulePath.contains(mp)) { + Files.createDirectories(dest); + IOUtils.copyRecursive(mp, dest); + } + } + } + + appBuilder.prepareApplicationFiles(params); + } + + static void extractMainClassInfoFromAppResources( + Map params) { + boolean hasMainClass = params.containsKey(MAIN_CLASS.getID()); + boolean hasMainJar = params.containsKey(MAIN_JAR.getID()); + boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID()); + boolean hasModule = params.containsKey(MODULE.getID()); + + if (hasMainClass && hasMainJar && hasMainJarClassPath || hasModule || + isRuntimeInstaller(params)) { + return; + } + + // it's a pair. + // The [0] is the srcdir [1] is the file relative to sourcedir + List filesToCheck = new ArrayList<>(); + + if (hasMainJar) { + RelativeFileSet rfs = MAIN_JAR.fetchFrom(params); + for (String s : rfs.getIncludedFiles()) { + filesToCheck.add( + new String[] {rfs.getBaseDirectory().toString(), s}); + } + } else if (hasMainJarClassPath) { + for (String s : CLASSPATH.fetchFrom(params).split("\\s+")) { + if (APP_RESOURCES.fetchFrom(params) != null) { + filesToCheck.add( + new String[] {APP_RESOURCES.fetchFrom(params) + .getBaseDirectory().toString(), s}); + } + } + } else { + List rfsl = APP_RESOURCES_LIST.fetchFrom(params); + if (rfsl == null || rfsl.isEmpty()) { + return; + } + for (RelativeFileSet rfs : rfsl) { + if (rfs == null) continue; + + for (String s : rfs.getIncludedFiles()) { + filesToCheck.add( + new String[]{rfs.getBaseDirectory().toString(), s}); + } + } + } + + // presume the set iterates in-order + for (String[] fnames : filesToCheck) { + try { + // only sniff jars + if (!fnames[1].toLowerCase().endsWith(".jar")) continue; + + File file = new File(fnames[0], fnames[1]); + // that actually exist + if (!file.exists()) continue; + + try (JarFile jf = new JarFile(file)) { + Manifest m = jf.getManifest(); + Attributes attrs = (m != null) ? + m.getMainAttributes() : null; + + if (attrs != null) { + if (!hasMainJar) { + if (fnames[0] == null) { + fnames[0] = file.getParentFile().toString(); + } + params.put(MAIN_JAR.getID(), new RelativeFileSet( + new File(fnames[0]), + new LinkedHashSet<>(Collections + .singletonList(file)))); + } + if (!hasMainJarClassPath) { + String cp = + attrs.getValue(Attributes.Name.CLASS_PATH); + params.put(CLASSPATH.getID(), + cp == null ? "" : cp); + } + break; + } + } + } catch (IOException ignore) { + ignore.printStackTrace(); + } + } + } + + static void validateMainClassInfoFromAppResources( + Map params) throws ConfigException { + boolean hasMainClass = params.containsKey(MAIN_CLASS.getID()); + boolean hasMainJar = params.containsKey(MAIN_JAR.getID()); + boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID()); + boolean hasModule = params.containsKey(MODULE.getID()); + boolean hasAppImage = params.containsKey(PREDEFINED_APP_IMAGE.getID()); + + if (hasMainClass && hasMainJar && hasMainJarClassPath || + hasAppImage || isRuntimeInstaller(params)) { + return; + } + if (hasModule) { + if (JLinkBundlerHelper.getMainClassFromModule(params) == null) { + throw new ConfigException( + I18N.getString("ERR_NoMainClass"), null); + } + } else { + extractMainClassInfoFromAppResources(params); + + if (!params.containsKey(MAIN_CLASS.getID())) { + if (hasMainJar) { + throw new ConfigException( + MessageFormat.format(I18N.getString( + "error.no-main-class-with-main-jar"), + MAIN_JAR.fetchFrom(params)), + MessageFormat.format(I18N.getString( + "error.no-main-class-with-main-jar.advice"), + MAIN_JAR.fetchFrom(params))); + } else { + throw new ConfigException( + I18N.getString("error.no-main-class"), + I18N.getString("error.no-main-class.advice")); + } + } + } + } + + private static List + createAppResourcesListFromString(String s, + Map objectObjectMap) { + List result = new ArrayList<>(); + for (String path : s.split("[:;]")) { + File f = new File(path); + if (f.getName().equals("*") || path.endsWith("/") || + path.endsWith("\\")) { + if (f.getName().equals("*")) { + f = f.getParentFile(); + } + Set theFiles = new HashSet<>(); + try { + try (Stream stream = Files.walk(f.toPath())) { + stream.filter(Files::isRegularFile) + .forEach(p -> theFiles.add(p.toFile())); + } + } catch (IOException e) { + e.printStackTrace(); + } + result.add(new RelativeFileSet(f, theFiles)); + } else { + result.add(new RelativeFileSet(f.getParentFile(), + Collections.singleton(f))); + } + } + return result; + } + + private static RelativeFileSet getMainJar( + String mainJarValue, Map params) { + for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(params)) { + File appResourcesRoot = rfs.getBaseDirectory(); + File mainJarFile = new File(appResourcesRoot, mainJarValue); + + if (mainJarFile.exists()) { + return new RelativeFileSet(appResourcesRoot, + new LinkedHashSet<>(Collections.singletonList( + mainJarFile))); + } + mainJarFile = new File(mainJarValue); + if (mainJarFile.exists()) { + // absolute path for main-jar may fail is not legal + // below contains explicit error message. + } else { + List modulePath = MODULE_PATH.fetchFrom(params); + modulePath.removeAll(getDefaultModulePath()); + if (!modulePath.isEmpty()) { + Path modularJarPath = JLinkBundlerHelper.findPathOfModule( + modulePath, mainJarValue); + if (modularJarPath != null && + Files.exists(modularJarPath)) { + return new RelativeFileSet(appResourcesRoot, + new LinkedHashSet<>(Collections.singletonList( + modularJarPath.toFile()))); + } + } + } + } + + throw new IllegalArgumentException( + new ConfigException(MessageFormat.format(I18N.getString( + "error.main-jar-does-not-exist"), + mainJarValue), I18N.getString( + "error.main-jar-does-not-exist.advice"))); + } + + static List getDefaultModulePath() { + List result = new ArrayList(); + Path jdkModulePath = Paths.get( + System.getProperty("java.home"), "jmods").toAbsolutePath(); + + if (jdkModulePath != null && Files.exists(jdkModulePath)) { + result.add(jdkModulePath); + } + else { + // On a developer build the JDK Home isn't where we expect it + // relative to the jmods directory. Do some extra + // processing to find it. + Map env = System.getenv(); + + if (env.containsKey("JDK_HOME")) { + jdkModulePath = Paths.get(env.get("JDK_HOME"), + ".." + File.separator + "images" + + File.separator + "jmods").toAbsolutePath(); + + if (jdkModulePath != null && Files.exists(jdkModulePath)) { + result.add(jdkModulePath); + } + } + } + + return result; + } + + static String getDefaultAppVersion(Map params) { + String appVersion = DEFAULT_VERSION; + + ModuleDescriptor descriptor = JLinkBundlerHelper.getMainModuleDescription(params); + if (descriptor != null) { + Optional oversion = descriptor.version(); + if (oversion.isPresent()) { + Log.verbose(MessageFormat.format(I18N.getString( + "message.module-version"), + oversion.get().toString(), + JLinkBundlerHelper.getMainModule(params))); + appVersion = oversion.get().toString(); + } + } + + return appVersion; + } +} --- /dev/null 2019-11-18 20:59:22.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/ToolValidator.java 2019-11-18 20:59:19.190447700 -0500 @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.IOException; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Stream; + + +public final class ToolValidator { + + ToolValidator(String tool) { + this(Path.of(tool)); + } + + ToolValidator(Path toolPath) { + this.toolPath = toolPath; + args = new ArrayList<>(); + + if (Platform.getPlatform() == Platform.LINUX) { + setCommandLine("--version"); + } + + setToolNotFoundErrorHandler(null); + setToolOldVersionErrorHandler(null); + } + + ToolValidator setCommandLine(String... args) { + this.args = List.of(args); + return this; + } + + ToolValidator setMinimalVersion(Comparable v) { + minimalVersion = v; + return this; + } + + ToolValidator setVersionParser(Function, String> v) { + versionParser = v; + return this; + } + + ToolValidator setToolNotFoundErrorHandler( + BiFunction v) { + toolNotFoundErrorHandler = v; + return this; + } + + ToolValidator setToolOldVersionErrorHandler(BiFunction v) { + toolOldVersionErrorHandler = v; + return this; + } + + ConfigException validate() { + List cmdline = new ArrayList<>(); + cmdline.add(toolPath.toString()); + cmdline.addAll(args); + + String name = toolPath.getFileName().toString(); + try { + ProcessBuilder pb = new ProcessBuilder(cmdline); + AtomicBoolean canUseTool = new AtomicBoolean(); + if (minimalVersion == null) { + // No version check. + canUseTool.setPlain(true); + } + + String[] version = new String[1]; + Executor.of(pb).setOutputConsumer(lines -> { + if (versionParser != null && minimalVersion != null) { + version[0] = versionParser.apply(lines); + if (minimalVersion.compareTo(version[0]) < 0) { + canUseTool.setPlain(true); + } + } + }).execute(); + + if (!canUseTool.getPlain()) { + if (toolOldVersionErrorHandler != null) { + return toolOldVersionErrorHandler.apply(name, version[0]); + } + return new ConfigException(MessageFormat.format(I18N.getString( + "error.tool-old-version"), name, minimalVersion), + MessageFormat.format(I18N.getString( + "error.tool-old-version.advice"), name, + minimalVersion)); + } + } catch (IOException e) { + if (toolNotFoundErrorHandler != null) { + return toolNotFoundErrorHandler.apply(name, e); + } + return new ConfigException(MessageFormat.format(I18N.getString( + "error.tool-not-found"), name, e.getMessage()), + MessageFormat.format(I18N.getString( + "error.tool-not-found.advice"), name), e); + } + + // All good. Tool can be used. + return null; + } + + private final Path toolPath; + private List args; + private Comparable minimalVersion; + private Function, String> versionParser; + private BiFunction toolNotFoundErrorHandler; + private BiFunction toolOldVersionErrorHandler; +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ValidOptions.java 2019-11-18 20:59:33.871305300 -0500 +++ /dev/null 2019-11-18 20:59:35.000000000 -0500 @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import jdk.jpackage.internal.Arguments.CLIOptions; - -/** - * ValidOptions - * - * Two basic methods for validating command line options. - * - * initArgs() - * Computes the Map of valid options for each mode on this Platform. - * - * checkIfSupported(CLIOptions mode, CLIOptions arg) - * Determine if the given arg is valid in the given mode. - * - * checkIfOtherSupported(CLIOptions mode, CLIOptions arg) - * Determine if the given arg is valid in the a different mode. - */ -class ValidOptions { - - enum USE { - ALL, // valid in all cases - LAUNCHER, // valid when creating a launcher - INSTALL // valid when creating an installer - } - - private static final HashMap options = new HashMap<>(); - - - // initializing list of mandatory arguments - static { - options.put(CLIOptions.CREATE_APP_IMAGE.getId(), USE.ALL); - options.put(CLIOptions.CREATE_INSTALLER.getId(), USE.ALL); - options.put(CLIOptions.NAME.getId(), USE.ALL); - options.put(CLIOptions.VERSION.getId(), USE.ALL); - options.put(CLIOptions.OUTPUT.getId(), USE.ALL); - options.put(CLIOptions.TEMP_ROOT.getId(), USE.ALL); - options.put(CLIOptions.VERBOSE.getId(), USE.ALL); - options.put(CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(), USE.ALL); - options.put(CLIOptions.RESOURCE_DIR.getId(), USE.ALL); - options.put(CLIOptions.IDENTIFIER.getId(), USE.ALL); - options.put(CLIOptions.DESCRIPTION.getId(), USE.ALL); - options.put(CLIOptions.VENDOR.getId(), USE.ALL); - options.put(CLIOptions.COPYRIGHT.getId(), USE.ALL); - - options.put(CLIOptions.INPUT.getId(), USE.LAUNCHER); - options.put(CLIOptions.MODULE.getId(), USE.LAUNCHER); - options.put(CLIOptions.MODULE_PATH.getId(), USE.LAUNCHER); - options.put(CLIOptions.ADD_MODULES.getId(), USE.LAUNCHER); - options.put(CLIOptions.MAIN_JAR.getId(), USE.LAUNCHER); - options.put(CLIOptions.APPCLASS.getId(), USE.LAUNCHER); - options.put(CLIOptions.ICON.getId(), USE.LAUNCHER); - options.put(CLIOptions.ARGUMENTS.getId(), USE.LAUNCHER); - options.put(CLIOptions.JAVA_OPTIONS.getId(), USE.LAUNCHER); - options.put(CLIOptions.ADD_LAUNCHER.getId(), USE.LAUNCHER); - - options.put(CLIOptions.INSTALLER_TYPE.getId(), USE.INSTALL); - options.put(CLIOptions.LICENSE_FILE.getId(), USE.INSTALL); - options.put(CLIOptions.INSTALL_DIR.getId(), USE.INSTALL); - options.put(CLIOptions.PREDEFINED_APP_IMAGE.getId(), USE.INSTALL); - - options.put(CLIOptions.FILE_ASSOCIATIONS.getId(), - (Platform.getPlatform() == Platform.MAC) ? USE.ALL : USE.INSTALL); - - if (Platform.getPlatform() == Platform.WINDOWS) { - options.put(CLIOptions.WIN_CONSOLE_HINT.getId(), USE.LAUNCHER); - - options.put(CLIOptions.WIN_MENU_HINT.getId(), USE.INSTALL); - options.put(CLIOptions.WIN_MENU_GROUP.getId(), USE.INSTALL); - options.put(CLIOptions.WIN_SHORTCUT_HINT.getId(), USE.INSTALL); - options.put(CLIOptions.WIN_DIR_CHOOSER.getId(), USE.INSTALL); - options.put(CLIOptions.WIN_REGISTRY_NAME.getId(), USE.INSTALL); - options.put(CLIOptions.WIN_UPGRADE_UUID.getId(), USE.INSTALL); - options.put(CLIOptions.WIN_PER_USER_INSTALLATION.getId(), - USE.INSTALL); - } - - if (Platform.getPlatform() == Platform.MAC) { - options.put(CLIOptions.MAC_SIGN.getId(), USE.ALL); - options.put(CLIOptions.MAC_BUNDLE_NAME.getId(), USE.ALL); - options.put(CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), USE.ALL); - options.put(CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(), - USE.ALL); - options.put(CLIOptions.MAC_SIGNING_KEY_NAME.getId(), USE.ALL); - options.put(CLIOptions.MAC_SIGNING_KEYCHAIN.getId(), USE.ALL); - options.put(CLIOptions.MAC_APP_STORE_CATEGORY.getId(), USE.ALL); - options.put(CLIOptions.MAC_APP_STORE_ENTITLEMENTS.getId(), - USE.ALL); - } - - if (Platform.getPlatform() == Platform.LINUX) { - options.put(CLIOptions.LINUX_BUNDLE_NAME.getId(), USE.INSTALL); - options.put(CLIOptions.LINUX_DEB_MAINTAINER.getId(), USE.INSTALL); - options.put(CLIOptions.LINUX_RPM_LICENSE_TYPE.getId(), USE.INSTALL); - options.put(CLIOptions.LINUX_PACKAGE_DEPENDENCIES.getId(), - USE.INSTALL); - options.put(CLIOptions.LINUX_MENU_GROUP.getId(), USE.INSTALL); - } - } - - static boolean checkIfSupported(CLIOptions arg) { - return options.containsKey(arg.getId()); - } - - static boolean checkIfImageSupported(CLIOptions arg) { - USE use = options.get(arg.getId()); - return USE.ALL == use || USE.LAUNCHER == use; - } - - static boolean checkIfInstallerSupported(CLIOptions arg) { - USE use = options.get(arg.getId()); - return USE.ALL == use || USE.INSTALL == use; - } -} --- /dev/null 2019-11-18 20:59:35.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/ValidOptions.java 2019-11-18 20:59:30.339028500 -0500 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import jdk.incubator.jpackage.internal.Arguments.CLIOptions; + +/** + * ValidOptions + * + * Two basic methods for validating command line options. + * + * initArgs() + * Computes the Map of valid options for each mode on this Platform. + * + * checkIfSupported(CLIOptions arg) + * Determine if the given arg is valid on this platform. + * + * checkIfImageSupported(CLIOptions arg) + * Determine if the given arg is valid for creating app image. + * + * checkIfInstallerSupported(CLIOptions arg) + * Determine if the given arg is valid for creating installer. + * + */ +class ValidOptions { + + enum USE { + ALL, // valid in all cases + LAUNCHER, // valid when creating a launcher + INSTALL // valid when creating an installer + } + + private static final HashMap options = new HashMap<>(); + + + // initializing list of mandatory arguments + static { + options.put(CLIOptions.NAME.getId(), USE.ALL); + options.put(CLIOptions.VERSION.getId(), USE.ALL); + options.put(CLIOptions.OUTPUT.getId(), USE.ALL); + options.put(CLIOptions.TEMP_ROOT.getId(), USE.ALL); + options.put(CLIOptions.VERBOSE.getId(), USE.ALL); + options.put(CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(), USE.ALL); + options.put(CLIOptions.RESOURCE_DIR.getId(), USE.ALL); + options.put(CLIOptions.DESCRIPTION.getId(), USE.ALL); + options.put(CLIOptions.VENDOR.getId(), USE.ALL); + options.put(CLIOptions.COPYRIGHT.getId(), USE.ALL); + options.put(CLIOptions.PACKAGE_TYPE.getId(), USE.ALL); + + options.put(CLIOptions.INPUT.getId(), USE.LAUNCHER); + options.put(CLIOptions.MODULE.getId(), USE.LAUNCHER); + options.put(CLIOptions.MODULE_PATH.getId(), USE.LAUNCHER); + options.put(CLIOptions.ADD_MODULES.getId(), USE.LAUNCHER); + options.put(CLIOptions.MAIN_JAR.getId(), USE.LAUNCHER); + options.put(CLIOptions.APPCLASS.getId(), USE.LAUNCHER); + options.put(CLIOptions.ICON.getId(), USE.LAUNCHER); + options.put(CLIOptions.ARGUMENTS.getId(), USE.LAUNCHER); + options.put(CLIOptions.JAVA_OPTIONS.getId(), USE.LAUNCHER); + options.put(CLIOptions.ADD_LAUNCHER.getId(), USE.LAUNCHER); + options.put(CLIOptions.BIND_SERVICES.getId(), USE.LAUNCHER); + + options.put(CLIOptions.LICENSE_FILE.getId(), USE.INSTALL); + options.put(CLIOptions.INSTALL_DIR.getId(), USE.INSTALL); + options.put(CLIOptions.PREDEFINED_APP_IMAGE.getId(), USE.INSTALL); + + options.put(CLIOptions.FILE_ASSOCIATIONS.getId(), + (Platform.getPlatform() == Platform.MAC) ? USE.ALL : USE.INSTALL); + + if (Platform.getPlatform() == Platform.WINDOWS) { + options.put(CLIOptions.WIN_CONSOLE_HINT.getId(), USE.LAUNCHER); + + options.put(CLIOptions.WIN_MENU_HINT.getId(), USE.INSTALL); + options.put(CLIOptions.WIN_MENU_GROUP.getId(), USE.INSTALL); + options.put(CLIOptions.WIN_SHORTCUT_HINT.getId(), USE.INSTALL); + options.put(CLIOptions.WIN_DIR_CHOOSER.getId(), USE.INSTALL); + options.put(CLIOptions.WIN_UPGRADE_UUID.getId(), USE.INSTALL); + options.put(CLIOptions.WIN_PER_USER_INSTALLATION.getId(), + USE.INSTALL); + } + + if (Platform.getPlatform() == Platform.MAC) { + options.put(CLIOptions.MAC_SIGN.getId(), USE.ALL); + options.put(CLIOptions.MAC_BUNDLE_NAME.getId(), USE.ALL); + options.put(CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), USE.ALL); + options.put(CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(), + USE.ALL); + options.put(CLIOptions.MAC_SIGNING_KEY_NAME.getId(), USE.ALL); + options.put(CLIOptions.MAC_SIGNING_KEYCHAIN.getId(), USE.ALL); + options.put(CLIOptions.MAC_APP_STORE_ENTITLEMENTS.getId(), + USE.ALL); + } + + if (Platform.getPlatform() == Platform.LINUX) { + options.put(CLIOptions.LINUX_BUNDLE_NAME.getId(), USE.INSTALL); + options.put(CLIOptions.LINUX_DEB_MAINTAINER.getId(), USE.INSTALL); + options.put(CLIOptions.LINUX_CATEGORY.getId(), USE.INSTALL); + options.put(CLIOptions.LINUX_RPM_LICENSE_TYPE.getId(), USE.INSTALL); + options.put(CLIOptions.LINUX_PACKAGE_DEPENDENCIES.getId(), + USE.INSTALL); + options.put(CLIOptions.LINUX_MENU_GROUP.getId(), USE.INSTALL); + options.put(CLIOptions.RELEASE.getId(), USE.INSTALL); + options.put(CLIOptions.LINUX_SHORTCUT_HINT.getId(), USE.INSTALL); + } + } + + static boolean checkIfSupported(CLIOptions arg) { + return options.containsKey(arg.getId()); + } + + static boolean checkIfImageSupported(CLIOptions arg) { + USE use = options.get(arg.getId()); + return USE.ALL == use || USE.LAUNCHER == use; + } + + static boolean checkIfInstallerSupported(CLIOptions arg) { + USE use = options.get(arg.getId()); + return USE.ALL == use || USE.INSTALL == use; + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties 2019-11-18 20:59:55.344020100 -0500 +++ /dev/null 2019-11-18 20:59:56.000000000 -0500 @@ -1,279 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -MSG_Help=Usage: jpackage \n\ -\n\ -where mode is one of: \n\ -\ create-app-image\n\ -\ Generates a platform-specific application image.\n\ -\ create-installer\n\ -\ Generates a platform-specific installer for the application.\n\ -\ \n\ -Sample usages:\n\ ---------------\n\ -\ Generate a non-modular application image:\n\ -\ jpackage create-app-image -o outputdir -i inputdir -n name \\\n\ -\ --main-class className --main-jar MyJar.jar\n\ -\ Generate a modular application image:\n\ -\ jpackage create-app-image -o outputdir -n name \\\n\ -\ -p modulePath -m moduleName/className\n\ -\ To provide your own options to jlink, run jlink separately:\n\ -\ jlink --output appRuntimeImage -p ModulePath -m moduleName\\\n\ -\ --no-header-files [...]\n\ -\ jpackage create-app-image -o outputdir -n name\\\n\ -\ -m moduleName/className --runtime-image appRuntimeIMage\n\ -\ Generate an application installer:\n\ -\ jpackage create-installer -o outputdir -n name \\\n\ -\ -p modulePath -m moduleName/className\n\ -\ jpackage create-installer -i inputdir -o outputdir -n name \\\n\ -\ --main-class package.ClassName --main-jar MyJar.jar\n\ -\ jpackage create-installer -o outputdir -n \\\n\ -\ --app-image [--installer-type ]\n\ -\ Generate a Java runtime installer:\n\ -\ jpackage create-installer -o outputdir -n name \\\n\ -\ --runtime-image \n\ -\n\ -Generic Options:\n\ -\ @ \n\ -\ Read options and/or mode from a file \n\ -\ This option can be used multiple times.\n\ -\ --app-version \n\ -\ Version of the application and/or installer\n\ -\ --copyright \n\ -\ Copyright for the application\n\ -\ --description \n\ -\ Description of the application\n\ -\ --help -h \n\ -\ Print the usage text with a list and description of each valid\n\ -\ option for the current platform to the output stream, and exit\n\ -\ --name -n \n\ -\ Name of the application and/or installer\n\ -\ --output -o \n\ -\ Path where generated output file is placed\n\ -\ (absolute path or relative to the current directory)\n\ -\ --temp-root \n\ -\ Path of a new or empty directory used to create temporary files\n\ -\ (absolute path or relative to the current directory)\n\ -\ If specified, the temp-root will not be removed upon the task\n\ -\ completion and must be removed manually\n\ -\ If not specified, a temporary directory will be created and\n\ -\ removed upon the task completion.\n\ -\ --vendor \n\ -\ Vendor of the application\n\ -\ --verbose\n\ -\ Enables verbose output\n\ -\ --version\n\ -\ Print the product version to the output stream and exit\n\ -\n\ -\Options for creating the runtime image:\n\ -\ --add-modules [,...]\n\ -\ A comma (",") separated list of modules to add.\n\ -\ This module list, along with the main module (if specified)\n\ -\ will be passed to jlink as the --add-module argument.\n\ -\ if not specified, either just the main module (if --module is\n\ -\ specified), or the default set of modules (if --main-jar is \n\ -\ specified) are used.\n\ -\ This option can be used multiple times.\n\ -\ --module-path -p ...\n\ -\ A {0} separated list of paths\n\ -\ Each path is either a directory of modules or the path to a\n\ -\ modular jar.\n\ -\ (each path is absolute or relative to the current directory)\n\ -\ This option can be used multiple times.\n\ -\ --runtime-image \n\ -\ Path of the predefined runtime image that will be copied into\n\ -\ the application image\n\ -\ (absolute path or relative to the current directory)\n\ -\ If --runtime-image is not specified, jpackage will run jlink to\n\ -\ create the runtime image using options:\n\ -\ --strip-debug, --no-header-files, --no-man-pages, and\n\ -\ --strip-native-commands. --bind-services will also be added if\n\ -\ --add-modules is not specified.\n\ -\n\ -\Options for creating the application image:\n\ -\ --icon \n\ -\ Path of the icon of the application bundle\n\ -\ (absolute path or relative to the current directory)\n\ -\ --input -i \n\ -\ Path of the input directory that contains the files to be packaged\n\ -\ (absolute path or relative to the current directory)\n\ -\ All files in the input directory will be packaged into the\n\ -\ application image.\n\ -\n\ -\Options for creating the application launcher(s):\n\ -\ --add-launcher =\n\ -\ Name of launcher, and a path to a Properties file that contains\n\ -\ a list of key, value pairs\n\ -\ (absolute path or relative to the current directory)\n\ -\ The keys "module", "add-modules", "main-jar", "main-class",\n\ -\ "arguments", "java-options", "app-version", "icon", and\n\ -\ "win-console" can be used.\n\ -\ These options are added to, or used to overwrite, the original\n\ -\ command line options to build an additional alternative launcher.\n\ -\ The main application launcher will be built from the command line\n\ -\ options. Additional alternative launchers can be built using\n\ -\ this option, and this option can be used multiple times to\n\ -\ build multiple additional launchers. \n\ -\ --arguments
\n\ -\ Command line arguments to pass to the main class if no command\n\ -\ line arguments are given to the launcher\n\ -\ This option can be used multiple times.\n\ -\ --java-options \n\ -\ Options to pass to the Java runtime\n\ -\ This option can be used multiple times.\n\ -\ --main-class \n\ -\ Qualified name of the application main class to execute\n\ -\ This option can only be used if --main-jar is specified.\n\ -\ --main-jar
\n\ -\ The main JAR of the application; containing the main class\n\ -\ (specified as a path relative to the input path)\n\ -\ Either --module or --main-jar option can be specified but not\n\ -\ both.\n\ -\ --module -m [/
]\n\ -\ The main module (and optionally main class) of the application\n\ -\ This module must be located on the module path.\n\ -\ When this option is specified, the main module will be linked\n\ -\ in the Java runtime image. Either --module or --main-jar\n\ -\ option can be specified but not both.\n\ -{2}\n\ -\Options for creating the application installer(s):\n\ -\ --app-image \n\ -\ Location of the predefined application image that is used\n\ -\ to build an installable package\n\ -\ (absolute path or relative to the current directory)\n\ -\ See create-app-image mode options to create the application image.\n\ -\ --file-associations \n\ -\ Path to a Properties file that contains list of key, value pairs\n\ -\ (absolute path or relative to the current directory)\n\ -\ The keys "extension", "mime-type", "icon", and "description"\n\ -\ can be used to describe the association.\n\ -\ This option can be used multiple times.\n\ -\ --identifier \n\ -\ An identifier that uniquely identifies the application\n\ -\ Defaults to the main class name.\n\ -\ The value should be a valid DNS name.\n\ -\ --install-dir \n\ -\ {4}\ -\ --installer-type \n\ -\ The type of the installer to create\n\ -\ Valid values are: {1} \n\ -\ If this option is not specified (in create-installer mode) all\n\ -\ supported types of installable packages for the current\n\ -\ platform will be created.\n\ -\ --license-file \n\ -\ Path to the license file\n\ -\ (absolute path or relative to the current directory)\n\ -\ --resource-dir \n\ -\ Path to override jpackage resources\n\ -\ Icons, template files, and other resources of jpackage can be\n\ -\ over-ridden by adding replacement resources to this directory.\n\ -\ (absolute path or relative to the current directory)\n\ -\ --runtime-image \n\ -\ Path of the predefined runtime image to install\n\ -\ (absolute path or relative to the current directory)\n\ -\ Option is required when creating a runtime installer.\n\ -\n\ -\Platform dependent options for creating the application installer(s):\n\ -{3} - -MSG_Help_win_launcher=\ -\n\ -\Platform dependent option for creating the application launcher:\n\ -\ --win-console\n\ -\ Creates a console launcher for the application, should be\n\ -\ specified for application which requires console interactions\n\ - -MSG_Help_win_install=\ -\ --win-dir-chooser\n\ -\ Adds a dialog to enable the user to choose a directory in which\n\ -\ the product is installed\n\ -\ --win-menu\n\ -\ Adds the application to the system menu\n\ -\ --win-menu-group \n\ -\ Start Menu group this application is placed in\n\ -\ --win-per-user-install\n\ -\ Request to perform an install on a per-user basis\n\ -\ --win-registry-name \n\ -\ Name of the application for registry references.\n\ -\ The default is the Application Name with only\n\ -\ alphanumerics, dots, and dashes (no whitespace)\n\ -\ --win-shortcut\n\ -\ Creates a desktop shortcut for the application\n\ -\ --win-upgrade-uuid \n\ -\ UUID associated with upgrades for this package\n\ - -MSG_Help_win_install_dir=\ -\Relative sub-path under the default installation location\n\ - -MSG_Help_mac_launcher=\ -\ --mac-bundle-identifier \n\ -\ An identifier that uniquely identifies the application for MacOSX\n\ -\ Defaults to the value of --identifier option.\n\ -\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ -\ and period (.) characters.\n\ -\ --mac-bundle-name \n\ -\ Name of the application as it appears in the Menu Bar\n\ -\ This can be different from the application name.\n\ -\ This name must be less than 16 characters long and be suitable for\n\ -\ displaying in the menu bar and the application Info window.\n\ -\ Defaults to the application name.\n\ -\ --mac-bundle-signing-prefix \n\ -\ When signing the application bundle, this value is prefixed to all\n\ -\ components that need to be signed that don't have\n\ -\ an existing bundle identifier.\n\ -\ --mac-sign\n\ -\ Request that the bundle be signed\n\ -\ --mac-signing-keychain \n\ -\ Path of the keychain to use\n\ -\ (absolute path or relative to the current directory)\n\ -\ If not specified, the standard keychains are used.\n\ -\ --mac-signing-key-user-name \n\ -\ User name portion of the typical\n\ -\ "Mac Developer ID Application: " signing key\n\ - -MSG_Help_linux_install=\ -\ --linux-bundle-name \n\ -\ Name for Linux bundle, defaults to the application name\n\ -\ --linux-deb-maintainer \n\ -\ Maintainer for .deb bundle\n\ -\ --linux-menu-group \n\ -\ Menu group this application is placed in\n\ -\ --linux-package-deps\n\ -\ Required packages or capabilities for the application\n\ -\ --linux-rpm-license-type \n\ -\ Type of the license ("License: " of the RPM .spec)\n\ - -MSG_Help_mac_linux_install_dir=\ -\Absolute path of the installation directory of the application\n\ - -MSG_Help_default_install_dir=\ -\Absolute path of the installation directory of the application on OS X\n\ -\ or Linux. Relative sub-path of the installation location of the application\n\ -\ such as "Program Files" or "AppData" on Windows.\n\ - -MSG_Help_no_args=Usage: jpackage \n\ -\Use jpackage --help (or -h) for a list of possible options\ - --- /dev/null 2019-11-18 20:59:57.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/resources/HelpResources.properties 2019-11-18 20:59:51.661376200 -0500 @@ -0,0 +1,275 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +MSG_Help=Usage: jpackage \n\ +\n\ +Sample usages:\n\ +--------------\n\ +\ Generate an application package suitable for the host system:\n\ +\ For a modular application:\n\ +\ jpackage -n name -p modulePath -m moduleName/className\n\ +\ For a non-modular application:\n\ +\ jpackage -i inputDir -n name \\\n\ +\ --main-class className --main-jar myJar.jar\n\ +\ From a pre-built application image:\n\ +\ jpackage -n name --app-image appImageDir\n\ +\ Generate an application image:\n\ +\ For a modular application:\n\ +\ jpackage --type app-image -n name -p modulePath \\\n\ +\ -m moduleName/className\n\ +\ For a non-modular application:\n\ +\ jpackage --type app-image -i inputDir -n name \\\n\ +\ --main-class className --main-jar myJar.jar\n\ +\ To provide your own options to jlink, run jlink separately:\n\ +\ jlink --output appRuntimeImage -p modulePath -m moduleName \\\n\ +\ --no-header-files [...]\n\ +\ jpackage --type app-image -n name \\\n\ +\ -m moduleName/className --runtime-image appRuntimeImage\n\ +\ Generate a Java runtime package:\n\ +\ jpackage -n name --runtime-image \n\ +\n\ +Generic Options:\n\ +\ @ \n\ +\ Read options and/or mode from a file \n\ +\ This option can be used multiple times.\n\ +\ --type -t \n\ +\ The type of package to create\n\ +\ Valid values are: {1} \n\ +\ If this option is not specified a platform dependent\n\ +\ default type will be created.\n\ +\ --app-version \n\ +\ Version of the application and/or package\n\ +\ --copyright \n\ +\ Copyright for the application\n\ +\ --description \n\ +\ Description of the application\n\ +\ --help -h \n\ +\ Print the usage text with a list and description of each valid\n\ +\ option for the current platform to the output stream, and exit\n\ +\ --name -n \n\ +\ Name of the application and/or package\n\ +\ --dest -d \n\ +\ Path where generated output file is placed\n\ +\ Defaults to the current working directory.\n\ +\ (absolute path or relative to the current directory)\n\ +\ --temp \n\ +\ Path of a new or empty directory used to create temporary files\n\ +\ (absolute path or relative to the current directory)\n\ +\ If specified, the temp dir will not be removed upon the task\n\ +\ completion and must be removed manually\n\ +\ If not specified, a temporary directory will be created and\n\ +\ removed upon the task completion.\n\ +\ --vendor \n\ +\ Vendor of the application\n\ +\ --verbose\n\ +\ Enables verbose output\n\ +\ --version\n\ +\ Print the product version to the output stream and exit\n\ +\n\ +\Options for creating the runtime image:\n\ +\ --add-modules [,...]\n\ +\ A comma (",") separated list of modules to add.\n\ +\ This module list, along with the main module (if specified)\n\ +\ will be passed to jlink as the --add-module argument.\n\ +\ if not specified, either just the main module (if --module is\n\ +\ specified), or the default set of modules (if --main-jar is \n\ +\ specified) are used.\n\ +\ This option can be used multiple times.\n\ +\ --module-path -p ...\n\ +\ A {0} separated list of paths\n\ +\ Each path is either a directory of modules or the path to a\n\ +\ modular jar.\n\ +\ (each path is absolute or relative to the current directory)\n\ +\ This option can be used multiple times.\n\ +\ --bind-services \n\ +\ Pass on --bind-services option to jlink (which will link in \n\ +\ service provider modules and their dependences) \n\ +\ --runtime-image \n\ +\ Path of the predefined runtime image that will be copied into\n\ +\ the application image\n\ +\ (absolute path or relative to the current directory)\n\ +\ If --runtime-image is not specified, jpackage will run jlink to\n\ +\ create the runtime image using options:\n\ +\ --strip-debug, --no-header-files, --no-man-pages, and\n\ +\ --strip-native-commands.\n\ +\n\ +\Options for creating the application image:\n\ +\ --icon \n\ +\ Path of the icon of the application package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --input -i \n\ +\ Path of the input directory that contains the files to be packaged\n\ +\ (absolute path or relative to the current directory)\n\ +\ All files in the input directory will be packaged into the\n\ +\ application image.\n\ +\n\ +\Options for creating the application launcher(s):\n\ +\ --add-launcher =\n\ +\ Name of launcher, and a path to a Properties file that contains\n\ +\ a list of key, value pairs\n\ +\ (absolute path or relative to the current directory)\n\ +\ The keys "module", "main-jar", "main-class",\n\ +\ "arguments", "java-options", "app-version", "icon", and\n\ +\ "win-console" can be used.\n\ +\ These options are added to, or used to overwrite, the original\n\ +\ command line options to build an additional alternative launcher.\n\ +\ The main application launcher will be built from the command line\n\ +\ options. Additional alternative launchers can be built using\n\ +\ this option, and this option can be used multiple times to\n\ +\ build multiple additional launchers. \n\ +\ --arguments
\n\ +\ Command line arguments to pass to the main class if no command\n\ +\ line arguments are given to the launcher\n\ +\ This option can be used multiple times.\n\ +\ --java-options \n\ +\ Options to pass to the Java runtime\n\ +\ This option can be used multiple times.\n\ +\ --main-class \n\ +\ Qualified name of the application main class to execute\n\ +\ This option can only be used if --main-jar is specified.\n\ +\ --main-jar
\n\ +\ The main JAR of the application; containing the main class\n\ +\ (specified as a path relative to the input path)\n\ +\ Either --module or --main-jar option can be specified but not\n\ +\ both.\n\ +\ --module -m [/
]\n\ +\ The main module (and optionally main class) of the application\n\ +\ This module must be located on the module path.\n\ +\ When this option is specified, the main module will be linked\n\ +\ in the Java runtime image. Either --module or --main-jar\n\ +\ option can be specified but not both.\n\ +{2}\n\ +\Options for creating the application package:\n\ +\ --app-image \n\ +\ Location of the predefined application image that is used\n\ +\ to build an installable package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --file-associations \n\ +\ Path to a Properties file that contains list of key, value pairs\n\ +\ (absolute path or relative to the current directory)\n\ +\ The keys "extension", "mime-type", "icon", and "description"\n\ +\ can be used to describe the association.\n\ +\ This option can be used multiple times.\n\ +\ --install-dir \n\ +\ {4}\ +\ --license-file \n\ +\ Path to the license file\n\ +\ (absolute path or relative to the current directory)\n\ +\ --resource-dir \n\ +\ Path to override jpackage resources\n\ +\ Icons, template files, and other resources of jpackage can be\n\ +\ over-ridden by adding replacement resources to this directory.\n\ +\ (absolute path or relative to the current directory)\n\ +\ --runtime-image \n\ +\ Path of the predefined runtime image to install\n\ +\ (absolute path or relative to the current directory)\n\ +\ Option is required when creating a runtime package.\n\ +\n\ +\Platform dependent options for creating the application package:\n\ +{3} + +MSG_Help_win_launcher=\ +\n\ +\Platform dependent option for creating the application launcher:\n\ +\ --win-console\n\ +\ Creates a console launcher for the application, should be\n\ +\ specified for application which requires console interactions\n\ + +MSG_Help_win_install=\ +\ --win-dir-chooser\n\ +\ Adds a dialog to enable the user to choose a directory in which\n\ +\ the product is installed\n\ +\ --win-menu\n\ +\ Adds the application to the system menu\n\ +\ --win-menu-group \n\ +\ Start Menu group this application is placed in\n\ +\ --win-per-user-install\n\ +\ Request to perform an install on a per-user basis\n\ +\ --win-shortcut\n\ +\ Creates a desktop shortcut for the application\n\ +\ --win-upgrade-uuid \n\ +\ UUID associated with upgrades for this package\n\ + +MSG_Help_win_install_dir=\ +\Relative sub-path under the default installation location\n\ + +MSG_Help_mac_launcher=\ +\ --mac-package-identifier \n\ +\ An identifier that uniquely identifies the application for macOS\n\ +\ Defaults to the main class name.\n\ +\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ +\ and period (.) characters.\n\ +\ --mac-package-name \n\ +\ Name of the application as it appears in the Menu Bar\n\ +\ This can be different from the application name.\n\ +\ This name must be less than 16 characters long and be suitable for\n\ +\ displaying in the menu bar and the application Info window.\n\ +\ Defaults to the application name.\n\ +\ --mac-package-signing-prefix \n\ +\ When signing the application package, this value is prefixed\n\ +\ to all components that need to be signed that don't have\n\ +\ an existing package identifier.\n\ +\ --mac-sign\n\ +\ Request that the package be signed\n\ +\ --mac-signing-keychain \n\ +\ Path of the keychain to search for the signing identity\n\ +\ (absolute path or relative to the current directory).\n\ +\ If not specified, the standard keychains are used.\n\ +\ --mac-signing-key-user-name \n\ +\ Team name portion in Apple signing identities' names.\n\ +\ For example "Developer ID Application: "\n\ + +MSG_Help_linux_install=\ +\ --linux-package-name \n\ +\ Name for Linux package, defaults to the application name\n\ +\ --linux-deb-maintainer \n\ +\ Maintainer for .deb package\n\ +\ --linux-menu-group \n\ +\ Menu group this application is placed in\n\ +\ --linux-package-deps\n\ +\ Required packages or capabilities for the application\n\ +\ --linux-rpm-license-type \n\ +\ Type of the license ("License: " of the RPM .spec)\n\ +\ --linux-app-release \n\ +\ Release value of the RPM .spec file or \n\ +\ Debian revision value of the DEB control file.\n\ +\ --linux-app-category \n\ +\ Group value of the RPM .spec file or \n\ +\ Section value of DEB control file.\n\ +\ --linux-shortcut\n\ +\ Creates a shortcut for the application\n\ + +MSG_Help_mac_linux_install_dir=\ +\Absolute path of the installation directory of the application\n\ + +MSG_Help_default_install_dir=\ +\Absolute path of the installation directory of the application on OS X\n\ +\ or Linux. Relative sub-path of the installation location of\n\ +\ the application such as "Program Files" or "AppData" on Windows.\n\ + +MSG_Help_no_args=Usage: jpackage \n\ +\Use jpackage --help (or -h) for a list of possible options\ + --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties 2019-11-18 21:00:16.545860000 -0500 +++ /dev/null 2019-11-18 21:00:17.000000000 -0500 @@ -1,279 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -MSG_Help=Usage: jpackage \n\ -\n\ -where mode is one of: \n\ -\ create-app-image\n\ -\ Generates a platform-specific application image.\n\ -\ create-installer\n\ -\ Generates a platform-specific installer for the application.\n\ -\ \n\ -Sample usages:\n\ ---------------\n\ -\ Generate a non-modular application image:\n\ -\ jpackage create-app-image -o outputdir -i inputdir -n name \\\n\ -\ --main-class className --main-jar MyJar.jar\n\ -\ Generate a modular application image:\n\ -\ jpackage create-app-image -o outputdir -n name \\\n\ -\ -p modulePath -m moduleName/className\n\ -\ To provide your own options to jlink, run jlink separately:\n\ -\ jlink --output appRuntimeImage -p ModulePath -m moduleName\\\n\ -\ --no-header-files [...]\n\ -\ jpackage create-app-image -o outputdir -n name\\\n\ -\ -m moduleName/className --runtime-image appRuntimeIMage\n\ -\ Generate an application installer:\n\ -\ jpackage create-installer -o outputdir -n name \\\n\ -\ -p modulePath -m moduleName/className\n\ -\ jpackage create-installer -i inputdir -o outputdir -n name \\\n\ -\ --main-class package.ClassName --main-jar MyJar.jar\n\ -\ jpackage create-installer -o outputdir -n \\\n\ -\ --app-image [--installer-type ]\n\ -\ Generate a Java runtime installer:\n\ -\ jpackage create-installer -o outputdir -n name \\\n\ -\ --runtime-image \n\ -\n\ -Generic Options:\n\ -\ @ \n\ -\ Read options and/or mode from a file \n\ -\ This option can be used multiple times.\n\ -\ --app-version \n\ -\ Version of the application and/or installer\n\ -\ --copyright \n\ -\ Copyright for the application\n\ -\ --description \n\ -\ Description of the application\n\ -\ --help -h \n\ -\ Print the usage text with a list and description of each valid\n\ -\ option for the current platform to the output stream, and exit\n\ -\ --name -n \n\ -\ Name of the application and/or installer\n\ -\ --output -o \n\ -\ Path where generated output file is placed\n\ -\ (absolute path or relative to the current directory)\n\ -\ --temp-root \n\ -\ Path of a new or empty directory used to create temporary files\n\ -\ (absolute path or relative to the current directory)\n\ -\ If specified, the temp-root will not be removed upon the task\n\ -\ completion and must be removed manually\n\ -\ If not specified, a temporary directory will be created and\n\ -\ removed upon the task completion.\n\ -\ --vendor \n\ -\ Vendor of the application\n\ -\ --verbose\n\ -\ Enables verbose output\n\ -\ --version\n\ -\ Print the product version to the output stream and exit\n\ -\n\ -\Options for creating the runtime image:\n\ -\ --add-modules [,...]\n\ -\ A comma (",") separated list of modules to add.\n\ -\ This module list, along with the main module (if specified)\n\ -\ will be passed to jlink as the --add-module argument.\n\ -\ if not specified, either just the main module (if --module is\n\ -\ specified), or the default set of modules (if --main-jar is \n\ -\ specified) are used.\n\ -\ This option can be used multiple times.\n\ -\ --module-path -p ...\n\ -\ A {0} separated list of paths\n\ -\ Each path is either a directory of modules or the path to a\n\ -\ modular jar.\n\ -\ (each path is absolute or relative to the current directory)\n\ -\ This option can be used multiple times.\n\ -\ --runtime-image \n\ -\ Path of the predefined runtime image that will be copied into\n\ -\ the application image\n\ -\ (absolute path or relative to the current directory)\n\ -\ If --runtime-image is not specified, jpackage will run jlink to\n\ -\ create the runtime image using options:\n\ -\ --strip-debug, --no-header-files, --no-man-pages, and\n\ -\ --strip-native-commands. --bind-services will also be added if\n\ -\ --add-modules is not specified.\n\ -\n\ -\Options for creating the application image:\n\ -\ --icon \n\ -\ Path of the icon of the application bundle\n\ -\ (absolute path or relative to the current directory)\n\ -\ --input -i \n\ -\ Path of the input directory that contains the files to be packaged\n\ -\ (absolute path or relative to the current directory)\n\ -\ All files in the input directory will be packaged into the\n\ -\ application image.\n\ -\n\ -\Options for creating the application launcher(s):\n\ -\ --add-launcher =\n\ -\ Name of launcher, and a path to a Properties file that contains\n\ -\ a list of key, value pairs\n\ -\ (absolute path or relative to the current directory)\n\ -\ The keys "module", "add-modules", "main-jar", "main-class",\n\ -\ "arguments", "java-options", "app-version", "icon", and\n\ -\ "win-console" can be used.\n\ -\ These options are added to, or used to overwrite, the original\n\ -\ command line options to build an additional alternative launcher.\n\ -\ The main application launcher will be built from the command line\n\ -\ options. Additional alternative launchers can be built using\n\ -\ this option, and this option can be used multiple times to\n\ -\ build multiple additional launchers. \n\ -\ --arguments
\n\ -\ Command line arguments to pass to the main class if no command\n\ -\ line arguments are given to the launcher\n\ -\ This option can be used multiple times.\n\ -\ --java-options \n\ -\ Options to pass to the Java runtime\n\ -\ This option can be used multiple times.\n\ -\ --main-class \n\ -\ Qualified name of the application main class to execute\n\ -\ This option can only be used if --main-jar is specified.\n\ -\ --main-jar
\n\ -\ The main JAR of the application; containing the main class\n\ -\ (specified as a path relative to the input path)\n\ -\ Either --module or --main-jar option can be specified but not\n\ -\ both.\n\ -\ --module -m [/
]\n\ -\ The main module (and optionally main class) of the application\n\ -\ This module must be located on the module path.\n\ -\ When this option is specified, the main module will be linked\n\ -\ in the Java runtime image. Either --module or --main-jar\n\ -\ option can be specified but not both.\n\ -{2}\n\ -\Options for creating the application installer(s):\n\ -\ --app-image \n\ -\ Location of the predefined application image that is used\n\ -\ to build an installable package\n\ -\ (absolute path or relative to the current directory)\n\ -\ See create-app-image mode options to create the application image.\n\ -\ --file-associations \n\ -\ Path to a Properties file that contains list of key, value pairs\n\ -\ (absolute path or relative to the current directory)\n\ -\ The keys "extension", "mime-type", "icon", and "description"\n\ -\ can be used to describe the association.\n\ -\ This option can be used multiple times.\n\ -\ --identifier \n\ -\ An identifier that uniquely identifies the application\n\ -\ Defaults to the main class name.\n\ -\ The value should be a valid DNS name.\n\ -\ --install-dir \n\ -\ {4}\ -\ --installer-type \n\ -\ The type of the installer to create\n\ -\ Valid values are: {1} \n\ -\ If this option is not specified (in create-installer mode) all\n\ -\ supported types of installable packages for the current\n\ -\ platform will be created.\n\ -\ --license-file \n\ -\ Path to the license file\n\ -\ (absolute path or relative to the current directory)\n\ -\ --resource-dir \n\ -\ Path to override jpackage resources\n\ -\ Icons, template files, and other resources of jpackage can be\n\ -\ over-ridden by adding replacement resources to this directory.\n\ -\ (absolute path or relative to the current directory)\n\ -\ --runtime-image \n\ -\ Path of the predefined runtime image to install\n\ -\ (absolute path or relative to the current directory)\n\ -\ Option is required when creating a runtime installer.\n\ -\n\ -\Platform dependent options for creating the application installer(s):\n\ -{3} - -MSG_Help_win_launcher=\ -\n\ -\Platform dependent option for creating the application launcher:\n\ -\ --win-console\n\ -\ Creates a console launcher for the application, should be\n\ -\ specified for application which requires console interactions\n\ - -MSG_Help_win_install=\ -\ --win-dir-chooser\n\ -\ Adds a dialog to enable the user to choose a directory in which\n\ -\ the product is installed\n\ -\ --win-menu\n\ -\ Adds the application to the system menu\n\ -\ --win-menu-group \n\ -\ Start Menu group this application is placed in\n\ -\ --win-per-user-install\n\ -\ Request to perform an install on a per-user basis\n\ -\ --win-registry-name \n\ -\ Name of the application for registry references.\n\ -\ The default is the Application Name with only\n\ -\ alphanumerics, dots, and dashes (no whitespace)\n\ -\ --win-shortcut\n\ -\ Creates a desktop shortcut for the application\n\ -\ --win-upgrade-uuid \n\ -\ UUID associated with upgrades for this package\n\ - -MSG_Help_win_install_dir=\ -\Relative sub-path under the default installation location\n\ - -MSG_Help_mac_launcher=\ -\ --mac-bundle-identifier \n\ -\ An identifier that uniquely identifies the application for MacOSX\n\ -\ Defaults to the value of --identifier option.\n\ -\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ -\ and period (.) characters.\n\ -\ --mac-bundle-name \n\ -\ Name of the application as it appears in the Menu Bar\n\ -\ This can be different from the application name.\n\ -\ This name must be less than 16 characters long and be suitable for\n\ -\ displaying in the menu bar and the application Info window.\n\ -\ Defaults to the application name.\n\ -\ --mac-bundle-signing-prefix \n\ -\ When signing the application bundle, this value is prefixed to all\n\ -\ components that need to be signed that don't have\n\ -\ an existing bundle identifier.\n\ -\ --mac-sign\n\ -\ Request that the bundle be signed\n\ -\ --mac-signing-keychain \n\ -\ Path of the keychain to use\n\ -\ (absolute path or relative to the current directory)\n\ -\ If not specified, the standard keychains are used.\n\ -\ --mac-signing-key-user-name \n\ -\ User name portion of the typical\n\ -\ "Mac Developer ID Application: " signing key\n\ - -MSG_Help_linux_install=\ -\ --linux-bundle-name \n\ -\ Name for Linux bundle, defaults to the application name\n\ -\ --linux-deb-maintainer \n\ -\ Maintainer for .deb bundle\n\ -\ --linux-menu-group \n\ -\ Menu group this application is placed in\n\ -\ --linux-package-deps\n\ -\ Required packages or capabilities for the application\n\ -\ --linux-rpm-license-type \n\ -\ Type of the license ("License: " of the RPM .spec)\n\ - -MSG_Help_mac_linux_install_dir=\ -\Absolute path of the installation directory of the application\n\ - -MSG_Help_default_install_dir=\ -\Absolute path of the installation directory of the application on OS X\n\ -\ or Linux. Relative sub-path of the installation location of the application\n\ -\ such as "Program Files" or "AppData" on Windows.\n\ - -MSG_Help_no_args=Usage: jpackage \n\ -\Use jpackage --help (or -h) for a list of possible options\ - --- /dev/null 2019-11-18 21:00:18.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/resources/HelpResources_ja.properties 2019-11-18 21:00:13.114212000 -0500 @@ -0,0 +1,275 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +MSG_Help=Usage: jpackage \n\ +\n\ +Sample usages:\n\ +--------------\n\ +\ Generate an application package suitable for the host system:\n\ +\ For a modular application:\n\ +\ jpackage -n name -p modulePath -m moduleName/className\n\ +\ For a non-modular application:\n\ +\ jpackage -i inputDir -n name \\\n\ +\ --main-class className --main-jar myJar.jar\n\ +\ From a pre-built application image:\n\ +\ jpackage -n name --app-image appImageDir\n\ +\ Generate an application image:\n\ +\ For a modular application:\n\ +\ jpackage --type app-image -n name -p modulePath \\\n\ +\ -m moduleName/className\n\ +\ For a non-modular application:\n\ +\ jpackage --type app-image -i inputDir -n name \\\n\ +\ --main-class className --main-jar myJar.jar\n\ +\ To provide your own options to jlink, run jlink separately:\n\ +\ jlink --output appRuntimeImage -p modulePath -m moduleName \\\n\ +\ --no-header-files [...]\n\ +\ jpackage --type app-image -n name \\\n\ +\ -m moduleName/className --runtime-image appRuntimeImage\n\ +\ Generate a Java runtime package:\n\ +\ jpackage -n name --runtime-image \n\ +\n\ +Generic Options:\n\ +\ @ \n\ +\ Read options and/or mode from a file \n\ +\ This option can be used multiple times.\n\ +\ --type -t \n\ +\ The type of package to create\n\ +\ Valid values are: {1} \n\ +\ If this option is not specified a platform dependent\n\ +\ default type will be created.\n\ +\ --app-version \n\ +\ Version of the application and/or package\n\ +\ --copyright \n\ +\ Copyright for the application\n\ +\ --description \n\ +\ Description of the application\n\ +\ --help -h \n\ +\ Print the usage text with a list and description of each valid\n\ +\ option for the current platform to the output stream, and exit\n\ +\ --name -n \n\ +\ Name of the application and/or package\n\ +\ --dest -d \n\ +\ Path where generated output file is placed\n\ +\ Defaults to the current working directory.\n\ +\ (absolute path or relative to the current directory)\n\ +\ --temp \n\ +\ Path of a new or empty directory used to create temporary files\n\ +\ (absolute path or relative to the current directory)\n\ +\ If specified, the temp dir will not be removed upon the task\n\ +\ completion and must be removed manually\n\ +\ If not specified, a temporary directory will be created and\n\ +\ removed upon the task completion.\n\ +\ --vendor \n\ +\ Vendor of the application\n\ +\ --verbose\n\ +\ Enables verbose output\n\ +\ --version\n\ +\ Print the product version to the output stream and exit\n\ +\n\ +\Options for creating the runtime image:\n\ +\ --add-modules [,...]\n\ +\ A comma (",") separated list of modules to add.\n\ +\ This module list, along with the main module (if specified)\n\ +\ will be passed to jlink as the --add-module argument.\n\ +\ if not specified, either just the main module (if --module is\n\ +\ specified), or the default set of modules (if --main-jar is \n\ +\ specified) are used.\n\ +\ This option can be used multiple times.\n\ +\ --module-path -p ...\n\ +\ A {0} separated list of paths\n\ +\ Each path is either a directory of modules or the path to a\n\ +\ modular jar.\n\ +\ (each path is absolute or relative to the current directory)\n\ +\ This option can be used multiple times.\n\ +\ --bind-services \n\ +\ Pass on --bind-services option to jlink (which will link in \n\ +\ service provider modules and their dependences) \n\ +\ --runtime-image \n\ +\ Path of the predefined runtime image that will be copied into\n\ +\ the application image\n\ +\ (absolute path or relative to the current directory)\n\ +\ If --runtime-image is not specified, jpackage will run jlink to\n\ +\ create the runtime image using options:\n\ +\ --strip-debug, --no-header-files, --no-man-pages, and\n\ +\ --strip-native-commands.\n\ +\n\ +\Options for creating the application image:\n\ +\ --icon \n\ +\ Path of the icon of the application package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --input -i \n\ +\ Path of the input directory that contains the files to be packaged\n\ +\ (absolute path or relative to the current directory)\n\ +\ All files in the input directory will be packaged into the\n\ +\ application image.\n\ +\n\ +\Options for creating the application launcher(s):\n\ +\ --add-launcher =\n\ +\ Name of launcher, and a path to a Properties file that contains\n\ +\ a list of key, value pairs\n\ +\ (absolute path or relative to the current directory)\n\ +\ The keys "module", "main-jar", "main-class",\n\ +\ "arguments", "java-options", "app-version", "icon", and\n\ +\ "win-console" can be used.\n\ +\ These options are added to, or used to overwrite, the original\n\ +\ command line options to build an additional alternative launcher.\n\ +\ The main application launcher will be built from the command line\n\ +\ options. Additional alternative launchers can be built using\n\ +\ this option, and this option can be used multiple times to\n\ +\ build multiple additional launchers. \n\ +\ --arguments
\n\ +\ Command line arguments to pass to the main class if no command\n\ +\ line arguments are given to the launcher\n\ +\ This option can be used multiple times.\n\ +\ --java-options \n\ +\ Options to pass to the Java runtime\n\ +\ This option can be used multiple times.\n\ +\ --main-class \n\ +\ Qualified name of the application main class to execute\n\ +\ This option can only be used if --main-jar is specified.\n\ +\ --main-jar
\n\ +\ The main JAR of the application; containing the main class\n\ +\ (specified as a path relative to the input path)\n\ +\ Either --module or --main-jar option can be specified but not\n\ +\ both.\n\ +\ --module -m [/
]\n\ +\ The main module (and optionally main class) of the application\n\ +\ This module must be located on the module path.\n\ +\ When this option is specified, the main module will be linked\n\ +\ in the Java runtime image. Either --module or --main-jar\n\ +\ option can be specified but not both.\n\ +{2}\n\ +\Options for creating the application package:\n\ +\ --app-image \n\ +\ Location of the predefined application image that is used\n\ +\ to build an installable package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --file-associations \n\ +\ Path to a Properties file that contains list of key, value pairs\n\ +\ (absolute path or relative to the current directory)\n\ +\ The keys "extension", "mime-type", "icon", and "description"\n\ +\ can be used to describe the association.\n\ +\ This option can be used multiple times.\n\ +\ --install-dir \n\ +\ {4}\ +\ --license-file \n\ +\ Path to the license file\n\ +\ (absolute path or relative to the current directory)\n\ +\ --resource-dir \n\ +\ Path to override jpackage resources\n\ +\ Icons, template files, and other resources of jpackage can be\n\ +\ over-ridden by adding replacement resources to this directory.\n\ +\ (absolute path or relative to the current directory)\n\ +\ --runtime-image \n\ +\ Path of the predefined runtime image to install\n\ +\ (absolute path or relative to the current directory)\n\ +\ Option is required when creating a runtime package.\n\ +\n\ +\Platform dependent options for creating the application package:\n\ +{3} + +MSG_Help_win_launcher=\ +\n\ +\Platform dependent option for creating the application launcher:\n\ +\ --win-console\n\ +\ Creates a console launcher for the application, should be\n\ +\ specified for application which requires console interactions\n\ + +MSG_Help_win_install=\ +\ --win-dir-chooser\n\ +\ Adds a dialog to enable the user to choose a directory in which\n\ +\ the product is installed\n\ +\ --win-menu\n\ +\ Adds the application to the system menu\n\ +\ --win-menu-group \n\ +\ Start Menu group this application is placed in\n\ +\ --win-per-user-install\n\ +\ Request to perform an install on a per-user basis\n\ +\ --win-shortcut\n\ +\ Creates a desktop shortcut for the application\n\ +\ --win-upgrade-uuid \n\ +\ UUID associated with upgrades for this package\n\ + +MSG_Help_win_install_dir=\ +\Relative sub-path under the default installation location\n\ + +MSG_Help_mac_launcher=\ +\ --mac-package-identifier \n\ +\ An identifier that uniquely identifies the application for macOS\n\ +\ Defaults to the main class name.\n\ +\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ +\ and period (.) characters.\n\ +\ --mac-package-name \n\ +\ Name of the application as it appears in the Menu Bar\n\ +\ This can be different from the application name.\n\ +\ This name must be less than 16 characters long and be suitable for\n\ +\ displaying in the menu bar and the application Info window.\n\ +\ Defaults to the application name.\n\ +\ --mac-package-signing-prefix \n\ +\ When signing the application package, this value is prefixed\n\ +\ to all components that need to be signed that don't have\n\ +\ an existing package identifier.\n\ +\ --mac-sign\n\ +\ Request that the package be signed\n\ +\ --mac-signing-keychain \n\ +\ Path of the keychain to search for the signing identity\n\ +\ (absolute path or relative to the current directory).\n\ +\ If not specified, the standard keychains are used.\n\ +\ --mac-signing-key-user-name \n\ +\ Team name portion in Apple signing identities' names.\n\ +\ For example "Developer ID Application: "\n\ + +MSG_Help_linux_install=\ +\ --linux-package-name \n\ +\ Name for Linux package, defaults to the application name\n\ +\ --linux-deb-maintainer \n\ +\ Maintainer for .deb package\n\ +\ --linux-menu-group \n\ +\ Menu group this application is placed in\n\ +\ --linux-package-deps\n\ +\ Required packages or capabilities for the application\n\ +\ --linux-rpm-license-type \n\ +\ Type of the license ("License: " of the RPM .spec)\n\ +\ --linux-app-release \n\ +\ Release value of the RPM .spec file or \n\ +\ Debian revision value of the DEB control file.\n\ +\ --linux-app-category \n\ +\ Group value of the RPM .spec file or \n\ +\ Section value of DEB control file.\n\ +\ --linux-shortcut\n\ +\ Creates a shortcut for the application\n\ + +MSG_Help_mac_linux_install_dir=\ +\Absolute path of the installation directory of the application\n\ + +MSG_Help_default_install_dir=\ +\Absolute path of the installation directory of the application on OS X\n\ +\ or Linux. Relative sub-path of the installation location of\n\ +\ the application such as "Program Files" or "AppData" on Windows.\n\ + +MSG_Help_no_args=Usage: jpackage \n\ +\Use jpackage --help (or -h) for a list of possible options\ + --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties 2019-11-18 21:00:37.820984900 -0500 +++ /dev/null 2019-11-18 21:00:39.000000000 -0500 @@ -1,279 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -MSG_Help=Usage: jpackage \n\ -\n\ -where mode is one of: \n\ -\ create-app-image\n\ -\ Generates a platform-specific application image.\n\ -\ create-installer\n\ -\ Generates a platform-specific installer for the application.\n\ -\ \n\ -Sample usages:\n\ ---------------\n\ -\ Generate a non-modular application image:\n\ -\ jpackage create-app-image -o outputdir -i inputdir -n name \\\n\ -\ --main-class className --main-jar MyJar.jar\n\ -\ Generate a modular application image:\n\ -\ jpackage create-app-image -o outputdir -n name \\\n\ -\ -p modulePath -m moduleName/className\n\ -\ To provide your own options to jlink, run jlink separately:\n\ -\ jlink --output appRuntimeImage -p ModulePath -m moduleName\\\n\ -\ --no-header-files [...]\n\ -\ jpackage create-app-image -o outputdir -n name\\\n\ -\ -m moduleName/className --runtime-image appRuntimeIMage\n\ -\ Generate an application installer:\n\ -\ jpackage create-installer -o outputdir -n name \\\n\ -\ -p modulePath -m moduleName/className\n\ -\ jpackage create-installer -i inputdir -o outputdir -n name \\\n\ -\ --main-class package.ClassName --main-jar MyJar.jar\n\ -\ jpackage create-installer -o outputdir -n \\\n\ -\ --app-image [--installer-type ]\n\ -\ Generate a Java runtime installer:\n\ -\ jpackage create-installer -o outputdir -n name \\\n\ -\ --runtime-image \n\ -\n\ -Generic Options:\n\ -\ @ \n\ -\ Read options and/or mode from a file \n\ -\ This option can be used multiple times.\n\ -\ --app-version \n\ -\ Version of the application and/or installer\n\ -\ --copyright \n\ -\ Copyright for the application\n\ -\ --description \n\ -\ Description of the application\n\ -\ --help -h \n\ -\ Print the usage text with a list and description of each valid\n\ -\ option for the current platform to the output stream, and exit\n\ -\ --name -n \n\ -\ Name of the application and/or installer\n\ -\ --output -o \n\ -\ Path where generated output file is placed\n\ -\ (absolute path or relative to the current directory)\n\ -\ --temp-root \n\ -\ Path of a new or empty directory used to create temporary files\n\ -\ (absolute path or relative to the current directory)\n\ -\ If specified, the temp-root will not be removed upon the task\n\ -\ completion and must be removed manually\n\ -\ If not specified, a temporary directory will be created and\n\ -\ removed upon the task completion.\n\ -\ --vendor \n\ -\ Vendor of the application\n\ -\ --verbose\n\ -\ Enables verbose output\n\ -\ --version\n\ -\ Print the product version to the output stream and exit\n\ -\n\ -\Options for creating the runtime image:\n\ -\ --add-modules [,...]\n\ -\ A comma (",") separated list of modules to add.\n\ -\ This module list, along with the main module (if specified)\n\ -\ will be passed to jlink as the --add-module argument.\n\ -\ if not specified, either just the main module (if --module is\n\ -\ specified), or the default set of modules (if --main-jar is \n\ -\ specified) are used.\n\ -\ This option can be used multiple times.\n\ -\ --module-path -p ...\n\ -\ A {0} separated list of paths\n\ -\ Each path is either a directory of modules or the path to a\n\ -\ modular jar.\n\ -\ (each path is absolute or relative to the current directory)\n\ -\ This option can be used multiple times.\n\ -\ --runtime-image \n\ -\ Path of the predefined runtime image that will be copied into\n\ -\ the application image\n\ -\ (absolute path or relative to the current directory)\n\ -\ If --runtime-image is not specified, jpackage will run jlink to\n\ -\ create the runtime image using options:\n\ -\ --strip-debug, --no-header-files, --no-man-pages, and\n\ -\ --strip-native-commands. --bind-services will also be added if\n\ -\ --add-modules is not specified.\n\ -\n\ -\Options for creating the application image:\n\ -\ --icon \n\ -\ Path of the icon of the application bundle\n\ -\ (absolute path or relative to the current directory)\n\ -\ --input -i \n\ -\ Path of the input directory that contains the files to be packaged\n\ -\ (absolute path or relative to the current directory)\n\ -\ All files in the input directory will be packaged into the\n\ -\ application image.\n\ -\n\ -\Options for creating the application launcher(s):\n\ -\ --add-launcher =\n\ -\ Name of launcher, and a path to a Properties file that contains\n\ -\ a list of key, value pairs\n\ -\ (absolute path or relative to the current directory)\n\ -\ The keys "module", "add-modules", "main-jar", "main-class",\n\ -\ "arguments", "java-options", "app-version", "icon", and\n\ -\ "win-console" can be used.\n\ -\ These options are added to, or used to overwrite, the original\n\ -\ command line options to build an additional alternative launcher.\n\ -\ The main application launcher will be built from the command line\n\ -\ options. Additional alternative launchers can be built using\n\ -\ this option, and this option can be used multiple times to\n\ -\ build multiple additional launchers. \n\ -\ --arguments
\n\ -\ Command line arguments to pass to the main class if no command\n\ -\ line arguments are given to the launcher\n\ -\ This option can be used multiple times.\n\ -\ --java-options \n\ -\ Options to pass to the Java runtime\n\ -\ This option can be used multiple times.\n\ -\ --main-class \n\ -\ Qualified name of the application main class to execute\n\ -\ This option can only be used if --main-jar is specified.\n\ -\ --main-jar
\n\ -\ The main JAR of the application; containing the main class\n\ -\ (specified as a path relative to the input path)\n\ -\ Either --module or --main-jar option can be specified but not\n\ -\ both.\n\ -\ --module -m [/
]\n\ -\ The main module (and optionally main class) of the application\n\ -\ This module must be located on the module path.\n\ -\ When this option is specified, the main module will be linked\n\ -\ in the Java runtime image. Either --module or --main-jar\n\ -\ option can be specified but not both.\n\ -{2}\n\ -\Options for creating the application installer(s):\n\ -\ --app-image \n\ -\ Location of the predefined application image that is used\n\ -\ to build an installable package\n\ -\ (absolute path or relative to the current directory)\n\ -\ See create-app-image mode options to create the application image.\n\ -\ --file-associations \n\ -\ Path to a Properties file that contains list of key, value pairs\n\ -\ (absolute path or relative to the current directory)\n\ -\ The keys "extension", "mime-type", "icon", and "description"\n\ -\ can be used to describe the association.\n\ -\ This option can be used multiple times.\n\ -\ --identifier \n\ -\ An identifier that uniquely identifies the application\n\ -\ Defaults to the main class name.\n\ -\ The value should be a valid DNS name.\n\ -\ --install-dir \n\ -\ {4}\ -\ --installer-type \n\ -\ The type of the installer to create\n\ -\ Valid values are: {1} \n\ -\ If this option is not specified (in create-installer mode) all\n\ -\ supported types of installable packages for the current\n\ -\ platform will be created.\n\ -\ --license-file \n\ -\ Path to the license file\n\ -\ (absolute path or relative to the current directory)\n\ -\ --resource-dir \n\ -\ Path to override jpackage resources\n\ -\ Icons, template files, and other resources of jpackage can be\n\ -\ over-ridden by adding replacement resources to this directory.\n\ -\ (absolute path or relative to the current directory)\n\ -\ --runtime-image \n\ -\ Path of the predefined runtime image to install\n\ -\ (absolute path or relative to the current directory)\n\ -\ Option is required when creating a runtime installer.\n\ -\n\ -\Platform dependent options for creating the application installer(s):\n\ -{3} - -MSG_Help_win_launcher=\ -\n\ -\Platform dependent option for creating the application launcher:\n\ -\ --win-console\n\ -\ Creates a console launcher for the application, should be\n\ -\ specified for application which requires console interactions\n\ - -MSG_Help_win_install=\ -\ --win-dir-chooser\n\ -\ Adds a dialog to enable the user to choose a directory in which\n\ -\ the product is installed\n\ -\ --win-menu\n\ -\ Adds the application to the system menu\n\ -\ --win-menu-group \n\ -\ Start Menu group this application is placed in\n\ -\ --win-per-user-install\n\ -\ Request to perform an install on a per-user basis\n\ -\ --win-registry-name \n\ -\ Name of the application for registry references.\n\ -\ The default is the Application Name with only\n\ -\ alphanumerics, dots, and dashes (no whitespace)\n\ -\ --win-shortcut\n\ -\ Creates a desktop shortcut for the application\n\ -\ --win-upgrade-uuid \n\ -\ UUID associated with upgrades for this package\n\ - -MSG_Help_win_install_dir=\ -\Relative sub-path under the default installation location\n\ - -MSG_Help_mac_launcher=\ -\ --mac-bundle-identifier \n\ -\ An identifier that uniquely identifies the application for MacOSX\n\ -\ Defaults to the value of --identifier option.\n\ -\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ -\ and period (.) characters.\n\ -\ --mac-bundle-name \n\ -\ Name of the application as it appears in the Menu Bar\n\ -\ This can be different from the application name.\n\ -\ This name must be less than 16 characters long and be suitable for\n\ -\ displaying in the menu bar and the application Info window.\n\ -\ Defaults to the application name.\n\ -\ --mac-bundle-signing-prefix \n\ -\ When signing the application bundle, this value is prefixed to all\n\ -\ components that need to be signed that don't have\n\ -\ an existing bundle identifier.\n\ -\ --mac-sign\n\ -\ Request that the bundle be signed\n\ -\ --mac-signing-keychain \n\ -\ Path of the keychain to use\n\ -\ (absolute path or relative to the current directory)\n\ -\ If not specified, the standard keychains are used.\n\ -\ --mac-signing-key-user-name \n\ -\ User name portion of the typical\n\ -\ "Mac Developer ID Application: " signing key\n\ - -MSG_Help_linux_install=\ -\ --linux-bundle-name \n\ -\ Name for Linux bundle, defaults to the application name\n\ -\ --linux-deb-maintainer \n\ -\ Maintainer for .deb bundle\n\ -\ --linux-menu-group \n\ -\ Menu group this application is placed in\n\ -\ --linux-package-deps\n\ -\ Required packages or capabilities for the application\n\ -\ --linux-rpm-license-type \n\ -\ Type of the license ("License: " of the RPM .spec)\n\ - -MSG_Help_mac_linux_install_dir=\ -\Absolute path of the installation directory of the application\n\ - -MSG_Help_default_install_dir=\ -\Absolute path of the installation directory of the application on OS X\n\ -\ or Linux. Relative sub-path of the installation location of the application\n\ -\ such as "Program Files" or "AppData" on Windows.\n\ - -MSG_Help_no_args=Usage: jpackage \n\ -\Use jpackage --help (or -h) for a list of possible options\ - --- /dev/null 2019-11-18 21:00:39.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/resources/HelpResources_zh_CN.properties 2019-11-18 21:00:34.147047800 -0500 @@ -0,0 +1,275 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +MSG_Help=Usage: jpackage \n\ +\n\ +Sample usages:\n\ +--------------\n\ +\ Generate an application package suitable for the host system:\n\ +\ For a modular application:\n\ +\ jpackage -n name -p modulePath -m moduleName/className\n\ +\ For a non-modular application:\n\ +\ jpackage -i inputDir -n name \\\n\ +\ --main-class className --main-jar myJar.jar\n\ +\ From a pre-built application image:\n\ +\ jpackage -n name --app-image appImageDir\n\ +\ Generate an application image:\n\ +\ For a modular application:\n\ +\ jpackage --type app-image -n name -p modulePath \\\n\ +\ -m moduleName/className\n\ +\ For a non-modular application:\n\ +\ jpackage --type app-image -i inputDir -n name \\\n\ +\ --main-class className --main-jar myJar.jar\n\ +\ To provide your own options to jlink, run jlink separately:\n\ +\ jlink --output appRuntimeImage -p modulePath -m moduleName \\\n\ +\ --no-header-files [...]\n\ +\ jpackage --type app-image -n name \\\n\ +\ -m moduleName/className --runtime-image appRuntimeImage\n\ +\ Generate a Java runtime package:\n\ +\ jpackage -n name --runtime-image \n\ +\n\ +Generic Options:\n\ +\ @ \n\ +\ Read options and/or mode from a file \n\ +\ This option can be used multiple times.\n\ +\ --type -t \n\ +\ The type of package to create\n\ +\ Valid values are: {1} \n\ +\ If this option is not specified a platform dependent\n\ +\ default type will be created.\n\ +\ --app-version \n\ +\ Version of the application and/or package\n\ +\ --copyright \n\ +\ Copyright for the application\n\ +\ --description \n\ +\ Description of the application\n\ +\ --help -h \n\ +\ Print the usage text with a list and description of each valid\n\ +\ option for the current platform to the output stream, and exit\n\ +\ --name -n \n\ +\ Name of the application and/or package\n\ +\ --dest -d \n\ +\ Path where generated output file is placed\n\ +\ Defaults to the current working directory.\n\ +\ (absolute path or relative to the current directory)\n\ +\ --temp \n\ +\ Path of a new or empty directory used to create temporary files\n\ +\ (absolute path or relative to the current directory)\n\ +\ If specified, the temp dir will not be removed upon the task\n\ +\ completion and must be removed manually\n\ +\ If not specified, a temporary directory will be created and\n\ +\ removed upon the task completion.\n\ +\ --vendor \n\ +\ Vendor of the application\n\ +\ --verbose\n\ +\ Enables verbose output\n\ +\ --version\n\ +\ Print the product version to the output stream and exit\n\ +\n\ +\Options for creating the runtime image:\n\ +\ --add-modules [,...]\n\ +\ A comma (",") separated list of modules to add.\n\ +\ This module list, along with the main module (if specified)\n\ +\ will be passed to jlink as the --add-module argument.\n\ +\ if not specified, either just the main module (if --module is\n\ +\ specified), or the default set of modules (if --main-jar is \n\ +\ specified) are used.\n\ +\ This option can be used multiple times.\n\ +\ --module-path -p ...\n\ +\ A {0} separated list of paths\n\ +\ Each path is either a directory of modules or the path to a\n\ +\ modular jar.\n\ +\ (each path is absolute or relative to the current directory)\n\ +\ This option can be used multiple times.\n\ +\ --bind-services \n\ +\ Pass on --bind-services option to jlink (which will link in \n\ +\ service provider modules and their dependences) \n\ +\ --runtime-image \n\ +\ Path of the predefined runtime image that will be copied into\n\ +\ the application image\n\ +\ (absolute path or relative to the current directory)\n\ +\ If --runtime-image is not specified, jpackage will run jlink to\n\ +\ create the runtime image using options:\n\ +\ --strip-debug, --no-header-files, --no-man-pages, and\n\ +\ --strip-native-commands.\n\ +\n\ +\Options for creating the application image:\n\ +\ --icon \n\ +\ Path of the icon of the application package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --input -i \n\ +\ Path of the input directory that contains the files to be packaged\n\ +\ (absolute path or relative to the current directory)\n\ +\ All files in the input directory will be packaged into the\n\ +\ application image.\n\ +\n\ +\Options for creating the application launcher(s):\n\ +\ --add-launcher =\n\ +\ Name of launcher, and a path to a Properties file that contains\n\ +\ a list of key, value pairs\n\ +\ (absolute path or relative to the current directory)\n\ +\ The keys "module", "main-jar", "main-class",\n\ +\ "arguments", "java-options", "app-version", "icon", and\n\ +\ "win-console" can be used.\n\ +\ These options are added to, or used to overwrite, the original\n\ +\ command line options to build an additional alternative launcher.\n\ +\ The main application launcher will be built from the command line\n\ +\ options. Additional alternative launchers can be built using\n\ +\ this option, and this option can be used multiple times to\n\ +\ build multiple additional launchers. \n\ +\ --arguments
\n\ +\ Command line arguments to pass to the main class if no command\n\ +\ line arguments are given to the launcher\n\ +\ This option can be used multiple times.\n\ +\ --java-options \n\ +\ Options to pass to the Java runtime\n\ +\ This option can be used multiple times.\n\ +\ --main-class \n\ +\ Qualified name of the application main class to execute\n\ +\ This option can only be used if --main-jar is specified.\n\ +\ --main-jar
\n\ +\ The main JAR of the application; containing the main class\n\ +\ (specified as a path relative to the input path)\n\ +\ Either --module or --main-jar option can be specified but not\n\ +\ both.\n\ +\ --module -m [/
]\n\ +\ The main module (and optionally main class) of the application\n\ +\ This module must be located on the module path.\n\ +\ When this option is specified, the main module will be linked\n\ +\ in the Java runtime image. Either --module or --main-jar\n\ +\ option can be specified but not both.\n\ +{2}\n\ +\Options for creating the application package:\n\ +\ --app-image \n\ +\ Location of the predefined application image that is used\n\ +\ to build an installable package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --file-associations \n\ +\ Path to a Properties file that contains list of key, value pairs\n\ +\ (absolute path or relative to the current directory)\n\ +\ The keys "extension", "mime-type", "icon", and "description"\n\ +\ can be used to describe the association.\n\ +\ This option can be used multiple times.\n\ +\ --install-dir \n\ +\ {4}\ +\ --license-file \n\ +\ Path to the license file\n\ +\ (absolute path or relative to the current directory)\n\ +\ --resource-dir \n\ +\ Path to override jpackage resources\n\ +\ Icons, template files, and other resources of jpackage can be\n\ +\ over-ridden by adding replacement resources to this directory.\n\ +\ (absolute path or relative to the current directory)\n\ +\ --runtime-image \n\ +\ Path of the predefined runtime image to install\n\ +\ (absolute path or relative to the current directory)\n\ +\ Option is required when creating a runtime package.\n\ +\n\ +\Platform dependent options for creating the application package:\n\ +{3} + +MSG_Help_win_launcher=\ +\n\ +\Platform dependent option for creating the application launcher:\n\ +\ --win-console\n\ +\ Creates a console launcher for the application, should be\n\ +\ specified for application which requires console interactions\n\ + +MSG_Help_win_install=\ +\ --win-dir-chooser\n\ +\ Adds a dialog to enable the user to choose a directory in which\n\ +\ the product is installed\n\ +\ --win-menu\n\ +\ Adds the application to the system menu\n\ +\ --win-menu-group \n\ +\ Start Menu group this application is placed in\n\ +\ --win-per-user-install\n\ +\ Request to perform an install on a per-user basis\n\ +\ --win-shortcut\n\ +\ Creates a desktop shortcut for the application\n\ +\ --win-upgrade-uuid \n\ +\ UUID associated with upgrades for this package\n\ + +MSG_Help_win_install_dir=\ +\Relative sub-path under the default installation location\n\ + +MSG_Help_mac_launcher=\ +\ --mac-package-identifier \n\ +\ An identifier that uniquely identifies the application for macOS\n\ +\ Defaults to the main class name.\n\ +\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ +\ and period (.) characters.\n\ +\ --mac-package-name \n\ +\ Name of the application as it appears in the Menu Bar\n\ +\ This can be different from the application name.\n\ +\ This name must be less than 16 characters long and be suitable for\n\ +\ displaying in the menu bar and the application Info window.\n\ +\ Defaults to the application name.\n\ +\ --mac-package-signing-prefix \n\ +\ When signing the application package, this value is prefixed\n\ +\ to all components that need to be signed that don't have\n\ +\ an existing package identifier.\n\ +\ --mac-sign\n\ +\ Request that the package be signed\n\ +\ --mac-signing-keychain \n\ +\ Path of the keychain to search for the signing identity\n\ +\ (absolute path or relative to the current directory).\n\ +\ If not specified, the standard keychains are used.\n\ +\ --mac-signing-key-user-name \n\ +\ Team name portion in Apple signing identities' names.\n\ +\ For example "Developer ID Application: "\n\ + +MSG_Help_linux_install=\ +\ --linux-package-name \n\ +\ Name for Linux package, defaults to the application name\n\ +\ --linux-deb-maintainer \n\ +\ Maintainer for .deb package\n\ +\ --linux-menu-group \n\ +\ Menu group this application is placed in\n\ +\ --linux-package-deps\n\ +\ Required packages or capabilities for the application\n\ +\ --linux-rpm-license-type \n\ +\ Type of the license ("License: " of the RPM .spec)\n\ +\ --linux-app-release \n\ +\ Release value of the RPM .spec file or \n\ +\ Debian revision value of the DEB control file.\n\ +\ --linux-app-category \n\ +\ Group value of the RPM .spec file or \n\ +\ Section value of DEB control file.\n\ +\ --linux-shortcut\n\ +\ Creates a shortcut for the application\n\ + +MSG_Help_mac_linux_install_dir=\ +\Absolute path of the installation directory of the application\n\ + +MSG_Help_default_install_dir=\ +\Absolute path of the installation directory of the application on OS X\n\ +\ or Linux. Relative sub-path of the installation location of\n\ +\ the application such as "Program Files" or "AppData" on Windows.\n\ + +MSG_Help_no_args=Usage: jpackage \n\ +\Use jpackage --help (or -h) for a list of possible options\ + --- /dev/null 2019-11-18 21:00:58.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/resources/MainResources.properties 2019-11-18 21:00:55.207909500 -0500 @@ -0,0 +1,92 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +param.copyright.default=Copyright (C) {0,date,YYYY} +param.description.default=None +param.vendor.default=Unknown + +message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize). +message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize). +message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}). +message.using-custom-resource=Using custom package resource {0} (loaded from {1}). +message.creating-app-bundle=Creating app package: {0} in {1} +message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists +message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists +message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}: {1} does not exists +message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists +message.debug-working-directory=Kept working directory for debug: {0} +message.bundle-created=Succeeded in building {0} package +message.module-version=Using version "{0}" from module "{1}" as application version +message.module-class=Using class "{0}" from module "{1}" as application main class + +error.cannot-create-output-dir=Destination directory {0} cannot be created +error.cannot-write-to-output-dir=Destination directory {0} is not writable +error.root-exists=Error: Application destination directory {0} already exists +error.no-main-class-with-main-jar=A main class was not specified nor was one found in the jar {0} +error.no-main-class-with-main-jar.advice=Specify a main class or ensure that the jar {0} specifies one in the manifest +error.no-main-class=A main class was not specified nor was one found in the supplied application resources +error.no-main-class.advice=Please specify a application class or ensure that the appResources has a jar containing one in the manifest +error.main-jar-does-not-exist=The configured main jar does not exist {0} in the input directory +error.main-jar-does-not-exist.advice=The main jar must be specified relative to the input directory (not an absolute path), and must exist within that directory + +error.tool-not-found=Can not find {0}. Reason: {1} +error.tool-not-found.advice=Please install {0} +error.tool-old-version=Can not find {0} {1} or newer +error.tool-old-version.advice=Please install {0} {1} or newer +error.jlink.failed=jlink failed with: {0} + +warning.module.does.not.exist=Module [{0}] does not exist +warning.no.jdk.modules.found=Warning: No JDK Modules found + +MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a package +MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1} \n\ +Advice to fix: {2} +MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem: {1} +MSG_BundlerRuntimeException=Bundler {0} failed because of {1} +MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a package + +ERR_NoMainClass=Error: Main application class is missing +ERR_UnsupportedOption=Error: Option [{0}] is not valid on this platform +ERR_InvalidTypeOption=Error: Option [{0}] is not valid with type [{1}] +ERR_NoInstallerEntryPoint=Error: Option [{0}] is not valid without --module or --main-jar entry point option + +ERR_MissingArgument=Error: Missing argument: {0} +ERR_MissingAppResources=Error: No application jars found +ERR_AppImageNotExist=Error: App image directory "{0}" does not exist +ERR_NoAddLauncherName=Error: --add-launcher option requires a name and a file path (--add-launcher =) +ERR_NoUniqueName=Error: --add-launcher = requires a unique name +ERR_NoJreInstallerName=Error: Jre Installers require a name parameter +ERR_InvalidAppName=Error: Invalid Application name: {0} +ERR_InvalidSLName=Error: Invalid Add Launcher name: {0} +ERR_LicenseFileNotExit=Error: Specified license file does not exist +ERR_BuildRootInvalid=Error: temp ({0}) must be non-existant or empty directory +ERR_InvalidOption=Error: Invalid Option: [{0}] +ERR_InvalidInstallerType=Error: Invalid or unsupported type: [{0}] +ERR_BothMainJarAndModule=Error: Cannot have both --main-jar and --module Options +ERR_NoEntryPoint=Error: creating application image requires --main-jar or --module Option +ERR_InputNotDirectory=Error: Input directory specified is not a directory: {0} +ERR_CannotReadInputDir=Error: No permission to read from input directory: {0} +ERR_CannotParseOptions=Error: Processing @filename option: {0} --- /dev/null 2019-11-18 21:01:09.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/resources/MainResources_ja.properties 2019-11-18 21:01:06.385132000 -0500 @@ -0,0 +1,92 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +param.copyright.default=Copyright (C) {0,date,YYYY} +param.description.default=None +param.vendor.default=Unknown + +message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize). +message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize). +message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}). +message.using-custom-resource=Using custom package resource {0} (loaded from {1}). +message.creating-app-bundle=Creating app package: {0} in {1} +message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists +message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists +message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}: {1} does not exists +message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists +message.debug-working-directory=Kept working directory for debug: {0} +message.bundle-created=Succeeded in building {0} package +message.module-version=Using version "{0}" from module "{1}" as application version +message.module-class=Using class "{0}" from module "{1}" as application main class + +error.cannot-create-output-dir=Destination directory {0} cannot be created +error.cannot-write-to-output-dir=Destination directory {0} is not writable +error.root-exists=Error: Application destination directory {0} already exists +error.no-main-class-with-main-jar=A main class was not specified nor was one found in the jar {0} +error.no-main-class-with-main-jar.advice=Specify a main class or ensure that the jar {0} specifies one in the manifest +error.no-main-class=A main class was not specified nor was one found in the supplied application resources +error.no-main-class.advice=Please specify a application class or ensure that the appResources has a jar containing one in the manifest +error.main-jar-does-not-exist=The configured main jar does not exist {0} in the input directory +error.main-jar-does-not-exist.advice=The main jar must be specified relative to the input directory (not an absolute path), and must exist within that directory + +error.tool-not-found=Can not find {0}. Reason: {1} +error.tool-not-found.advice=Please install {0} +error.tool-old-version=Can not find {0} {1} or newer +error.tool-old-version.advice=Please install {0} {1} or newer +error.jlink.failed=jlink failed with: {0} + +warning.module.does.not.exist=Module [{0}] does not exist +warning.no.jdk.modules.found=Warning: No JDK Modules found + +MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a package +MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1} \n\ +Advice to fix: {2} +MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem: {1} +MSG_BundlerRuntimeException=Bundler {0} failed because of {1} +MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a package + +ERR_NoMainClass=Error: Main application class is missing +ERR_UnsupportedOption=Error: Option [{0}] is not valid on this platform +ERR_InvalidTypeOption=Error: Option [{0}] is not valid with type [{1}] +ERR_NoInstallerEntryPoint=Error: Option [{0}] is not valid without --module or --main-jar entry point option + +ERR_MissingArgument=Error: Missing argument: {0} +ERR_MissingAppResources=Error: No application jars found +ERR_AppImageNotExist=Error: App image directory "{0}" does not exist +ERR_NoAddLauncherName=Error: --add-launcher option requires a name and a file path (--add-launcher =) +ERR_NoUniqueName=Error: --add-launcher = requires a unique name +ERR_NoJreInstallerName=Error: Jre Installers require a name parameter +ERR_InvalidAppName=Error: Invalid Application name: {0} +ERR_InvalidSLName=Error: Invalid Add Launcher name: {0} +ERR_LicenseFileNotExit=Error: Specified license file does not exist +ERR_BuildRootInvalid=Error: temp ({0}) must be non-existant or empty directory +ERR_InvalidOption=Error: Invalid Option: [{0}] +ERR_InvalidInstallerType=Error: Invalid or unsupported type: [{0}] +ERR_BothMainJarAndModule=Error: Cannot have both --main-jar and --module Options +ERR_NoEntryPoint=Error: creating application image requires --main-jar or --module Option +ERR_InputNotDirectory=Error: Input directory specified is not a directory: {0} +ERR_CannotReadInputDir=Error: No permission to read from input directory: {0} +ERR_CannotParseOptions=Error: Processing @filename option: {0} --- /dev/null 2019-11-18 21:01:19.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/resources/MainResources_zh_CN.properties 2019-11-18 21:01:16.692793500 -0500 @@ -0,0 +1,92 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +param.copyright.default=Copyright (C) {0,date,YYYY} +param.description.default=None +param.vendor.default=Unknown + +message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize). +message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize). +message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}). +message.using-custom-resource=Using custom package resource {0} (loaded from {1}). +message.creating-app-bundle=Creating app package: {0} in {1} +message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists +message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists +message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}: {1} does not exists +message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists +message.debug-working-directory=Kept working directory for debug: {0} +message.bundle-created=Succeeded in building {0} package +message.module-version=Using version "{0}" from module "{1}" as application version +message.module-class=Using class "{0}" from module "{1}" as application main class + +error.cannot-create-output-dir=Destination directory {0} cannot be created +error.cannot-write-to-output-dir=Destination directory {0} is not writable +error.root-exists=Error: Application destination directory {0} already exists +error.no-main-class-with-main-jar=A main class was not specified nor was one found in the jar {0} +error.no-main-class-with-main-jar.advice=Specify a main class or ensure that the jar {0} specifies one in the manifest +error.no-main-class=A main class was not specified nor was one found in the supplied application resources +error.no-main-class.advice=Please specify a application class or ensure that the appResources has a jar containing one in the manifest +error.main-jar-does-not-exist=The configured main jar does not exist {0} in the input directory +error.main-jar-does-not-exist.advice=The main jar must be specified relative to the input directory (not an absolute path), and must exist within that directory + +error.tool-not-found=Can not find {0}. Reason: {1} +error.tool-not-found.advice=Please install {0} +error.tool-old-version=Can not find {0} {1} or newer +error.tool-old-version.advice=Please install {0} {1} or newer +error.jlink.failed=jlink failed with: {0} + +warning.module.does.not.exist=Module [{0}] does not exist +warning.no.jdk.modules.found=Warning: No JDK Modules found + +MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a package +MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1} \n\ +Advice to fix: {2} +MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem: {1} +MSG_BundlerRuntimeException=Bundler {0} failed because of {1} +MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a package + +ERR_NoMainClass=Error: Main application class is missing +ERR_UnsupportedOption=Error: Option [{0}] is not valid on this platform +ERR_InvalidTypeOption=Error: Option [{0}] is not valid with type [{1}] +ERR_NoInstallerEntryPoint=Error: Option [{0}] is not valid without --module or --main-jar entry point option + +ERR_MissingArgument=Error: Missing argument: {0} +ERR_MissingAppResources=Error: No application jars found +ERR_AppImageNotExist=Error: App image directory "{0}" does not exist +ERR_NoAddLauncherName=Error: --add-launcher option requires a name and a file path (--add-launcher =) +ERR_NoUniqueName=Error: --add-launcher = requires a unique name +ERR_NoJreInstallerName=Error: Jre Installers require a name parameter +ERR_InvalidAppName=Error: Invalid Application name: {0} +ERR_InvalidSLName=Error: Invalid Add Launcher name: {0} +ERR_LicenseFileNotExit=Error: Specified license file does not exist +ERR_BuildRootInvalid=Error: temp ({0}) must be non-existant or empty directory +ERR_InvalidOption=Error: Invalid Option: [{0}] +ERR_InvalidInstallerType=Error: Invalid or unsupported type: [{0}] +ERR_BothMainJarAndModule=Error: Cannot have both --main-jar and --module Options +ERR_NoEntryPoint=Error: creating application image requires --main-jar or --module Option +ERR_InputNotDirectory=Error: Input directory specified is not a directory: {0} +ERR_CannotReadInputDir=Error: No permission to read from input directory: {0} +ERR_CannotParseOptions=Error: Processing @filename option: {0} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java 2019-11-18 21:01:30.733743000 -0500 +++ /dev/null 2019-11-18 21:01:32.000000000 -0500 @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal.resources; - -public class ResourceLocator { - -} --- /dev/null 2019-11-18 21:01:32.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/resources/ResourceLocator.java 2019-11-18 21:01:27.325800800 -0500 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal.resources; + +/* + * ResourceLocator + * This empty class is the only class in this package. Otherwise the + * package consists only of resources. ResourceLocator is needed in order + * to call getResourceAsStream() to get those resources. + */ + +public class ResourceLocator { + public ResourceLocator() { + } +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/main/CommandLine.java 2019-11-18 21:01:51.913929900 -0500 +++ /dev/null 2019-11-18 21:01:53.000000000 -0500 @@ -1,316 +0,0 @@ -/* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.main; - -import java.io.IOException; -import java.io.Reader; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Various utility methods for processing Java tool command line arguments. - * - *

This is NOT part of any supported API. - * If you write code that depends on this, you do so at your own risk. - * This code and its internal interfaces are subject to change or - * deletion without notice. - */ -public class CommandLine { - /** - * Process Win32-style command files for the specified command line - * arguments and return the resulting arguments. A command file argument - * is of the form '@file' where 'file' is the name of the file whose - * contents are to be parsed for additional arguments. The contents of - * the command file are parsed using StreamTokenizer and the original - * '@file' argument replaced with the resulting tokens. Recursive command - * files are not supported. The '@' character itself can be quoted with - * the sequence '@@'. - * @param args the arguments that may contain @files - * @return the arguments, with @files expanded - * @throws IOException if there is a problem reading any of the @files - */ - public static String[] parse(String[] args) throws IOException { - List newArgs = new ArrayList<>(); - appendParsedCommandArgs(newArgs, Arrays.asList(args)); - return newArgs.toArray(new String[newArgs.size()]); - } - - private static void appendParsedCommandArgs(List newArgs, List args) throws IOException { - for (String arg : args) { - if (arg.length() > 1 && arg.charAt(0) == '@') { - arg = arg.substring(1); - if (arg.charAt(0) == '@') { - newArgs.add(arg); - } else { - loadCmdFile(arg, newArgs); - } - } else { - newArgs.add(arg); - } - } - } - - /** - * Process the given environment variable and appends any Win32-style - * command files for the specified command line arguments and return - * the resulting arguments. A command file argument - * is of the form '@file' where 'file' is the name of the file whose - * contents are to be parsed for additional arguments. The contents of - * the command file are parsed using StreamTokenizer and the original - * '@file' argument replaced with the resulting tokens. Recursive command - * files are not supported. The '@' character itself can be quoted with - * the sequence '@@'. - * @param envVariable the env variable to process - * @param args the arguments that may contain @files - * @return the arguments, with environment variable's content and expansion of @files - * @throws IOException if there is a problem reading any of the @files - * @throws com.sun.tools.javac.main.CommandLine.UnmatchedQuote - */ - public static List parse(String envVariable, List args) - throws IOException, UnmatchedQuote { - - List inArgs = new ArrayList<>(); - appendParsedEnvVariables(inArgs, envVariable); - inArgs.addAll(args); - List newArgs = new ArrayList<>(); - appendParsedCommandArgs(newArgs, inArgs); - return newArgs; - } - - /** - * Process the given environment variable and appends any Win32-style - * command files for the specified command line arguments and return - * the resulting arguments. A command file argument - * is of the form '@file' where 'file' is the name of the file whose - * contents are to be parsed for additional arguments. The contents of - * the command file are parsed using StreamTokenizer and the original - * '@file' argument replaced with the resulting tokens. Recursive command - * files are not supported. The '@' character itself can be quoted with - * the sequence '@@'. - * @param envVariable the env variable to process - * @param args the arguments that may contain @files - * @return the arguments, with environment variable's content and expansion of @files - * @throws IOException if there is a problem reading any of the @files - * @throws com.sun.tools.javac.main.CommandLine.UnmatchedQuote - */ - public static String[] parse(String envVariable, String[] args) throws IOException, UnmatchedQuote { - List out = parse(envVariable, Arrays.asList(args)); - return out.toArray(new String[out.size()]); - } - - private static void loadCmdFile(String name, List args) throws IOException { - try (Reader r = Files.newBufferedReader(Paths.get(name), Charset.defaultCharset())) { - Tokenizer t = new Tokenizer(r); - String s; - while ((s = t.nextToken()) != null) { - args.add(s); - } - } - } - - public static class Tokenizer { - private final Reader in; - private int ch; - - public Tokenizer(Reader in) throws IOException { - this.in = in; - ch = in.read(); - } - - public String nextToken() throws IOException { - skipWhite(); - if (ch == -1) { - return null; - } - - StringBuilder sb = new StringBuilder(); - char quoteChar = 0; - - while (ch != -1) { - switch (ch) { - case ' ': - case '\t': - case '\f': - if (quoteChar == 0) { - return sb.toString(); - } - sb.append((char) ch); - break; - - case '\n': - case '\r': - return sb.toString(); - - case '\'': - case '"': - if (quoteChar == 0) { - quoteChar = (char) ch; - } else if (quoteChar == ch) { - quoteChar = 0; - } else { - sb.append((char) ch); - } - break; - - case '\\': - if (quoteChar != 0) { - ch = in.read(); - switch (ch) { - case '\n': - case '\r': - while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f') { - ch = in.read(); - } - continue; - - case 'n': - ch = '\n'; - break; - case 'r': - ch = '\r'; - break; - case 't': - ch = '\t'; - break; - case 'f': - ch = '\f'; - break; - } - } - sb.append((char) ch); - break; - - default: - sb.append((char) ch); - } - - ch = in.read(); - } - - return sb.toString(); - } - - void skipWhite() throws IOException { - while (ch != -1) { - switch (ch) { - case ' ': - case '\t': - case '\n': - case '\r': - case '\f': - break; - - case '#': - ch = in.read(); - while (ch != '\n' && ch != '\r' && ch != -1) { - ch = in.read(); - } - break; - - default: - return; - } - - ch = in.read(); - } - } - } - - @SuppressWarnings("fallthrough") - private static void appendParsedEnvVariables(List newArgs, String envVariable) - throws UnmatchedQuote { - - if (envVariable == null) { - return; - } - String in = System.getenv(envVariable); - if (in == null || in.trim().isEmpty()) { - return; - } - - final char NUL = (char)0; - final int len = in.length(); - - int pos = 0; - StringBuilder sb = new StringBuilder(); - char quote = NUL; - char ch; - - loop: - while (pos < len) { - ch = in.charAt(pos); - switch (ch) { - case '\"': case '\'': - if (quote == NUL) { - quote = ch; - } else if (quote == ch) { - quote = NUL; - } else { - sb.append(ch); - } - pos++; - break; - case '\f': case '\n': case '\r': case '\t': case ' ': - if (quote == NUL) { - newArgs.add(sb.toString()); - sb.setLength(0); - while (ch == '\f' || ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ') { - pos++; - if (pos >= len) { - break loop; - } - ch = in.charAt(pos); - } - break; - } - // fall through - default: - sb.append(ch); - pos++; - } - } - if (sb.length() != 0) { - newArgs.add(sb.toString()); - } - if (quote != NUL) { - throw new UnmatchedQuote(envVariable); - } - } - - public static class UnmatchedQuote extends Exception { - private static final long serialVersionUID = 0; - - public final String variableName; - - UnmatchedQuote(String variable) { - this.variableName = variable; - } - } -} --- /dev/null 2019-11-18 21:01:53.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/main/CommandLine.java 2019-11-18 21:01:48.413501100 -0500 @@ -0,0 +1,214 @@ +/* + * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.main; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.File; +import java.io.Reader; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * This file was originally a copy of CommandLine.java in + * com.sun.tools.javac.main. + * It should track changes made to that file. + */ + +/** + * Various utility methods for processing Java tool command line arguments. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +class CommandLine { + /** + * Process Win32-style command files for the specified command line + * arguments and return the resulting arguments. A command file argument + * is of the form '@file' where 'file' is the name of the file whose + * contents are to be parsed for additional arguments. The contents of + * the command file are parsed using StreamTokenizer and the original + * '@file' argument replaced with the resulting tokens. Recursive command + * files are not supported. The '@' character itself can be quoted with + * the sequence '@@'. + * @param args the arguments that may contain @files + * @return the arguments, with @files expanded + * @throws IOException if there is a problem reading any of the @files + */ + public static String[] parse(String[] args) throws IOException { + List newArgs = new ArrayList<>(); + appendParsedCommandArgs(newArgs, Arrays.asList(args)); + return newArgs.toArray(new String[newArgs.size()]); + } + + private static void appendParsedCommandArgs(List newArgs, + List args) throws IOException { + for (String arg : args) { + if (arg.length() > 1 && arg.charAt(0) == '@') { + arg = arg.substring(1); + if (arg.charAt(0) == '@') { + newArgs.add(arg); + } else { + loadCmdFile(arg, newArgs); + } + } else { + newArgs.add(arg); + } + } + } + + private static void loadCmdFile(String name, List args) + throws IOException { + if (!Files.isReadable(Path.of(name))) { + throw new FileNotFoundException(name); + } + try (Reader r = Files.newBufferedReader(Paths.get(name), + Charset.defaultCharset())) { + Tokenizer t = new Tokenizer(r); + String s; + while ((s = t.nextToken()) != null) { + args.add(s); + } + } + } + + public static class Tokenizer { + private final Reader in; + private int ch; + + public Tokenizer(Reader in) throws IOException { + this.in = in; + ch = in.read(); + } + + public String nextToken() throws IOException { + skipWhite(); + if (ch == -1) { + return null; + } + + StringBuilder sb = new StringBuilder(); + char quoteChar = 0; + + while (ch != -1) { + switch (ch) { + case ' ': + case '\t': + case '\f': + if (quoteChar == 0) { + return sb.toString(); + } + sb.append((char) ch); + break; + + case '\n': + case '\r': + return sb.toString(); + + case '\'': + case '"': + if (quoteChar == 0) { + quoteChar = (char) ch; + } else if (quoteChar == ch) { + quoteChar = 0; + } else { + sb.append((char) ch); + } + break; + + case '\\': + if (quoteChar != 0) { + ch = in.read(); + switch (ch) { + case '\n': + case '\r': + while (ch == ' ' || ch == '\n' + || ch == '\r' || ch == '\t' + || ch == '\f') { + ch = in.read(); + } + continue; + + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case 'f': + ch = '\f'; + break; + } + } + sb.append((char) ch); + break; + + default: + sb.append((char) ch); + } + + ch = in.read(); + } + + return sb.toString(); + } + + void skipWhite() throws IOException { + while (ch != -1) { + switch (ch) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\f': + break; + + case '#': + ch = in.read(); + while (ch != '\n' && ch != '\r' && ch != -1) { + ch = in.read(); + } + break; + + default: + return; + } + + ch = in.read(); + } + } + } +} --- /dev/null 2019-11-18 21:02:12.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/main/Main.java 2019-11-18 21:02:09.823342000 -0500 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.main; + +import jdk.incubator.jpackage.internal.Arguments; +import jdk.incubator.jpackage.internal.Log; +import jdk.incubator.jpackage.internal.CLIHelp; +import java.io.PrintWriter; +import java.util.ResourceBundle; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.text.MessageFormat; + +public class Main { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.MainResources"); + + /** + * main(String... args) + * This is the entry point for the jpackage tool. + * + * @param args command line arguments + */ + public static void main(String... args) throws Exception { + // Create logger with default system.out and system.err + Log.setLogger(null); + + int status = new jdk.incubator.jpackage.main.Main().execute(args); + System.exit(status); + } + + /** + * execute() - this is the entry point for the ToolProvider API. + * + * @param out output stream + * @param err error output stream + * @param args command line arguments + * @return an exit code. 0 means success, non-zero means an error occurred. + */ + public int execute(PrintWriter out, PrintWriter err, String... args) { + // Create logger with provided streams + Log.Logger logger = new Log.Logger(); + logger.setPrintWriter(out, err); + Log.setLogger(logger); + + return execute(args); + } + + private int execute(String... args) { + try { + String[] newArgs; + try { + newArgs = CommandLine.parse(args); + } catch (FileNotFoundException fnfe) { + Log.error(MessageFormat.format(I18N.getString( + "ERR_CannotParseOptions"), fnfe.getMessage())); + return 1; + } catch (IOException ioe) { + Log.error(ioe.getMessage()); + return 1; + } + + if (newArgs.length == 0) { + CLIHelp.showHelp(true); + } else if (hasHelp(newArgs)){ + if (hasVersion(newArgs)) { + Log.info(System.getProperty("java.version") + "\n"); + } + CLIHelp.showHelp(false); + } else if (hasVersion(newArgs)) { + Log.info(System.getProperty("java.version")); + } else { + Arguments arguments = new Arguments(newArgs); + if (!arguments.processArguments()) { + // processArguments() will log error message if failed. + return 1; + } + } + return 0; + } finally { + Log.flush(); + } + } + + private boolean hasHelp(String[] args) { + for (String a : args) { + if ("--help".equals(a) || "-h".equals(a)) { + return true; + } + } + return false; + } + + private boolean hasVersion(String[] args) { + for (String a : args) { + if ("--version".equals(a)) { + return true; + } + } + return false; + } + +} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java 2019-11-18 21:02:24.036490000 -0500 +++ /dev/null 2019-11-18 21:02:25.000000000 -0500 @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -@FunctionalInterface -interface ArgAction { - void execute(); -} --- /dev/null 2019-11-18 21:02:25.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/classes/module-info.java 2019-11-18 21:02:20.560057700 -0500 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Defines the Java Packaging tool, jpackage. + * + *

jpackage is a tool for generating self-contained application bundles. + * + * @moduleGraph + * @since 14 + */ + +module jdk.incubator.jpackage { + requires jdk.jlink; + + requires java.desktop; + + uses jdk.incubator.jpackage.internal.Bundler; + uses jdk.incubator.jpackage.internal.Bundlers; + + provides jdk.incubator.jpackage.internal.Bundlers with + jdk.incubator.jpackage.internal.BasicBundlers; + +/* + * while jpackage is incubating, do not provide ToolProvider directly, but + * instead export jdk.incubator.jpackage.ToolProviderFactory + * + * provides java.util.spi.ToolProvider + * with jdk.incubator.jpackage.internal.JPackageToolProvider; + */ + exports jdk.incubator.jpackage; + + +} --- old/src/jdk.jpackage/share/native/libapplauncher/FileAttributes.h 2019-11-18 21:02:44.589996500 -0500 +++ /dev/null 2019-11-18 21:02:45.000000000 -0500 @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef FILEATTRIBUTES_H -#define FILEATTRIBUTES_H - -#include "Platform.h" -#include "PlatformString.h" -#include "FileAttribute.h" - -#include - -class FileAttributes { -private: - TString FFileName; - bool FFollowLink; - std::vector FAttributes; - - bool WriteAttributes(); - bool ReadAttributes(); - bool Valid(const FileAttribute Value); - -public: - FileAttributes(const TString FileName, bool FollowLink = true); - - void Append(const FileAttribute Value); - bool Contains(const FileAttribute Value); - void Remove(const FileAttribute Value); -}; - -#endif // FILEATTRIBUTES_H - --- /dev/null 2019-11-18 21:02:46.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/FileAttributes.h 2019-11-18 21:02:41.179191200 -0500 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef FILEATTRIBUTES_H +#define FILEATTRIBUTES_H + +#include "Platform.h" +#include "PlatformString.h" +#include "FileAttribute.h" + +#include + +class FileAttributes { +private: + TString FFileName; + bool FFollowLink; + std::vector FAttributes; + + bool WriteAttributes(); + bool ReadAttributes(); + bool Valid(const FileAttribute Value); + +public: + FileAttributes(const TString FileName, bool FollowLink = true); + + void Append(const FileAttribute Value); + bool Contains(const FileAttribute Value); + void Remove(const FileAttribute Value); +}; + +#endif // FILEATTRIBUTES_H + --- old/src/jdk.jpackage/share/native/libapplauncher/FilePath.h 2019-11-18 21:02:55.011426200 -0500 +++ /dev/null 2019-11-18 21:02:56.000000000 -0500 @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef FILEPATH_H -#define FILEPATH_H - -#include "Platform.h" -#include "PlatformString.h" -#include "FileAttribute.h" - -#include - -class FileAttributes { -private: - TString FFileName; - bool FFollowLink; - std::vector FAttributes; - - bool WriteAttributes(); - bool ReadAttributes(); - bool Valid(const FileAttribute Value); - -public: - FileAttributes(const TString FileName, bool FollowLink = true); - - void Append(const FileAttribute Value); - bool Contains(const FileAttribute Value); - void Remove(const FileAttribute Value); -}; - -class FilePath { -private: - FilePath(void) {} - ~FilePath(void) {} - -public: - static bool FileExists(const TString FileName); - static bool DirectoryExists(const TString DirectoryName); - - static bool DeleteFile(const TString FileName); - static bool DeleteDirectory(const TString DirectoryName); - - static TString ExtractFilePath(TString Path); - static TString ExtractFileExt(TString Path); - static TString ExtractFileName(TString Path); - static TString ChangeFileExt(TString Path, TString Extension); - - static TString IncludeTrailingSeparator(const TString value); - static TString IncludeTrailingSeparator(const char* value); - static TString IncludeTrailingSeparator(const wchar_t* value); - static TString FixPathForPlatform(TString Path); - static TString FixPathSeparatorForPlatform(TString Path); - static TString PathSeparator(); - - static bool CreateDirectory(TString Path, bool ownerOnly); - static void ChangePermissions(TString FileName, bool ownerOnly); -}; - -#endif //FILEPATH_H --- /dev/null 2019-11-18 21:02:56.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/FilePath.h 2019-11-18 21:02:51.689158900 -0500 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef FILEPATH_H +#define FILEPATH_H + +#include "Platform.h" +#include "PlatformString.h" +#include "FileAttribute.h" + +#include + +class FileAttributes { +private: + TString FFileName; + bool FFollowLink; + std::vector FAttributes; + + bool WriteAttributes(); + bool ReadAttributes(); + bool Valid(const FileAttribute Value); + +public: + FileAttributes(const TString FileName, bool FollowLink = true); + + void Append(const FileAttribute Value); + bool Contains(const FileAttribute Value); + void Remove(const FileAttribute Value); +}; + +class FilePath { +private: + FilePath(void) {} + ~FilePath(void) {} + +public: + static bool FileExists(const TString FileName); + static bool DirectoryExists(const TString DirectoryName); + + static bool DeleteFile(const TString FileName); + static bool DeleteDirectory(const TString DirectoryName); + + static TString ExtractFilePath(TString Path); + static TString ExtractFileExt(TString Path); + static TString ExtractFileName(TString Path); + static TString ChangeFileExt(TString Path, TString Extension); + + static TString IncludeTrailingSeparator(const TString value); + static TString IncludeTrailingSeparator(const char* value); + static TString IncludeTrailingSeparator(const wchar_t* value); + static TString FixPathForPlatform(TString Path); + static TString FixPathSeparatorForPlatform(TString Path); + static TString PathSeparator(); + + static bool CreateDirectory(TString Path, bool ownerOnly); + static void ChangePermissions(TString FileName, bool ownerOnly); +}; + +#endif //FILEPATH_H --- old/src/jdk.jpackage/share/native/libapplauncher/Helpers.cpp 2019-11-18 21:03:05.468618100 -0500 +++ /dev/null 2019-11-18 21:03:07.000000000 -0500 @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Helpers.h" -#include "PlatformString.h" -#include "PropertyFile.h" - - -bool Helpers::SplitOptionIntoNameValue( - TString option, TString& Name, TString& Value) { - bool hasValue = false; - Name = _T(""); - Value = _T(""); - unsigned int index = 0; - - for (; index < option.length(); index++) { - TCHAR c = option[index]; - - switch (c) { - case '=': { - index++; - hasValue = true; - break; - } - - case '\\': { - if (index + 1 < option.length()) { - c = option[index + 1]; - - switch (c) { - case '\\': { - index++; - Name += '\\'; - break; - } - - case '=': { - index++; - Name += '='; - break; - } - } - - } - - continue; - } - - default: { - Name += c; - continue; - } - } - - break; - } - - if (hasValue) { - Value = option.substr(index, index - option.length()); - } - - return (option.length() > 0); -} - - -TString Helpers::ReplaceString(TString subject, const TString& search, - const TString& replace) { - size_t pos = 0; - while((pos = subject.find(search, pos)) != TString::npos) { - subject.replace(pos, search.length(), replace); - pos += replace.length(); - } - return subject; -} - -TString Helpers::ConvertIdToFilePath(TString Value) { - TString search; - search = '.'; - TString replace; - replace = '/'; - TString result = ReplaceString(Value, search, replace); - return result; -} - -TString Helpers::ConvertIdToJavaPath(TString Value) { - TString search; - search = '.'; - TString replace; - replace = '/'; - TString result = ReplaceString(Value, search, replace); - search = '\\'; - result = ReplaceString(result, search, replace); - return result; -} - -TString Helpers::ConvertJavaPathToId(TString Value) { - TString search; - search = '/'; - TString replace; - replace = '.'; - TString result = ReplaceString(Value, search, replace); - return result; -} - -OrderedMap - Helpers::GetJavaOptionsFromConfig(IPropertyContainer* config) { - OrderedMap result; - - for (unsigned int index = 0; index < config->GetCount(); index++) { - TString argname = - TString(_T("jvmarg.")) + PlatformString(index + 1).toString(); - TString argvalue; - - if (config->GetValue(argname, argvalue) == false) { - break; - } - else if (argvalue.empty() == false) { - TString name; - TString value; - if (Helpers::SplitOptionIntoNameValue(argvalue, name, value)) { - result.Append(name, value); - } - } - } - - return result; -} - -std::list Helpers::GetArgsFromConfig(IPropertyContainer* config) { - std::list result; - - for (unsigned int index = 0; index < config->GetCount(); index++) { - TString argname = TString(_T("arg.")) - + PlatformString(index + 1).toString(); - TString argvalue; - - if (config->GetValue(argname, argvalue) == false) { - break; - } - else if (argvalue.empty() == false) { - result.push_back((argvalue)); - } - } - - return result; -} - -std::list - Helpers::MapToNameValueList(OrderedMap Map) { - std::list result; - std::vector keys = Map.GetKeys(); - - for (OrderedMap::const_iterator iterator = Map.begin(); - iterator != Map.end(); iterator++) { - JPPair *item = *iterator; - TString key = item->first; - TString value = item->second; - - if (value.length() == 0) { - result.push_back(key); - } else { - result.push_back(key + _T('=') + value); - } - } - - return result; -} - -TString Helpers::NameValueToString(TString name, TString value) { - TString result; - - if (value.empty() == true) { - result = name; - } - else { - result = name + TString(_T("=")) + value; - } - - return result; -} - -std::list Helpers::StringToArray(TString Value) { - std::list result; - TString line; - - for (unsigned int index = 0; index < Value.length(); index++) { - TCHAR c = Value[index]; - - switch (c) { - case '\n': { - result.push_back(line); - line = _T(""); - break; - } - - case '\r': { - result.push_back(line); - line = _T(""); - - if (Value[index + 1] == '\n') - index++; - - break; - } - - default: { - line += c; - } - } - } - - // The buffer may not have ended with a Carriage Return/Line Feed. - if (line.length() > 0) { - result.push_back(line); - } - - return result; -} --- /dev/null 2019-11-18 21:03:07.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Helpers.cpp 2019-11-18 21:03:02.224314500 -0500 @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Helpers.h" +#include "PlatformString.h" +#include "PropertyFile.h" + + +bool Helpers::SplitOptionIntoNameValue( + TString option, TString& Name, TString& Value) { + bool hasValue = false; + Name = _T(""); + Value = _T(""); + unsigned int index = 0; + + for (; index < option.length(); index++) { + TCHAR c = option[index]; + + switch (c) { + case '=': { + index++; + hasValue = true; + break; + } + + case '\\': { + if (index + 1 < option.length()) { + c = option[index + 1]; + + switch (c) { + case '\\': { + index++; + Name += '\\'; + break; + } + + case '=': { + index++; + Name += '='; + break; + } + } + + } + + continue; + } + + default: { + Name += c; + continue; + } + } + + break; + } + + if (hasValue) { + Value = option.substr(index, index - option.length()); + } + + return (option.length() > 0); +} + + +TString Helpers::ReplaceString(TString subject, const TString& search, + const TString& replace) { + size_t pos = 0; + while((pos = subject.find(search, pos)) != TString::npos) { + subject.replace(pos, search.length(), replace); + pos += replace.length(); + } + return subject; +} + +TString Helpers::ConvertIdToFilePath(TString Value) { + TString search; + search = '.'; + TString replace; + replace = '/'; + TString result = ReplaceString(Value, search, replace); + return result; +} + +TString Helpers::ConvertIdToJavaPath(TString Value) { + TString search; + search = '.'; + TString replace; + replace = '/'; + TString result = ReplaceString(Value, search, replace); + search = '\\'; + result = ReplaceString(result, search, replace); + return result; +} + +TString Helpers::ConvertJavaPathToId(TString Value) { + TString search; + search = '/'; + TString replace; + replace = '.'; + TString result = ReplaceString(Value, search, replace); + return result; +} + +OrderedMap + Helpers::GetJavaOptionsFromConfig(IPropertyContainer* config) { + OrderedMap result; + + for (unsigned int index = 0; index < config->GetCount(); index++) { + TString argname = + TString(_T("jvmarg.")) + PlatformString(index + 1).toString(); + TString argvalue; + + if (config->GetValue(argname, argvalue) == false) { + break; + } + else if (argvalue.empty() == false) { + TString name; + TString value; + if (Helpers::SplitOptionIntoNameValue(argvalue, name, value)) { + result.Append(name, value); + } + } + } + + return result; +} + +std::list Helpers::GetArgsFromConfig(IPropertyContainer* config) { + std::list result; + + for (unsigned int index = 0; index < config->GetCount(); index++) { + TString argname = TString(_T("arg.")) + + PlatformString(index + 1).toString(); + TString argvalue; + + if (config->GetValue(argname, argvalue) == false) { + break; + } + else if (argvalue.empty() == false) { + result.push_back((argvalue)); + } + } + + return result; +} + +std::list + Helpers::MapToNameValueList(OrderedMap Map) { + std::list result; + std::vector keys = Map.GetKeys(); + + for (OrderedMap::const_iterator iterator = Map.begin(); + iterator != Map.end(); iterator++) { + JPPair *item = *iterator; + TString key = item->first; + TString value = item->second; + + if (value.length() == 0) { + result.push_back(key); + } else { + result.push_back(key + _T('=') + value); + } + } + + return result; +} + +TString Helpers::NameValueToString(TString name, TString value) { + TString result; + + if (value.empty() == true) { + result = name; + } + else { + result = name + TString(_T("=")) + value; + } + + return result; +} + +std::list Helpers::StringToArray(TString Value) { + std::list result; + TString line; + + for (unsigned int index = 0; index < Value.length(); index++) { + TCHAR c = Value[index]; + + switch (c) { + case '\n': { + result.push_back(line); + line = _T(""); + break; + } + + case '\r': { + result.push_back(line); + line = _T(""); + + if (Value[index + 1] == '\n') + index++; + + break; + } + + default: { + line += c; + } + } + } + + // The buffer may not have ended with a Carriage Return/Line Feed. + if (line.length() > 0) { + result.push_back(line); + } + + return result; +} --- old/src/jdk.jpackage/share/native/libapplauncher/Helpers.h 2019-11-18 21:03:16.321609400 -0500 +++ /dev/null 2019-11-18 21:03:17.000000000 -0500 @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef HELPERS_H -#define HELPERS_H - -#include "Platform.h" -#include "OrderedMap.h" -#include "IniFile.h" - - -class Helpers { -private: - Helpers(void) {} - ~Helpers(void) {} - -public: - // Supports two formats for option: - // Example 1: - // foo=bar - // - // Example 2: - // - static bool SplitOptionIntoNameValue(TString option, - TString& Name, TString& Value); - static TString ReplaceString(TString subject, const TString& search, - const TString& replace); - static TString ConvertIdToFilePath(TString Value); - static TString ConvertIdToJavaPath(TString Value); - static TString ConvertJavaPathToId(TString Value); - - static OrderedMap - GetJavaOptionsFromConfig(IPropertyContainer* config); - static std::list GetArgsFromConfig(IPropertyContainer* config); - - static std::list - MapToNameValueList(OrderedMap Map); - - static TString NameValueToString(TString name, TString value); - - static std::list StringToArray(TString Value); -}; - -#endif // HELPERS_H --- /dev/null 2019-11-18 21:03:17.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Helpers.h 2019-11-18 21:03:12.709647700 -0500 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef HELPERS_H +#define HELPERS_H + +#include "Platform.h" +#include "OrderedMap.h" +#include "IniFile.h" + + +class Helpers { +private: + Helpers(void) {} + ~Helpers(void) {} + +public: + // Supports two formats for option: + // Example 1: + // foo=bar + // + // Example 2: + // + static bool SplitOptionIntoNameValue(TString option, + TString& Name, TString& Value); + static TString ReplaceString(TString subject, const TString& search, + const TString& replace); + static TString ConvertIdToFilePath(TString Value); + static TString ConvertIdToJavaPath(TString Value); + static TString ConvertJavaPathToId(TString Value); + + static OrderedMap + GetJavaOptionsFromConfig(IPropertyContainer* config); + static std::list GetArgsFromConfig(IPropertyContainer* config); + + static std::list + MapToNameValueList(OrderedMap Map); + + static TString NameValueToString(TString name, TString value); + + static std::list StringToArray(TString Value); +}; + +#endif // HELPERS_H --- old/src/jdk.jpackage/share/native/libapplauncher/IniFile.cpp 2019-11-18 21:03:26.586977700 -0500 +++ /dev/null 2019-11-18 21:03:28.000000000 -0500 @@ -1,261 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "IniFile.h" -#include "Helpers.h" - -#include - - -IniFile::IniFile() : ISectionalPropertyContainer() { -} - -IniFile::~IniFile() { - for (OrderedMap::iterator iterator = - FMap.begin(); iterator != FMap.end(); iterator++) { - JPPair *item = *iterator; - delete item->second; - } -} - -bool IniFile::LoadFromFile(const TString FileName) { - bool result = false; - Platform& platform = Platform::GetInstance(); - - std::list contents = platform.LoadFromFile(FileName); - - if (contents.empty() == false) { - bool found = false; - - // Determine the if file is an INI file or property file. - // Assign FDefaultSection if it is - // an INI file. Otherwise FDefaultSection is NULL. - for (std::list::const_iterator iterator = contents.begin(); - iterator != contents.end(); iterator++) { - TString line = *iterator; - - if (line[0] == ';') { - // Semicolon is a comment so ignore the line. - continue; - } - else { - if (line[0] == '[') { - found = true; - } - - break; - } - } - - if (found == true) { - TString sectionName; - - for (std::list::const_iterator iterator = contents.begin(); - iterator != contents.end(); iterator++) { - TString line = *iterator; - - if (line[0] == ';') { - // Semicolon is a comment so ignore the line. - continue; - } - else if (line[0] == '[' && line[line.length() - 1] == ']') { - sectionName = line.substr(1, line.size() - 2); - } - else if (sectionName.empty() == false) { - TString name; - TString value; - - if (Helpers::SplitOptionIntoNameValue( - line, name, value) == true) { - Append(sectionName, name, value); - } - } - } - - result = true; - } - } - - return result; -} - -bool IniFile::SaveToFile(const TString FileName, bool ownerOnly) { - bool result = false; - - std::list contents; - std::vector keys = FMap.GetKeys(); - - for (unsigned int index = 0; index < keys.size(); index++) { - TString name = keys[index]; - IniSectionData *section; - - if (FMap.GetValue(name, section) == true) { - contents.push_back(_T("[") + name + _T("]")); - std::list lines = section->GetLines(); - contents.insert(contents.end(), lines.begin(), lines.end()); - contents.push_back(_T("")); - } - } - - Platform& platform = Platform::GetInstance(); - platform.SaveToFile(FileName, contents, ownerOnly); - result = true; - return result; -} - -void IniFile::Append(const TString SectionName, - const TString Key, TString Value) { - if (FMap.ContainsKey(SectionName) == true) { - IniSectionData* section; - - if (FMap.GetValue(SectionName, section) == true && section != NULL) { - section->SetValue(Key, Value); - } - } - else { - IniSectionData *section = new IniSectionData(); - section->SetValue(Key, Value); - FMap.Append(SectionName, section); - } -} - -void IniFile::AppendSection(const TString SectionName, - OrderedMap Values) { - if (FMap.ContainsKey(SectionName) == true) { - IniSectionData* section; - - if (FMap.GetValue(SectionName, section) == true && section != NULL) { - section->Append(Values); - } - } - else { - IniSectionData *section = new IniSectionData(Values); - FMap.Append(SectionName, section); - } -} - -bool IniFile::GetValue(const TString SectionName, - const TString Key, TString& Value) { - bool result = false; - IniSectionData* section; - - if (FMap.GetValue(SectionName, section) == true && section != NULL) { - result = section->GetValue(Key, Value); - } - - return result; -} - -bool IniFile::SetValue(const TString SectionName, - const TString Key, TString Value) { - bool result = false; - IniSectionData* section; - - if (FMap.GetValue(SectionName, section) && section != NULL) { - result = section->SetValue(Key, Value); - } - else { - Append(SectionName, Key, Value); - } - - - return result; -} - -bool IniFile::GetSection(const TString SectionName, - OrderedMap &Data) { - bool result = false; - - if (FMap.ContainsKey(SectionName) == true) { - IniSectionData* section; - - if (FMap.GetValue(SectionName, section) == true && section != NULL) { - OrderedMap data = section->GetData(); - Data.Append(data); - result = true; - } - } - - return result; -} - -bool IniFile::ContainsSection(const TString SectionName) { - return FMap.ContainsKey(SectionName); -} - -//---------------------------------------------------------------------------- - -IniSectionData::IniSectionData() { - FMap.SetAllowDuplicates(true); -} - -IniSectionData::IniSectionData(OrderedMap Values) { - FMap = Values; -} - -std::vector IniSectionData::GetKeys() { - return FMap.GetKeys(); -} - -std::list IniSectionData::GetLines() { - std::list result; - std::vector keys = FMap.GetKeys(); - - for (unsigned int index = 0; index < keys.size(); index++) { - TString name = keys[index]; - TString value; - - if (FMap.GetValue(name, value) == true) { - name = Helpers::ReplaceString(name, _T("="), _T("\\=")); - value = Helpers::ReplaceString(value, _T("="), _T("\\=")); - - TString line = name + _T('=') + value; - result.push_back(line); - } - } - - return result; -} - -OrderedMap IniSectionData::GetData() { - OrderedMap result = FMap; - return result; -} - -bool IniSectionData::GetValue(const TString Key, TString& Value) { - return FMap.GetValue(Key, Value); -} - -bool IniSectionData::SetValue(const TString Key, TString Value) { - return FMap.SetValue(Key, Value); -} - -void IniSectionData::Append(OrderedMap Values) { - FMap.Append(Values); -} - -size_t IniSectionData::GetCount() { - return FMap.Count(); -} --- /dev/null 2019-11-18 21:03:28.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/IniFile.cpp 2019-11-18 21:03:23.295045100 -0500 @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "IniFile.h" +#include "Helpers.h" + +#include + + +IniFile::IniFile() : ISectionalPropertyContainer() { +} + +IniFile::~IniFile() { + for (OrderedMap::iterator iterator = + FMap.begin(); iterator != FMap.end(); iterator++) { + JPPair *item = *iterator; + delete item->second; + } +} + +bool IniFile::LoadFromFile(const TString FileName) { + bool result = false; + Platform& platform = Platform::GetInstance(); + + std::list contents = platform.LoadFromFile(FileName); + + if (contents.empty() == false) { + bool found = false; + + // Determine the if file is an INI file or property file. + // Assign FDefaultSection if it is + // an INI file. Otherwise FDefaultSection is NULL. + for (std::list::const_iterator iterator = contents.begin(); + iterator != contents.end(); iterator++) { + TString line = *iterator; + + if (line[0] == ';') { + // Semicolon is a comment so ignore the line. + continue; + } + else { + if (line[0] == '[') { + found = true; + } + + break; + } + } + + if (found == true) { + TString sectionName; + + for (std::list::const_iterator iterator = contents.begin(); + iterator != contents.end(); iterator++) { + TString line = *iterator; + + if (line[0] == ';') { + // Semicolon is a comment so ignore the line. + continue; + } + else if (line[0] == '[' && line[line.length() - 1] == ']') { + sectionName = line.substr(1, line.size() - 2); + } + else if (sectionName.empty() == false) { + TString name; + TString value; + + if (Helpers::SplitOptionIntoNameValue( + line, name, value) == true) { + Append(sectionName, name, value); + } + } + } + + result = true; + } + } + + return result; +} + +bool IniFile::SaveToFile(const TString FileName, bool ownerOnly) { + bool result = false; + + std::list contents; + std::vector keys = FMap.GetKeys(); + + for (unsigned int index = 0; index < keys.size(); index++) { + TString name = keys[index]; + IniSectionData *section; + + if (FMap.GetValue(name, section) == true) { + contents.push_back(_T("[") + name + _T("]")); + std::list lines = section->GetLines(); + contents.insert(contents.end(), lines.begin(), lines.end()); + contents.push_back(_T("")); + } + } + + Platform& platform = Platform::GetInstance(); + platform.SaveToFile(FileName, contents, ownerOnly); + result = true; + return result; +} + +void IniFile::Append(const TString SectionName, + const TString Key, TString Value) { + if (FMap.ContainsKey(SectionName) == true) { + IniSectionData* section; + + if (FMap.GetValue(SectionName, section) == true && section != NULL) { + section->SetValue(Key, Value); + } + } + else { + IniSectionData *section = new IniSectionData(); + section->SetValue(Key, Value); + FMap.Append(SectionName, section); + } +} + +void IniFile::AppendSection(const TString SectionName, + OrderedMap Values) { + if (FMap.ContainsKey(SectionName) == true) { + IniSectionData* section; + + if (FMap.GetValue(SectionName, section) == true && section != NULL) { + section->Append(Values); + } + } + else { + IniSectionData *section = new IniSectionData(Values); + FMap.Append(SectionName, section); + } +} + +bool IniFile::GetValue(const TString SectionName, + const TString Key, TString& Value) { + bool result = false; + IniSectionData* section; + + if (FMap.GetValue(SectionName, section) == true && section != NULL) { + result = section->GetValue(Key, Value); + } + + return result; +} + +bool IniFile::SetValue(const TString SectionName, + const TString Key, TString Value) { + bool result = false; + IniSectionData* section; + + if (FMap.GetValue(SectionName, section) && section != NULL) { + result = section->SetValue(Key, Value); + } + else { + Append(SectionName, Key, Value); + } + + + return result; +} + +bool IniFile::GetSection(const TString SectionName, + OrderedMap &Data) { + bool result = false; + + if (FMap.ContainsKey(SectionName) == true) { + IniSectionData* section = NULL; + + if (FMap.GetValue(SectionName, section) == true && section != NULL) { + OrderedMap data = section->GetData(); + Data.Append(data); + result = true; + } + } + + return result; +} + +bool IniFile::ContainsSection(const TString SectionName) { + return FMap.ContainsKey(SectionName); +} + +//---------------------------------------------------------------------------- + +IniSectionData::IniSectionData() { + FMap.SetAllowDuplicates(true); +} + +IniSectionData::IniSectionData(OrderedMap Values) { + FMap = Values; +} + +std::vector IniSectionData::GetKeys() { + return FMap.GetKeys(); +} + +std::list IniSectionData::GetLines() { + std::list result; + std::vector keys = FMap.GetKeys(); + + for (unsigned int index = 0; index < keys.size(); index++) { + TString name = keys[index]; + TString value; + + if (FMap.GetValue(name, value) == true) { + name = Helpers::ReplaceString(name, _T("="), _T("\\=")); + value = Helpers::ReplaceString(value, _T("="), _T("\\=")); + + TString line = name + _T('=') + value; + result.push_back(line); + } + } + + return result; +} + +OrderedMap IniSectionData::GetData() { + OrderedMap result = FMap; + return result; +} + +bool IniSectionData::GetValue(const TString Key, TString& Value) { + return FMap.GetValue(Key, Value); +} + +bool IniSectionData::SetValue(const TString Key, TString Value) { + return FMap.SetValue(Key, Value); +} + +void IniSectionData::Append(OrderedMap Values) { + FMap.Append(Values); +} + +size_t IniSectionData::GetCount() { + return FMap.Count(); +} --- old/src/jdk.jpackage/share/native/libapplauncher/IniFile.h 2019-11-18 21:03:47.043256900 -0500 +++ /dev/null 2019-11-18 21:03:48.000000000 -0500 @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef INIFILE_H -#define INIFILE_H - -#include "Platform.h" -#include "OrderedMap.h" - -#include - - -class IniSectionData : public IPropertyContainer { -private: - OrderedMap FMap; - -public: - IniSectionData(); - IniSectionData(OrderedMap Values); - - std::vector GetKeys(); - std::list GetLines(); - OrderedMap GetData(); - - bool SetValue(const TString Key, TString Value); - void Append(OrderedMap Values); - - virtual bool GetValue(const TString Key, TString& Value); - virtual size_t GetCount(); -}; - - -class IniFile : public ISectionalPropertyContainer { -private: - OrderedMap FMap; - -public: - IniFile(); - virtual ~IniFile(); - - void internalTest(); - - bool LoadFromFile(const TString FileName); - bool SaveToFile(const TString FileName, bool ownerOnly = true); - - void Append(const TString SectionName, const TString Key, TString Value); - void AppendSection(const TString SectionName, - OrderedMap Values); - bool SetValue(const TString SectionName, - const TString Key, TString Value); - - // ISectionalPropertyContainer - virtual bool GetSection(const TString SectionName, - OrderedMap &Data); - virtual bool ContainsSection(const TString SectionName); - virtual bool GetValue(const TString SectionName, - const TString Key, TString& Value); -}; - -#endif // INIFILE_H --- /dev/null 2019-11-18 21:03:48.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/IniFile.h 2019-11-18 21:03:43.460338100 -0500 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef INIFILE_H +#define INIFILE_H + +#include "Platform.h" +#include "OrderedMap.h" + +#include + + +class IniSectionData : public IPropertyContainer { +private: + OrderedMap FMap; + +public: + IniSectionData(); + IniSectionData(OrderedMap Values); + + std::vector GetKeys(); + std::list GetLines(); + OrderedMap GetData(); + + bool SetValue(const TString Key, TString Value); + void Append(OrderedMap Values); + + virtual bool GetValue(const TString Key, TString& Value); + virtual size_t GetCount(); +}; + + +class IniFile : public ISectionalPropertyContainer { +private: + OrderedMap FMap; + +public: + IniFile(); + virtual ~IniFile(); + + void internalTest(); + + bool LoadFromFile(const TString FileName); + bool SaveToFile(const TString FileName, bool ownerOnly = true); + + void Append(const TString SectionName, const TString Key, TString Value); + void AppendSection(const TString SectionName, + OrderedMap Values); + bool SetValue(const TString SectionName, + const TString Key, TString Value); + + // ISectionalPropertyContainer + virtual bool GetSection(const TString SectionName, + OrderedMap &Data); + virtual bool ContainsSection(const TString SectionName); + virtual bool GetValue(const TString SectionName, + const TString Key, TString& Value); +}; + +#endif // INIFILE_H --- old/src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.cpp 2019-11-18 21:03:57.480099000 -0500 +++ /dev/null 2019-11-18 21:03:59.000000000 -0500 @@ -1,303 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "JavaVirtualMachine.h" -#include "Platform.h" -#include "PlatformString.h" -#include "FilePath.h" -#include "Package.h" -#include "Helpers.h" -#include "Messages.h" -#include "Macros.h" - -#include "jni.h" - -#include -#include -#include - - -bool RunVM() { - JavaVirtualMachine javavm; - - bool result = javavm.StartJVM(); - - if (!result) { - Platform& platform = Platform::GetInstance(); - platform.ShowMessage(_T("Failed to launch JVM\n")); - } - - return result; -} - -//---------------------------------------------------------------------------- - -JavaOptions::JavaOptions(): FOptions(NULL) { -} - -JavaOptions::~JavaOptions() { - if (FOptions != NULL) { - for (unsigned int index = 0; index < GetCount(); index++) { - delete[] FOptions[index].optionString; - } - - delete[] FOptions; - } -} - -void JavaOptions::AppendValue(const TString Key, TString Value, void* Extra) { - JavaOptionItem item; - item.name = Key; - item.value = Value; - item.extraInfo = Extra; - FItems.push_back(item); -} - -void JavaOptions::AppendValue(const TString Key, TString Value) { - AppendValue(Key, Value, NULL); -} - -void JavaOptions::AppendValue(const TString Key) { - AppendValue(Key, _T(""), NULL); -} - -void JavaOptions::AppendValues(OrderedMap Values) { - std::vector orderedKeys = Values.GetKeys(); - - for (std::vector::const_iterator iterator = orderedKeys.begin(); - iterator != orderedKeys.end(); iterator++) { - TString name = *iterator; - TString value; - - if (Values.GetValue(name, value) == true) { - AppendValue(name, value); - } - } -} - -void JavaOptions::ReplaceValue(const TString Key, TString Value) { - for (std::list::iterator iterator = FItems.begin(); - iterator != FItems.end(); iterator++) { - - TString lkey = iterator->name; - - if (lkey == Key) { - JavaOptionItem item = *iterator; - item.value = Value; - iterator = FItems.erase(iterator); - FItems.insert(iterator, item); - break; - } - } -} - -std::list JavaOptions::ToList() { - std::list result; - Macros& macros = Macros::GetInstance(); - - for (std::list::const_iterator iterator = FItems.begin(); - iterator != FItems.end(); iterator++) { - TString key = iterator->name; - TString value = iterator->value; - TString option = Helpers::NameValueToString(key, value); - option = macros.ExpandMacros(option); - result.push_back(option); - } - - return result; -} - -size_t JavaOptions::GetCount() { - return FItems.size(); -} - -//---------------------------------------------------------------------------- - -JavaVirtualMachine::JavaVirtualMachine() { -} - -JavaVirtualMachine::~JavaVirtualMachine(void) { -} - -bool JavaVirtualMachine::StartJVM() { - Platform& platform = Platform::GetInstance(); - Package& package = Package::GetInstance(); - - TString classpath = package.GetClassPath(); - TString modulepath = package.GetModulePath(); - JavaOptions options; - - if (modulepath.empty() == false) { - options.AppendValue(_T("-Djava.module.path"), modulepath); - } - - options.AppendValue(_T("-Djava.library.path"), - package.GetPackageAppDirectory() + FilePath::PathSeparator() - + package.GetPackageLauncherDirectory()); - options.AppendValue( - _T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); - options.AppendValues(package.GetJavaOptions()); - -#ifdef DEBUG - if (package.Debugging() == dsJava) { - options.AppendValue(_T("-Xdebug"), _T("")); - options.AppendValue( - _T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"), - _T("")); - platform.ShowMessage(_T("localhost:5005")); - } -#endif // DEBUG - - TString maxHeapSizeOption; - TString minHeapSizeOption; - - - if (package.GetMemoryState() == PackageBootFields::msAuto) { - TPlatformNumber memorySize = package.GetMemorySize(); - TString memory = - PlatformString((size_t)memorySize).toString() + _T("m"); - maxHeapSizeOption = TString(_T("-Xmx")) + memory; - options.AppendValue(maxHeapSizeOption, _T("")); - - if (memorySize > 256) - minHeapSizeOption = _T("-Xms256m"); - else - minHeapSizeOption = _T("-Xms") + memory; - - options.AppendValue(minHeapSizeOption, _T("")); - } - - TString mainClassName = package.GetMainClassName(); - TString mainModule = package.GetMainModule(); - - if (mainClassName.empty() == true && mainModule.empty() == true) { - Messages& messages = Messages::GetInstance(); - platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED)); - return false; - } - - configureLibrary(); - - // Initialize the arguments to JLI_Launch() - // - // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. - // This new thread simply re-runs main(argc, argv). Therefore we do not - // want to add new args if we are still in the original main thread so we - // will treat them as command line args provided by the user ... - // Only propagate original set of args first time. - - options.AppendValue(_T("-classpath")); - options.AppendValue(classpath); - - std::list vmargs; - vmargs.push_back(package.GetCommandName()); - - if (package.HasSplashScreen() == true) { - options.AppendValue(TString(_T("-splash:")) - + package.GetSplashScreenFileName(), _T("")); - } - - if (mainModule.empty() == true) { - options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), - _T("")); - } else { - options.AppendValue(_T("-m")); - options.AppendValue(mainModule); - } - - return launchVM(options, vmargs); -} - -void JavaVirtualMachine::configureLibrary() { - Platform& platform = Platform::GetInstance(); - Package& package = Package::GetInstance(); - TString libName = package.GetJavaLibraryFileName(); - platform.addPlatformDependencies(&javaLibrary); - javaLibrary.Load(libName); -} - -bool JavaVirtualMachine::launchVM(JavaOptions& options, - std::list& vmargs) { - Platform& platform = Platform::GetInstance(); - Package& package = Package::GetInstance(); - -#ifdef MAC - // Mac adds a ProcessSerialNumber to args when launched from .app - // filter out the psn since they it's not expected in the app - if (platform.IsMainThread() == false) { - std::list loptions = options.ToList(); - vmargs.splice(vmargs.end(), loptions, - loptions.begin(), loptions.end()); - } -#else - std::list loptions = options.ToList(); - vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); -#endif - - std::list largs = package.GetArgs(); - vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end()); - - size_t argc = vmargs.size(); - DynamicBuffer argv(argc + 1); - if (argv.GetData() == NULL) { - return false; - } - - unsigned int index = 0; - for (std::list::const_iterator iterator = vmargs.begin(); - iterator != vmargs.end(); iterator++) { - TString item = *iterator; - std::string arg = PlatformString(item).toStdString(); -#ifdef DEBUG - printf("%i %s\n", index, arg.c_str()); -#endif // DEBUG - argv[index] = PlatformString::duplicate(arg.c_str()); - index++; - } - - argv[argc] = NULL; - -// On Mac we can only free the boot fields if the calling thread is -// not the main thread. -#ifdef MAC - if (platform.IsMainThread() == false) { - package.FreeBootFields(); - } -#else - package.FreeBootFields(); -#endif // MAC - - if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) { - return true; - } - - for (index = 0; index < argc; index++) { - if (argv[index] != NULL) { - delete[] argv[index]; - } - } - - return false; -} --- /dev/null 2019-11-18 21:03:59.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/JavaVirtualMachine.cpp 2019-11-18 21:03:54.194016900 -0500 @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "JavaVirtualMachine.h" +#include "Platform.h" +#include "PlatformString.h" +#include "FilePath.h" +#include "Package.h" +#include "Helpers.h" +#include "Messages.h" +#include "Macros.h" + +#include "jni.h" + +#include +#include +#include + + +bool RunVM() { + JavaVirtualMachine javavm; + + bool result = javavm.StartJVM(); + + if (!result) { + Platform& platform = Platform::GetInstance(); + platform.ShowMessage(_T("Failed to launch JVM\n")); + } + + return result; +} + +//---------------------------------------------------------------------------- + +JavaOptions::JavaOptions(): FOptions(NULL) { +} + +JavaOptions::~JavaOptions() { + if (FOptions != NULL) { + for (unsigned int index = 0; index < GetCount(); index++) { + delete[] FOptions[index].optionString; + } + + delete[] FOptions; + } +} + +void JavaOptions::AppendValue(const TString Key, TString Value, void* Extra) { + JavaOptionItem item; + item.name = Key; + item.value = Value; + item.extraInfo = Extra; + FItems.push_back(item); +} + +void JavaOptions::AppendValue(const TString Key, TString Value) { + AppendValue(Key, Value, NULL); +} + +void JavaOptions::AppendValue(const TString Key) { + AppendValue(Key, _T(""), NULL); +} + +void JavaOptions::AppendValues(OrderedMap Values) { + if (Values.GetAllowDuplicates()) { + for (int i = 0; i < (int)Values.Count(); i++) { + TString name, value; + + bool bResult = Values.GetKey(i, name); + bResult &= Values.GetValue(i, value); + + if (bResult) { + AppendValue(name, value); + } + } + } else { // In case we asked to add values from OrderedMap with allow + // duplicates set to false. Not used now, but should avoid possible + // bugs. + std::vector orderedKeys = Values.GetKeys(); + + for (std::vector::const_iterator iterator = orderedKeys.begin(); + iterator != orderedKeys.end(); iterator++) { + TString name = *iterator; + TString value; + + if (Values.GetValue(name, value) == true) { + AppendValue(name, value); + } + } + } +} + +void JavaOptions::ReplaceValue(const TString Key, TString Value) { + for (std::list::iterator iterator = FItems.begin(); + iterator != FItems.end(); iterator++) { + + TString lkey = iterator->name; + + if (lkey == Key) { + JavaOptionItem item = *iterator; + item.value = Value; + iterator = FItems.erase(iterator); + FItems.insert(iterator, item); + break; + } + } +} + +std::list JavaOptions::ToList() { + std::list result; + Macros& macros = Macros::GetInstance(); + + for (std::list::const_iterator iterator = FItems.begin(); + iterator != FItems.end(); iterator++) { + TString key = iterator->name; + TString value = iterator->value; + TString option = Helpers::NameValueToString(key, value); + option = macros.ExpandMacros(option); + result.push_back(option); + } + + return result; +} + +size_t JavaOptions::GetCount() { + return FItems.size(); +} + +//---------------------------------------------------------------------------- + +JavaVirtualMachine::JavaVirtualMachine() { +} + +JavaVirtualMachine::~JavaVirtualMachine(void) { +} + +bool JavaVirtualMachine::StartJVM() { + Platform& platform = Platform::GetInstance(); + Package& package = Package::GetInstance(); + + TString classpath = package.GetClassPath(); + TString modulepath = package.GetModulePath(); + JavaOptions options; + + if (modulepath.empty() == false) { + options.AppendValue(_T("-Djava.module.path"), modulepath); + } + + options.AppendValue(_T("-Djava.library.path"), + package.GetPackageAppDirectory() + FilePath::PathSeparator() + + package.GetPackageLauncherDirectory()); + options.AppendValue( + _T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); + options.AppendValues(package.GetJavaOptions()); + +#ifdef DEBUG + if (package.Debugging() == dsJava) { + options.AppendValue(_T("-Xdebug"), _T("")); + options.AppendValue( + _T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"), + _T("")); + platform.ShowMessage(_T("localhost:5005")); + } +#endif // DEBUG + + TString maxHeapSizeOption; + TString minHeapSizeOption; + + + if (package.GetMemoryState() == PackageBootFields::msAuto) { + TPlatformNumber memorySize = package.GetMemorySize(); + TString memory = + PlatformString((size_t)memorySize).toString() + _T("m"); + maxHeapSizeOption = TString(_T("-Xmx")) + memory; + options.AppendValue(maxHeapSizeOption, _T("")); + + if (memorySize > 256) + minHeapSizeOption = _T("-Xms256m"); + else + minHeapSizeOption = _T("-Xms") + memory; + + options.AppendValue(minHeapSizeOption, _T("")); + } + + TString mainClassName = package.GetMainClassName(); + TString mainModule = package.GetMainModule(); + + if (mainClassName.empty() == true && mainModule.empty() == true) { + Messages& messages = Messages::GetInstance(); + platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED)); + return false; + } + + configureLibrary(); + + // Initialize the arguments to JLI_Launch() + // + // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. + // This new thread simply re-runs main(argc, argv). Therefore we do not + // want to add new args if we are still in the original main thread so we + // will treat them as command line args provided by the user ... + // Only propagate original set of args first time. + + options.AppendValue(_T("-classpath")); + options.AppendValue(classpath); + + std::list vmargs; + vmargs.push_back(package.GetCommandName()); + + if (package.HasSplashScreen() == true) { + options.AppendValue(TString(_T("-splash:")) + + package.GetSplashScreenFileName(), _T("")); + } + + if (mainModule.empty() == true) { + options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), + _T("")); + } else { + options.AppendValue(_T("-m")); + options.AppendValue(mainModule); + } + + return launchVM(options, vmargs); +} + +void JavaVirtualMachine::configureLibrary() { + Platform& platform = Platform::GetInstance(); + Package& package = Package::GetInstance(); + TString libName = package.GetJavaLibraryFileName(); + platform.addPlatformDependencies(&javaLibrary); + javaLibrary.Load(libName); +} + +bool JavaVirtualMachine::launchVM(JavaOptions& options, + std::list& vmargs) { + Platform& platform = Platform::GetInstance(); + Package& package = Package::GetInstance(); + +#ifdef MAC + // Mac adds a ProcessSerialNumber to args when launched from .app + // filter out the psn since they it's not expected in the app + if (platform.IsMainThread() == false) { + std::list loptions = options.ToList(); + vmargs.splice(vmargs.end(), loptions, + loptions.begin(), loptions.end()); + } +#else + std::list loptions = options.ToList(); + vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); +#endif + + std::list largs = package.GetArgs(); + vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end()); + + size_t argc = vmargs.size(); + DynamicBuffer argv(argc + 1); + if (argv.GetData() == NULL) { + return false; + } + + unsigned int index = 0; + for (std::list::const_iterator iterator = vmargs.begin(); + iterator != vmargs.end(); iterator++) { + TString item = *iterator; + std::string arg = PlatformString(item).toStdString(); +#ifdef DEBUG + printf("%i %s\n", index, arg.c_str()); +#endif // DEBUG + argv[index] = PlatformString::duplicate(arg.c_str()); + index++; + } + + argv[argc] = NULL; + +// On Mac we can only free the boot fields if the calling thread is +// not the main thread. +#ifdef MAC + if (platform.IsMainThread() == false) { + package.FreeBootFields(); + } +#else + package.FreeBootFields(); +#endif // MAC + + if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) { + return true; + } + + for (index = 0; index < argc; index++) { + if (argv[index] != NULL) { + delete[] argv[index]; + } + } + + return false; +} --- old/src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.h 2019-11-18 21:04:18.648461300 -0500 +++ /dev/null 2019-11-18 21:04:20.000000000 -0500 @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef JAVAVIRTUALMACHINE_H -#define JAVAVIRTUALMACHINE_H - - -#include "jni.h" -#include "Platform.h" -#include "Library.h" - -struct JavaOptionItem { - TString name; - TString value; - void* extraInfo; -}; - -class JavaOptions { -private: - std::list FItems; - JavaVMOption* FOptions; - -public: - JavaOptions(); - ~JavaOptions(); - - void AppendValue(const TString Key, TString Value, void* Extra); - void AppendValue(const TString Key, TString Value); - void AppendValue(const TString Key); - void AppendValues(OrderedMap Values); - void ReplaceValue(const TString Key, TString Value); - std::list ToList(); - size_t GetCount(); -}; - -class JavaVirtualMachine { -private: - JavaLibrary javaLibrary; - - void configureLibrary(); - bool launchVM(JavaOptions& options, std::list& vmargs); -public: - JavaVirtualMachine(); - ~JavaVirtualMachine(void); - - bool StartJVM(); -}; - -bool RunVM(); - -#endif // JAVAVIRTUALMACHINE_H --- /dev/null 2019-11-18 21:04:20.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/JavaVirtualMachine.h 2019-11-18 21:04:15.157593300 -0500 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef JAVAVIRTUALMACHINE_H +#define JAVAVIRTUALMACHINE_H + + +#include "jni.h" +#include "Platform.h" +#include "Library.h" + +struct JavaOptionItem { + TString name; + TString value; + void* extraInfo; +}; + +class JavaOptions { +private: + std::list FItems; + JavaVMOption* FOptions; + +public: + JavaOptions(); + ~JavaOptions(); + + void AppendValue(const TString Key, TString Value, void* Extra); + void AppendValue(const TString Key, TString Value); + void AppendValue(const TString Key); + void AppendValues(OrderedMap Values); + void ReplaceValue(const TString Key, TString Value); + std::list ToList(); + size_t GetCount(); +}; + +class JavaVirtualMachine { +private: + JavaLibrary javaLibrary; + + void configureLibrary(); + bool launchVM(JavaOptions& options, std::list& vmargs); +public: + JavaVirtualMachine(); + ~JavaVirtualMachine(void); + + bool StartJVM(); +}; + +bool RunVM(); + +#endif // JAVAVIRTUALMACHINE_H --- old/src/jdk.jpackage/share/native/libapplauncher/Library.cpp 2019-11-18 21:04:29.105589200 -0500 +++ /dev/null 2019-11-18 21:04:30.000000000 -0500 @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Library.h" -#include "Platform.h" -#include "Messages.h" -#include "PlatformString.h" - -#include -#include - -Library::Library() { - Initialize(); -} - -Library::Library(const TString &FileName) { - Initialize(); - Load(FileName); -} - -Library::~Library() { - Unload(); -} - -void Library::Initialize() { - FModule = NULL; - FDependentLibraryNames = NULL; - FDependenciesLibraries = NULL; -} - -void Library::InitializeDependencies() { - if (FDependentLibraryNames == NULL) { - FDependentLibraryNames = new std::vector(); - } - - if (FDependenciesLibraries == NULL) { - FDependenciesLibraries = new std::vector(); - } -} - -void Library::LoadDependencies() { - if (FDependentLibraryNames != NULL && FDependenciesLibraries != NULL) { - for (std::vector::const_iterator iterator = - FDependentLibraryNames->begin(); - iterator != FDependentLibraryNames->end(); iterator++) { - Library* library = new Library(); - - if (library->Load(*iterator) == true) { - FDependenciesLibraries->push_back(library); - } - } - - delete FDependentLibraryNames; - FDependentLibraryNames = NULL; - } -} - -void Library::UnloadDependencies() { - if (FDependenciesLibraries != NULL) { - for (std::vector::const_iterator iterator = - FDependenciesLibraries->begin(); - iterator != FDependenciesLibraries->end(); iterator++) { - Library* library = *iterator; - - if (library != NULL) { - library->Unload(); - delete library; - } - } - - delete FDependenciesLibraries; - FDependenciesLibraries = NULL; - } -} - -Procedure Library::GetProcAddress(const std::string& MethodName) const { - Platform& platform = Platform::GetInstance(); - return platform.GetProcAddress(FModule, MethodName); -} - -bool Library::Load(const TString &FileName) { - bool result = true; - - if (FModule == NULL) { - LoadDependencies(); - Platform& platform = Platform::GetInstance(); - FModule = platform.LoadLibrary(FileName); - - if (FModule == NULL) { - Messages& messages = Messages::GetInstance(); - platform.ShowMessage(messages.GetMessage(LIBRARY_NOT_FOUND), - FileName); - result = false; - } else { - fname = PlatformString(FileName).toStdString(); - } - } - - return result; -} - -bool Library::Unload() { - bool result = false; - - if (FModule != NULL) { - Platform& platform = Platform::GetInstance(); - platform.FreeLibrary(FModule); - FModule = NULL; - UnloadDependencies(); - result = true; - } - - return result; -} - -void Library::AddDependency(const TString &FileName) { - InitializeDependencies(); - - if (FDependentLibraryNames != NULL) { - FDependentLibraryNames->push_back(FileName); - } -} - -void Library::AddDependencies(const std::vector &Dependencies) { - if (Dependencies.size() > 0) { - InitializeDependencies(); - - if (FDependentLibraryNames != NULL) { - for (std::vector::const_iterator iterator = - FDependentLibraryNames->begin(); - iterator != FDependentLibraryNames->end(); iterator++) { - TString fileName = *iterator; - AddDependency(fileName); - } - } - } -} - -JavaLibrary::JavaLibrary() : Library(), FCreateProc(NULL) { -} - -bool JavaLibrary::JavaVMCreate(size_t argc, char *argv[]) { - if (FCreateProc == NULL) { - FCreateProc = (JAVA_CREATE) GetProcAddress(LAUNCH_FUNC); - } - - if (FCreateProc == NULL) { - Platform& platform = Platform::GetInstance(); - Messages& messages = Messages::GetInstance(); - platform.ShowMessage( - messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT)); - return false; - } - - return FCreateProc((int) argc, argv, - 0, NULL, - 0, NULL, - "", - "", - "java", - "java", - false, - false, - false, - 0) == 0; -} --- /dev/null 2019-11-18 21:04:30.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Library.cpp 2019-11-18 21:04:25.646344500 -0500 @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Library.h" +#include "Platform.h" +#include "Messages.h" +#include "PlatformString.h" + +#include +#include + +Library::Library() { + Initialize(); +} + +Library::Library(const TString &FileName) { + Initialize(); + Load(FileName); +} + +Library::~Library() { + Unload(); +} + +void Library::Initialize() { + FModule = NULL; + FDependentLibraryNames = NULL; + FDependenciesLibraries = NULL; +} + +void Library::InitializeDependencies() { + if (FDependentLibraryNames == NULL) { + FDependentLibraryNames = new std::vector(); + } + + if (FDependenciesLibraries == NULL) { + FDependenciesLibraries = new std::vector(); + } +} + +void Library::LoadDependencies() { + if (FDependentLibraryNames != NULL && FDependenciesLibraries != NULL) { + for (std::vector::const_iterator iterator = + FDependentLibraryNames->begin(); + iterator != FDependentLibraryNames->end(); iterator++) { + Library* library = new Library(); + + if (library->Load(*iterator) == true) { + FDependenciesLibraries->push_back(library); + } + } + + delete FDependentLibraryNames; + FDependentLibraryNames = NULL; + } +} + +void Library::UnloadDependencies() { + if (FDependenciesLibraries != NULL) { + for (std::vector::const_iterator iterator = + FDependenciesLibraries->begin(); + iterator != FDependenciesLibraries->end(); iterator++) { + Library* library = *iterator; + + if (library != NULL) { + library->Unload(); + delete library; + } + } + + delete FDependenciesLibraries; + FDependenciesLibraries = NULL; + } +} + +Procedure Library::GetProcAddress(const std::string& MethodName) const { + Platform& platform = Platform::GetInstance(); + return platform.GetProcAddress(FModule, MethodName); +} + +bool Library::Load(const TString &FileName) { + bool result = true; + + if (FModule == NULL) { + LoadDependencies(); + Platform& platform = Platform::GetInstance(); + FModule = platform.LoadLibrary(FileName); + + if (FModule == NULL) { + Messages& messages = Messages::GetInstance(); + platform.ShowMessage(messages.GetMessage(LIBRARY_NOT_FOUND), + FileName); + result = false; + } else { + fname = PlatformString(FileName).toStdString(); + } + } + + return result; +} + +bool Library::Unload() { + bool result = false; + + if (FModule != NULL) { + Platform& platform = Platform::GetInstance(); + platform.FreeLibrary(FModule); + FModule = NULL; + UnloadDependencies(); + result = true; + } + + return result; +} + +void Library::AddDependency(const TString &FileName) { + InitializeDependencies(); + + if (FDependentLibraryNames != NULL) { + FDependentLibraryNames->push_back(FileName); + } +} + +void Library::AddDependencies(const std::vector &Dependencies) { + if (Dependencies.size() > 0) { + InitializeDependencies(); + + if (FDependentLibraryNames != NULL) { + for (std::vector::const_iterator iterator = + FDependentLibraryNames->begin(); + iterator != FDependentLibraryNames->end(); iterator++) { + TString fileName = *iterator; + AddDependency(fileName); + } + } + } +} + +JavaLibrary::JavaLibrary() : Library(), FCreateProc(NULL) { +} + +bool JavaLibrary::JavaVMCreate(size_t argc, char *argv[]) { + if (FCreateProc == NULL) { + FCreateProc = (JAVA_CREATE) GetProcAddress(LAUNCH_FUNC); + } + + if (FCreateProc == NULL) { + Platform& platform = Platform::GetInstance(); + Messages& messages = Messages::GetInstance(); + platform.ShowMessage( + messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT)); + return false; + } + + return FCreateProc((int) argc, argv, + 0, NULL, + 0, NULL, + "", + "", + "java", + "java", + false, + false, + false, + 0) == 0; +} --- old/src/jdk.jpackage/share/native/libapplauncher/Library.h 2019-11-18 21:04:39.705125300 -0500 +++ /dev/null 2019-11-18 21:04:41.000000000 -0500 @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef LIBRARY_H -#define LIBRARY_H - -#include "PlatformDefs.h" -//#include "Platform.h" -#include "OrderedMap.h" - -#include "jni.h" -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -// Private typedef for function pointer casting -#define LAUNCH_FUNC "JLI_Launch" - -typedef int (JNICALL *JAVA_CREATE)(int argc, char ** argv, - int jargc, const char** jargv, - int appclassc, const char** appclassv, - const char* fullversion, - const char* dotversion, - const char* pname, - const char* lname, - jboolean javaargs, - jboolean cpwildcard, - jboolean javaw, - jint ergo); - -class Library { -private: - std::vector *FDependentLibraryNames; - std::vector *FDependenciesLibraries; - Module FModule; - std::string fname; - - void Initialize(); - void InitializeDependencies(); - void LoadDependencies(); - void UnloadDependencies(); - -public: - void* GetProcAddress(const std::string& MethodName) const; - -public: - Library(); - Library(const TString &FileName); - ~Library(); - - bool Load(const TString &FileName); - bool Unload(); - - const std::string& GetName() const { - return fname; - } - - void AddDependency(const TString &FileName); - void AddDependencies(const std::vector &Dependencies); -}; - -class JavaLibrary : public Library { - JAVA_CREATE FCreateProc; - JavaLibrary(const TString &FileName); -public: - JavaLibrary(); - bool JavaVMCreate(size_t argc, char *argv[]); -}; - -#endif // LIBRARY_H - --- /dev/null 2019-11-18 21:04:41.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Library.h 2019-11-18 21:04:36.186781500 -0500 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef LIBRARY_H +#define LIBRARY_H + +#include "PlatformDefs.h" +//#include "Platform.h" +#include "OrderedMap.h" + +#include "jni.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +// Private typedef for function pointer casting + +#if defined(_WIN32) && !defined(_WIN64) +#define LAUNCH_FUNC "_JLI_Launch@56" +#else +#define LAUNCH_FUNC "JLI_Launch" +#endif + + +typedef int (JNICALL *JAVA_CREATE)(int argc, char ** argv, + int jargc, const char** jargv, + int appclassc, const char** appclassv, + const char* fullversion, + const char* dotversion, + const char* pname, + const char* lname, + jboolean javaargs, + jboolean cpwildcard, + jboolean javaw, + jint ergo); + +class Library { +private: + std::vector *FDependentLibraryNames; + std::vector *FDependenciesLibraries; + Module FModule; + std::string fname; + + void Initialize(); + void InitializeDependencies(); + void LoadDependencies(); + void UnloadDependencies(); + +public: + void* GetProcAddress(const std::string& MethodName) const; + +public: + Library(); + Library(const TString &FileName); + ~Library(); + + bool Load(const TString &FileName); + bool Unload(); + + const std::string& GetName() const { + return fname; + } + + void AddDependency(const TString &FileName); + void AddDependencies(const std::vector &Dependencies); +}; + +class JavaLibrary : public Library { + JAVA_CREATE FCreateProc; + JavaLibrary(const TString &FileName); +public: + JavaLibrary(); + bool JavaVMCreate(size_t argc, char *argv[]); +}; + +#endif // LIBRARY_H + --- old/src/jdk.jpackage/share/native/libapplauncher/Macros.cpp 2019-11-18 21:04:59.779145800 -0500 +++ /dev/null 2019-11-18 21:05:01.000000000 -0500 @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Macros.h" -#include "Package.h" -#include "Helpers.h" - - -Macros::Macros(void) { -} - -Macros::~Macros(void) { -} - -void Macros::Initialize() { - Package& package = Package::GetInstance(); - Macros& macros = Macros::GetInstance(); - - // Public macros. - macros.AddMacro(_T("$APPDIR"), package.GetPackageRootDirectory()); - macros.AddMacro(_T("$PACKAGEDIR"), package.GetPackageAppDirectory()); - macros.AddMacro(_T("$LAUNCHERDIR"), package.GetPackageLauncherDirectory()); - macros.AddMacro(_T("$APPDATADIR"), package.GetAppDataDirectory()); - - TString javaHome = - FilePath::ExtractFilePath(package.GetJavaLibraryFileName()); - macros.AddMacro(_T("$JREHOME"), javaHome); - - // App CDS Macros - macros.AddMacro(_T("$CACHEDIR"), package.GetAppCDSCacheDirectory()); - - // Private macros. - TString javaVMLibraryName = FilePath::ExtractFileName(javaHome); - macros.AddMacro(_T("$JAVAVMLIBRARYNAME"), javaVMLibraryName); -} - -Macros& Macros::GetInstance() { - static Macros instance; - return instance; -} - -TString Macros::ExpandMacros(TString Value) { - TString result = Value; - - for (std::map::iterator iterator = FData.begin(); - iterator != FData.end(); - iterator++) { - - TString name = iterator->first; - - if (Value.find(name) != TString::npos) { - TString lvalue = iterator->second; - result = Helpers::ReplaceString(Value, name, lvalue); - result = ExpandMacros(result); - break; - } - } - - return result; -} - -void Macros::AddMacro(TString Key, TString Value) { - FData.insert(std::map::value_type(Key, Value)); -} --- /dev/null 2019-11-18 21:05:01.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Macros.cpp 2019-11-18 21:04:56.386618200 -0500 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Macros.h" +#include "Package.h" +#include "Helpers.h" + + +Macros::Macros(void) { +} + +Macros::~Macros(void) { +} + +void Macros::Initialize() { + Package& package = Package::GetInstance(); + Macros& macros = Macros::GetInstance(); + + // Public macros. + macros.AddMacro(_T("$ROOTDIR"), package.GetPackageRootDirectory()); + macros.AddMacro(_T("$APPDIR"), package.GetPackageAppDirectory()); + macros.AddMacro(_T("$BINDIR"), package.GetPackageLauncherDirectory()); +} + +Macros& Macros::GetInstance() { + static Macros instance; + return instance; +} + +TString Macros::ExpandMacros(TString Value) { + TString result = Value; + + for (std::map::iterator iterator = FData.begin(); + iterator != FData.end(); + iterator++) { + + TString name = iterator->first; + + if (Value.find(name) != TString::npos) { + TString lvalue = iterator->second; + result = Helpers::ReplaceString(Value, name, lvalue); + result = ExpandMacros(result); + break; + } + } + + return result; +} + +void Macros::AddMacro(TString Key, TString Value) { + FData.insert(std::map::value_type(Key, Value)); +} --- old/src/jdk.jpackage/share/native/libapplauncher/Macros.h 2019-11-18 21:05:20.178696100 -0500 +++ /dev/null 2019-11-18 21:05:21.000000000 -0500 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef MACROS_H -#define MACROS_H - -#include "Platform.h" - -#include - - -class Macros { -private: - std::map FData; - - Macros(void); - -public: - static Macros& GetInstance(); - static void Initialize(); - ~Macros(void); - - TString ExpandMacros(TString Value); - void AddMacro(TString Key, TString Value); -}; - -#endif // MACROS_H --- /dev/null 2019-11-18 21:05:21.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Macros.h 2019-11-18 21:05:16.775156100 -0500 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef MACROS_H +#define MACROS_H + +#include "Platform.h" + +#include + + +class Macros { +private: + std::map FData; + + Macros(void); + +public: + static Macros& GetInstance(); + static void Initialize(); + ~Macros(void); + + TString ExpandMacros(TString Value); + void AddMacro(TString Key, TString Value); +}; + +#endif // MACROS_H --- old/src/jdk.jpackage/share/native/libapplauncher/Messages.cpp 2019-11-18 21:05:30.535384700 -0500 +++ /dev/null 2019-11-18 21:05:31.000000000 -0500 @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Messages.h" -#include "Platform.h" -#include "FilePath.h" -#include "Helpers.h" -#include "Macros.h" -#include "JavaVirtualMachine.h" - -Messages::Messages(void) { - FMessages.SetReadOnly(false); - FMessages.SetValue(LIBRARY_NOT_FOUND, _T("Failed to find library.")); - FMessages.SetValue(FAILED_CREATING_JVM, _T("Failed to create JVM")); - FMessages.SetValue(FAILED_LOCATING_JVM_ENTRY_POINT, - _T("Failed to locate JLI_Launch")); - FMessages.SetValue(NO_MAIN_CLASS_SPECIFIED, _T("No main class specified")); - FMessages.SetValue(METHOD_NOT_FOUND, _T("No method %s in class %s.")); - FMessages.SetValue(CLASS_NOT_FOUND, _T("Class %s not found.")); - FMessages.SetValue(ERROR_INVOKING_METHOD, _T("Error invoking method.")); - FMessages.SetValue(APPCDS_CACHE_FILE_NOT_FOUND, - _T("Error: AppCDS cache does not exists:\n%s\n")); -} - -Messages& Messages::GetInstance() { - static Messages instance; - // Guaranteed to be destroyed. Instantiated on first use. - return instance; -} - -Messages::~Messages(void) { -} - -TString Messages::GetMessage(const TString Key) { - TString result; - FMessages.GetValue(Key, result); - Macros& macros = Macros::GetInstance(); - result = macros.ExpandMacros(result); - return result; -} --- /dev/null 2019-11-18 21:05:32.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Messages.cpp 2019-11-18 21:05:27.106377900 -0500 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Messages.h" +#include "Platform.h" +#include "FilePath.h" +#include "Helpers.h" +#include "Macros.h" +#include "JavaVirtualMachine.h" + +Messages::Messages(void) { + FMessages.SetReadOnly(false); + FMessages.SetValue(LIBRARY_NOT_FOUND, _T("Failed to find library.")); + FMessages.SetValue(FAILED_CREATING_JVM, _T("Failed to create JVM")); + FMessages.SetValue(FAILED_LOCATING_JVM_ENTRY_POINT, + _T("Failed to locate JLI_Launch")); + FMessages.SetValue(NO_MAIN_CLASS_SPECIFIED, _T("No main class specified")); + FMessages.SetValue(METHOD_NOT_FOUND, _T("No method %s in class %s.")); + FMessages.SetValue(CLASS_NOT_FOUND, _T("Class %s not found.")); + FMessages.SetValue(ERROR_INVOKING_METHOD, _T("Error invoking method.")); + FMessages.SetValue(APPCDS_CACHE_FILE_NOT_FOUND, + _T("Error: AppCDS cache does not exists:\n%s\n")); +} + +Messages& Messages::GetInstance() { + static Messages instance; + // Guaranteed to be destroyed. Instantiated on first use. + return instance; +} + +Messages::~Messages(void) { +} + +TString Messages::GetMessage(const TString Key) { + TString result; + FMessages.GetValue(Key, result); + Macros& macros = Macros::GetInstance(); + result = macros.ExpandMacros(result); + return result; +} --- old/src/jdk.jpackage/share/native/libapplauncher/Messages.h 2019-11-18 21:05:40.786782400 -0500 +++ /dev/null 2019-11-18 21:05:42.000000000 -0500 @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef MESSAGES_H -#define MESSAGES_H - -#include "PropertyFile.h" - -#define LIBRARY_NOT_FOUND _T("library.not.found") -#define FAILED_CREATING_JVM _T("failed.creating.jvm") -#define FAILED_LOCATING_JVM_ENTRY_POINT _T("failed.locating.jvm.entry.point") -#define NO_MAIN_CLASS_SPECIFIED _T("no.main.class.specified") - -#define METHOD_NOT_FOUND _T("method.not.found") -#define CLASS_NOT_FOUND _T("class.not.found") -#define ERROR_INVOKING_METHOD _T("error.invoking.method") - -#define CONFIG_FILE_NOT_FOUND _T("config.file.not.found") - -#define BUNDLED_JVM_NOT_FOUND _T("bundled.jvm.not.found") - -#define APPCDS_CACHE_FILE_NOT_FOUND _T("appcds.cache.file.not.found") - -class Messages { -private: - PropertyFile FMessages; - - Messages(void); -public: - static Messages& GetInstance(); - ~Messages(void); - - TString GetMessage(const TString Key); -}; - -#endif // MESSAGES_H --- /dev/null 2019-11-18 21:05:42.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Messages.h 2019-11-18 21:05:37.478969900 -0500 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef MESSAGES_H +#define MESSAGES_H + +#include "PropertyFile.h" + +#define LIBRARY_NOT_FOUND _T("library.not.found") +#define FAILED_CREATING_JVM _T("failed.creating.jvm") +#define FAILED_LOCATING_JVM_ENTRY_POINT _T("failed.locating.jvm.entry.point") +#define NO_MAIN_CLASS_SPECIFIED _T("no.main.class.specified") + +#define METHOD_NOT_FOUND _T("method.not.found") +#define CLASS_NOT_FOUND _T("class.not.found") +#define ERROR_INVOKING_METHOD _T("error.invoking.method") + +#define CONFIG_FILE_NOT_FOUND _T("config.file.not.found") + +#define BUNDLED_JVM_NOT_FOUND _T("bundled.jvm.not.found") + +#define APPCDS_CACHE_FILE_NOT_FOUND _T("appcds.cache.file.not.found") + +class Messages { +private: + PropertyFile FMessages; + + Messages(void); +public: + static Messages& GetInstance(); + ~Messages(void); + + TString GetMessage(const TString Key); +}; + +#endif // MESSAGES_H --- old/src/jdk.jpackage/share/native/libapplauncher/OrderedMap.h 2019-11-18 21:05:51.167223700 -0500 +++ /dev/null 2019-11-18 21:05:52.000000000 -0500 @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef ORDEREDMAP_H -#define ORDEREDMAP_H - -#include -#include -#include -#include - -#include - -template -struct JPPair -{ - typedef _T1 first_type; - typedef _T2 second_type; - - first_type first; - second_type second; - - JPPair(first_type Value1, second_type Value2) { - first = Value1; - second = Value2; - } -}; - - -template -class OrderedMap { -public: - typedef TKey key_type; - typedef TValue mapped_type; - typedef JPPair container_type; - typedef typename std::vector::iterator iterator; - typedef typename std::vector::const_iterator const_iterator; - -private: - typedef std::map map_type; - typedef std::vector list_type; - - map_type FMap; - list_type FList; - bool FAllowDuplicates; - - typename list_type::iterator FindListItem(const key_type Key) { - typename list_type::iterator result = FList.end(); - - for (typename list_type::iterator iterator = - FList.begin(); iterator != FList.end(); iterator++) { - container_type *item = *iterator; - - if (item->first == Key) { - result = iterator; - break; - } - } - - return result; - } - -public: - OrderedMap() { - FAllowDuplicates = false; - } - - OrderedMap(const OrderedMap &Value) { - Append(Value); - } - - ~OrderedMap() { - Clear(); - } - - void SetAllowDuplicates(bool Value) { - FAllowDuplicates = Value; - } - - iterator begin() { - return FList.begin(); - } - - const_iterator begin() const { - return FList.begin(); - } - - iterator end() { - return FList.end(); - } - - const_iterator end() const { - return FList.end(); - } - - void Clear() { - for (typename list_type::iterator iterator = - FList.begin(); iterator != FList.end(); iterator++) { - container_type *item = *iterator; - - if (item != NULL) { - delete item; - item = NULL; - } - } - - FMap.clear(); - FList.clear(); - } - - bool ContainsKey(key_type Key) { - bool result = false; - - if (FMap.find(Key) != FMap.end()) { - result = true; - } - - return result; - } - - std::vector GetKeys() { - std::vector result; - - for (typename list_type::const_iterator iterator = FList.begin(); - iterator != FList.end(); iterator++) { - container_type *item = *iterator; - result.push_back(item->first); - } - - return result; - } - - void Assign(const OrderedMap &Value) { - Clear(); - Append(Value); - } - - void Append(const OrderedMap &Value) { - for (size_t index = 0; index < Value.FList.size(); index++) { - container_type *item = Value.FList[index]; - Append(item->first, item->second); - } - } - - void Append(key_type Key, mapped_type Value) { - container_type *item = new container_type(Key, Value); - FMap.insert(std::pair(Key, item)); - FList.push_back(item); - } - - bool RemoveByKey(key_type Key) { - bool result = false; - typename list_type::iterator iterator = FindListItem(Key); - - if (iterator != FList.end()) { - FMap.erase(Key); - FList.erase(iterator); - result = true; - } - - return result; - } - - bool GetValue(key_type Key, mapped_type &Value) { - bool result = false; - container_type* item = FMap[Key]; - - if (item != NULL) { - Value = item->second; - result = true; - } - - return result; - } - - bool SetValue(key_type Key, mapped_type &Value) { - bool result = false; - - if ((FAllowDuplicates == false) && (ContainsKey(Key) == true)) { - container_type *item = FMap[Key]; - - if (item != NULL) { - item->second = Value; - result = true; - } - } - else { - Append(Key, Value); - result = true; - } - - return result; - } - - mapped_type &operator[](key_type Key) { - container_type* item = FMap[Key]; - assert(item != NULL); - - if (item != NULL) { - return item->second; - } - - throw std::invalid_argument("Key not found"); - } - - OrderedMap& operator= (OrderedMap &Value) { - Clear(); - Append(Value); - return *this; - } - - size_t Count() { - return FList.size(); - } -}; - -#endif // ORDEREDMAP_H --- /dev/null 2019-11-18 21:05:52.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/OrderedMap.h 2019-11-18 21:05:47.692444400 -0500 @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ORDEREDMAP_H +#define ORDEREDMAP_H + +#include +#include +#include +#include + +#include + +template +struct JPPair +{ + typedef _T1 first_type; + typedef _T2 second_type; + + first_type first; + second_type second; + + JPPair(first_type Value1, second_type Value2) { + first = Value1; + second = Value2; + } +}; + + +template +class OrderedMap { +public: + typedef TKey key_type; + typedef TValue mapped_type; + typedef JPPair container_type; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + +private: + typedef std::map map_type; + typedef std::vector list_type; + + map_type FMap; + list_type FList; + bool FAllowDuplicates; + + typename list_type::iterator FindListItem(const key_type Key) { + typename list_type::iterator result = FList.end(); + + for (typename list_type::iterator iterator = + FList.begin(); iterator != FList.end(); iterator++) { + container_type *item = *iterator; + + if (item->first == Key) { + result = iterator; + break; + } + } + + return result; + } + +public: + OrderedMap() { + FAllowDuplicates = false; + } + + OrderedMap(const OrderedMap &Value) { + Append(Value); + FAllowDuplicates = Value.GetAllowDuplicates(); + } + + ~OrderedMap() { + Clear(); + } + + void SetAllowDuplicates(bool Value) { + FAllowDuplicates = Value; + } + + bool GetAllowDuplicates() const { + return FAllowDuplicates; + } + + iterator begin() { + return FList.begin(); + } + + const_iterator begin() const { + return FList.begin(); + } + + iterator end() { + return FList.end(); + } + + const_iterator end() const { + return FList.end(); + } + + void Clear() { + for (typename list_type::iterator iterator = + FList.begin(); iterator != FList.end(); iterator++) { + container_type *item = *iterator; + + if (item != NULL) { + delete item; + item = NULL; + } + } + + FMap.clear(); + FList.clear(); + } + + bool ContainsKey(key_type Key) { + bool result = false; + + if (FMap.find(Key) != FMap.end()) { + result = true; + } + + return result; + } + + std::vector GetKeys() { + std::vector result; + + for (typename list_type::const_iterator iterator = FList.begin(); + iterator != FList.end(); iterator++) { + container_type *item = *iterator; + result.push_back(item->first); + } + + return result; + } + + void Assign(const OrderedMap &Value) { + Clear(); + Append(Value); + } + + void Append(const OrderedMap &Value) { + for (size_t index = 0; index < Value.FList.size(); index++) { + container_type *item = Value.FList[index]; + Append(item->first, item->second); + } + } + + void Append(key_type Key, mapped_type Value) { + container_type *item = new container_type(Key, Value); + FMap.insert(std::pair(Key, item)); + FList.push_back(item); + } + + bool RemoveByKey(key_type Key) { + bool result = false; + typename list_type::iterator iterator = FindListItem(Key); + + if (iterator != FList.end()) { + FMap.erase(Key); + FList.erase(iterator); + result = true; + } + + return result; + } + + bool GetValue(key_type Key, mapped_type &Value) { + bool result = false; + container_type* item = FMap[Key]; + + if (item != NULL) { + Value = item->second; + result = true; + } + + return result; + } + + bool SetValue(key_type Key, mapped_type &Value) { + bool result = false; + + if ((FAllowDuplicates == false) && (ContainsKey(Key) == true)) { + container_type *item = FMap[Key]; + + if (item != NULL) { + item->second = Value; + result = true; + } + } + else { + Append(Key, Value); + result = true; + } + + return result; + } + + bool GetKey(int index, key_type &Value) { + if (index < 0 || index >= (int)FList.size()) { + return false; + } + container_type *item = FList.at(index); + if (item != NULL) { + Value = item->first; + return true; + } + + return false; + } + + bool GetValue(int index, mapped_type &Value) { + if (index < 0 || index >= (int)FList.size()) { + return false; + } + container_type *item = FList.at(index); + if (item != NULL) { + Value = item->second; + return true; + } + + return false; + } + + mapped_type &operator[](key_type Key) { + container_type* item = FMap[Key]; + assert(item != NULL); + + if (item != NULL) { + return item->second; + } + + throw std::invalid_argument("Key not found"); + } + + OrderedMap& operator= (OrderedMap &Value) { + Clear(); + FAllowDuplicates = Value.GetAllowDuplicates(); + Append(Value); + return *this; + } + + size_t Count() { + return FList.size(); + } +}; + +#endif // ORDEREDMAP_H --- old/src/jdk.jpackage/share/native/libapplauncher/Package.cpp 2019-11-18 21:06:11.744638800 -0500 +++ /dev/null 2019-11-18 21:06:13.000000000 -0500 @@ -1,556 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Package.h" -#include "Helpers.h" -#include "Macros.h" -#include "IniFile.h" - -#include - - -Package::Package(void) { - FInitialized = false; - Initialize(); -} - -TPlatformNumber StringToPercentageOfNumber(TString Value, - TPlatformNumber Number) { - TPlatformNumber result = 0; - size_t percentage = atoi(PlatformString(Value.c_str())); - - if (percentage > 0 && Number > 0) { - result = Number * percentage / 100; - } - - return result; -} - -void Package::Initialize() { - if (FInitialized == true) { - return; - } - - Platform& platform = Platform::GetInstance(); - - FBootFields = new PackageBootFields(); - FDebugging = dsNone; - - FBootFields->FPackageRootDirectory = platform.GetPackageRootDirectory(); - FBootFields->FPackageAppDirectory = platform.GetPackageAppDirectory(); - FBootFields->FPackageLauncherDirectory = - platform.GetPackageLauncherDirectory(); - FBootFields->FAppDataDirectory = platform.GetAppDataDirectory(); - - std::map keys = platform.GetKeys(); - - // Read from configure.cfg/Info.plist - AutoFreePtr config = - platform.GetConfigFile(platform.GetConfigFileName()); - - config->GetValue(keys[CONFIG_SECTION_APPLICATION], - keys[JPACKAGE_APP_DATA_DIR], FBootFields->FPackageAppDataDirectory); - FBootFields->FPackageAppDataDirectory = - FilePath::FixPathForPlatform(FBootFields->FPackageAppDataDirectory); - - // Main JAR. - config->GetValue(keys[CONFIG_SECTION_APPLICATION], - keys[CONFIG_MAINJAR_KEY], FBootFields->FMainJar); - FBootFields->FMainJar = - FilePath::IncludeTrailingSeparator(GetPackageAppDirectory()) - + FilePath::FixPathForPlatform(FBootFields->FMainJar); - - // Main Module. - config->GetValue(keys[CONFIG_SECTION_APPLICATION], - keys[CONFIG_MAINMODULE_KEY], FBootFields->FMainModule); - - // Classpath. - // 1. If the provided class path contains main jar then only use - // provided class path. - // 2. If class path provided by config file is empty then add main jar. - // 3. If main jar is not in provided class path then add it. - config->GetValue(keys[CONFIG_SECTION_APPLICATION], - keys[CONFIG_CLASSPATH_KEY], FBootFields->FClassPath); - FBootFields->FClassPath = - FilePath::FixPathSeparatorForPlatform(FBootFields->FClassPath); - - if (FBootFields->FClassPath.empty() == true) { - FBootFields->FClassPath = GetMainJar(); - } else if (FBootFields->FClassPath.find(GetMainJar()) == TString::npos) { - FBootFields->FClassPath = GetMainJar() - + FilePath::PathSeparator() + FBootFields->FClassPath; - } - - // Modulepath. - config->GetValue(keys[CONFIG_SECTION_APPLICATION], - keys[CONFIG_MODULEPATH_KEY], FBootFields->FModulePath); - FBootFields->FModulePath = - FilePath::FixPathSeparatorForPlatform(FBootFields->FModulePath); - - // Main Class. - config->GetValue(keys[CONFIG_SECTION_APPLICATION], - keys[CONFIG_MAINCLASSNAME_KEY], FBootFields->FMainClassName); - - // Splash Screen. - if (config->GetValue(keys[CONFIG_SECTION_APPLICATION], - keys[CONFIG_SPLASH_KEY], - FBootFields->FSplashScreenFileName) == true) { - FBootFields->FSplashScreenFileName = - FilePath::IncludeTrailingSeparator(GetPackageAppDirectory()) - + FilePath::FixPathForPlatform(FBootFields->FSplashScreenFileName); - - if (FilePath::FileExists(FBootFields->FSplashScreenFileName) == false) { - FBootFields->FSplashScreenFileName = _T(""); - } - } - - // Runtime. - config->GetValue(keys[CONFIG_SECTION_APPLICATION], - keys[JAVA_RUNTIME_KEY], FBootFields->FJavaRuntimeDirectory); - - // Read jvmargs. - PromoteAppCDSState(config); - ReadJavaOptions(config); - - // Read args if none were passed in. - if (FBootFields->FArgs.size() == 0) { - OrderedMap args; - - if (config->GetSection(keys[CONFIG_SECTION_ARGOPTIONS], args) == true) { - FBootFields->FArgs = Helpers::MapToNameValueList(args); - } - } - - // Auto Memory. - TString autoMemory; - - if (config->GetValue(keys[CONFIG_SECTION_APPLICATION], - keys[CONFIG_APP_MEMORY], autoMemory) == true) { - if (autoMemory == _T("auto") || autoMemory == _T("100%")) { - FBootFields->FMemoryState = PackageBootFields::msAuto; - FBootFields->FMemorySize = platform.GetMemorySize(); - } else if (autoMemory.length() == 2 && isdigit(autoMemory[0]) && - autoMemory[1] == '%') { - FBootFields->FMemoryState = PackageBootFields::msAuto; - FBootFields->FMemorySize = - StringToPercentageOfNumber(autoMemory.substr(0, 1), - platform.GetMemorySize()); - } else if (autoMemory.length() == 3 && isdigit(autoMemory[0]) && - isdigit(autoMemory[1]) && autoMemory[2] == '%') { - FBootFields->FMemoryState = PackageBootFields::msAuto; - FBootFields->FMemorySize = - StringToPercentageOfNumber(autoMemory.substr(0, 2), - platform.GetMemorySize()); - } else { - FBootFields->FMemoryState = PackageBootFields::msManual; - FBootFields->FMemorySize = 0; - } - } - - // Debug - TString debug; - if (config->GetValue(keys[CONFIG_SECTION_APPLICATION], - keys[CONFIG_APP_DEBUG], debug) == true) { - FBootFields->FArgs.push_back(debug); - } -} - -void Package::Clear() { - FreeBootFields(); - FInitialized = false; -} - -// This is the only location that the AppCDS state should be modified except -// by command line arguments provided by the user. -// -// The state of AppCDS is as follows: -// -// -> cdsUninitialized -// -> cdsGenCache If -Xappcds:generatecache -// -> cdsDisabled If -Xappcds:off -// -> cdsEnabled If "AppCDSJavaOptions" section is present -// -> cdsAuto If "AppCDSJavaOptions" section is present and -// app.appcds.cache=auto -// -> cdsDisabled Default -// -void Package::PromoteAppCDSState(ISectionalPropertyContainer* Config) { - Platform& platform = Platform::GetInstance(); - std::map keys = platform.GetKeys(); - - // The AppCDS state can change at this point. - switch (platform.GetAppCDSState()) { - case cdsEnabled: - case cdsAuto: - case cdsDisabled: - case cdsGenCache: { - // Do nothing. - break; - } - - case cdsUninitialized: { - if (Config->ContainsSection( - keys[CONFIG_SECTION_APPCDSJAVAOPTIONS]) == true) { - // If the AppCDS section is present then enable AppCDS. - TString appCDSCacheValue; - - // If running with AppCDS enabled, and the configuration has - // been setup so "auto" is enabled, then - // the launcher will attempt to generate the cache file - // automatically and run the application. - if (Config->GetValue(keys[CONFIG_SECTION_APPLICATION], - _T("app.appcds.cache"), appCDSCacheValue) == true && - appCDSCacheValue == _T("auto")) { - platform.SetAppCDSState(cdsAuto); - } - else { - platform.SetAppCDSState(cdsEnabled); - } - } else { - - platform.SetAppCDSState(cdsDisabled); - } - } - } -} - -void Package::ReadJavaOptions(ISectionalPropertyContainer* Config) { - Platform& platform = Platform::GetInstance(); - std::map keys = platform.GetKeys(); - - // Evaluate based on the current AppCDS state. - switch (platform.GetAppCDSState()) { - case cdsUninitialized: { - throw Exception(_T("Internal Error")); - } - - case cdsDisabled: { - Config->GetSection(keys[CONFIG_SECTION_JAVAOPTIONS], - FBootFields->FJavaOptions); - break; - } - - case cdsGenCache: { - Config->GetSection(keys[ - CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS], - FBootFields->FJavaOptions); - break; - } - - case cdsAuto: - case cdsEnabled: { - if (Config->GetValue(keys[CONFIG_SECTION_APPCDSJAVAOPTIONS], - _T( "-XX:SharedArchiveFile"), - FBootFields->FAppCDSCacheFileName) == true) { - // File names may contain the incorrect path separators. - // The cache file name must be corrected at this point. - if (FBootFields->FAppCDSCacheFileName.empty() == false) { - IniFile* iniConfig = dynamic_cast(Config); - - if (iniConfig != NULL) { - FBootFields->FAppCDSCacheFileName = - FilePath::FixPathForPlatform( - FBootFields->FAppCDSCacheFileName); - iniConfig->SetValue(keys[ - CONFIG_SECTION_APPCDSJAVAOPTIONS], - _T( "-XX:SharedArchiveFile"), - FBootFields->FAppCDSCacheFileName); - } - } - - Config->GetSection(keys[CONFIG_SECTION_APPCDSJAVAOPTIONS], - FBootFields->FJavaOptions); - } - - break; - } - } -} - -void Package::SetCommandLineArguments(int argc, TCHAR* argv[]) { - if (argc > 0) { - std::list args; - - // Prepare app arguments. Skip value at index 0 - - // this is path to executable. - FBootFields->FCommandName = argv[0]; - - // Path to executable is at 0 index so start at index 1. - for (int index = 1; index < argc; index++) { - TString arg = argv[index]; - -#ifdef DEBUG - if (arg == _T("-debug")) { - FDebugging = dsNative; - } - - if (arg == _T("-javadebug")) { - FDebugging = dsJava; - } -#endif //DEBUG -#ifdef MAC - if (arg.find(_T("-psn_"), 0) != TString::npos) { - Platform& platform = Platform::GetInstance(); - - if (platform.IsMainThread() == true) { -#ifdef DEBUG - printf("%s\n", arg.c_str()); -#endif //DEBUG - continue; - } - } - - if (arg == _T("-NSDocumentRevisionsDebugMode")) { - // Ignore -NSDocumentRevisionsDebugMode and - // the following YES/NO - index++; - continue; - } -#endif //MAC - - args.push_back(arg); - } - - if (args.size() > 0) { - FBootFields->FArgs = args; - } - } -} - -Package& Package::GetInstance() { - static Package instance; - // Guaranteed to be destroyed. Instantiated on first use. - return instance; -} - -Package::~Package(void) { - FreeBootFields(); -} - -void Package::FreeBootFields() { - if (FBootFields != NULL) { - delete FBootFields; - FBootFields = NULL; - } -} - -OrderedMap Package::GetJavaOptions() { - return FBootFields->FJavaOptions; -} - -std::vector GetKeysThatAreNotDuplicates(OrderedMap &Defaults, OrderedMap &Overrides) { - std::vector result; - std::vector overrideKeys = Overrides.GetKeys(); - - for (size_t index = 0; index < overrideKeys.size(); index++) { - TString overridesKey = overrideKeys[index]; - TString overridesValue; - TString defaultValue; - - if ((Defaults.ContainsKey(overridesKey) == false) || - (Defaults.GetValue(overridesKey, defaultValue) == true && - Overrides.GetValue(overridesKey, overridesValue) == true && - defaultValue != overridesValue)) { - result.push_back(overridesKey); - } - } - - return result; -} - -OrderedMap CreateOrderedMapFromKeyList(OrderedMap &Map, std::vector &Keys) { - OrderedMap result; - - for (size_t index = 0; index < Keys.size(); index++) { - TString key = Keys[index]; - TString value; - - if (Map.GetValue(key, value) == true) { - result.Append(key, value); - } - } - - return result; -} - -std::vector GetKeysThatAreNotOverridesOfDefaultValues( - OrderedMap &Defaults, OrderedMap &Overrides) { - std::vector result; - std::vector keys = Overrides.GetKeys(); - - for (unsigned int index = 0; index< keys.size(); index++) { - TString key = keys[index]; - - if (Defaults.ContainsKey(key) == true) { - try { - TString value = Overrides[key]; - Defaults[key] = value; - } - catch (std::out_of_range &) { - } - } - else { - result.push_back(key); - } - } - - return result; -} - -std::list Package::GetArgs() { - assert(FBootFields != NULL); - return FBootFields->FArgs; -} - -TString Package::GetPackageRootDirectory() { - assert(FBootFields != NULL); - return FBootFields->FPackageRootDirectory; -} - -TString Package::GetPackageAppDirectory() { - assert(FBootFields != NULL); - return FBootFields->FPackageAppDirectory; -} - -TString Package::GetPackageLauncherDirectory() { - assert(FBootFields != NULL); - return FBootFields->FPackageLauncherDirectory; -} - -TString Package::GetAppDataDirectory() { - assert(FBootFields != NULL); - return FBootFields->FAppDataDirectory; -} - -TString Package::GetAppCDSCacheDirectory() { - if (FAppCDSCacheDirectory.empty()) { - Platform& platform = Platform::GetInstance(); - FAppCDSCacheDirectory = FilePath::IncludeTrailingSeparator( - platform.GetAppDataDirectory()) - + FilePath::IncludeTrailingSeparator( - GetPackageAppDataDirectory()) + _T("cache"); - - Macros& macros = Macros::GetInstance(); - FAppCDSCacheDirectory = macros.ExpandMacros(FAppCDSCacheDirectory); - FAppCDSCacheDirectory = - FilePath::FixPathForPlatform(FAppCDSCacheDirectory); - } - - return FAppCDSCacheDirectory; -} - -TString Package::GetAppCDSCacheFileName() { - assert(FBootFields != NULL); - - if (FBootFields->FAppCDSCacheFileName.empty() == false) { - Macros& macros = Macros::GetInstance(); - FBootFields->FAppCDSCacheFileName = - macros.ExpandMacros(FBootFields->FAppCDSCacheFileName); - FBootFields->FAppCDSCacheFileName = - FilePath::FixPathForPlatform(FBootFields->FAppCDSCacheFileName); - } - - return FBootFields->FAppCDSCacheFileName; -} - -TString Package::GetPackageAppDataDirectory() { - assert(FBootFields != NULL); - return FBootFields->FPackageAppDataDirectory; -} - -TString Package::GetClassPath() { - assert(FBootFields != NULL); - return FBootFields->FClassPath; -} - -TString Package::GetModulePath() { - assert(FBootFields != NULL); - return FBootFields->FModulePath; -} - -TString Package::GetMainJar() { - assert(FBootFields != NULL); - return FBootFields->FMainJar; -} - -TString Package::GetMainModule() { - assert(FBootFields != NULL); - return FBootFields->FMainModule; -} - -TString Package::GetMainClassName() { - assert(FBootFields != NULL); - return FBootFields->FMainClassName; -} - -TString Package::GetJavaLibraryFileName() { - assert(FBootFields != NULL); - - if (FBootFields->FJavaLibraryFileName.empty() == true) { - Platform& platform = Platform::GetInstance(); - Macros& macros = Macros::GetInstance(); - TString jvmRuntimePath = macros.ExpandMacros(GetJavaRuntimeDirectory()); - FBootFields->FJavaLibraryFileName = - platform.GetBundledJavaLibraryFileName(jvmRuntimePath); - } - - return FBootFields->FJavaLibraryFileName; -} - -TString Package::GetJavaRuntimeDirectory() { - assert(FBootFields != NULL); - return FBootFields->FJavaRuntimeDirectory; -} - -TString Package::GetSplashScreenFileName() { - assert(FBootFields != NULL); - return FBootFields->FSplashScreenFileName; -} - -bool Package::HasSplashScreen() { - assert(FBootFields != NULL); - return FilePath::FileExists(FBootFields->FSplashScreenFileName); -} - -TString Package::GetCommandName() { - assert(FBootFields != NULL); - return FBootFields->FCommandName; -} - -TPlatformNumber Package::GetMemorySize() { - assert(FBootFields != NULL); - return FBootFields->FMemorySize; -} - -PackageBootFields::MemoryState Package::GetMemoryState() { - assert(FBootFields != NULL); - return FBootFields->FMemoryState; -} - -DebugState Package::Debugging() { - return FDebugging; -} --- /dev/null 2019-11-18 21:06:13.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Package.cpp 2019-11-18 21:06:08.329223100 -0500 @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Package.h" +#include "Helpers.h" +#include "Macros.h" +#include "IniFile.h" + +#include + + +Package::Package(void) { + FInitialized = false; + Initialize(); +} + +TPlatformNumber StringToPercentageOfNumber(TString Value, + TPlatformNumber Number) { + TPlatformNumber result = 0; + size_t percentage = atoi(PlatformString(Value.c_str())); + + if (percentage > 0 && Number > 0) { + result = Number * percentage / 100; + } + + return result; +} + +void Package::Initialize() { + if (FInitialized == true) { + return; + } + + Platform& platform = Platform::GetInstance(); + + FBootFields = new PackageBootFields(); + FDebugging = dsNone; + + // Allow duplicates for Java options, so we can have multiple --add-exports + // or similar args. + FBootFields->FJavaOptions.SetAllowDuplicates(true); + FBootFields->FPackageRootDirectory = platform.GetPackageRootDirectory(); + FBootFields->FPackageAppDirectory = platform.GetPackageAppDirectory(); + FBootFields->FPackageLauncherDirectory = + platform.GetPackageLauncherDirectory(); + FBootFields->FAppDataDirectory = platform.GetAppDataDirectory(); + + std::map keys = platform.GetKeys(); + + // Read from configure.cfg/Info.plist + AutoFreePtr config = + platform.GetConfigFile(platform.GetConfigFileName()); + + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[JPACKAGE_APP_DATA_DIR], FBootFields->FPackageAppDataDirectory); + FBootFields->FPackageAppDataDirectory = + FilePath::FixPathForPlatform(FBootFields->FPackageAppDataDirectory); + + // Main JAR. + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_MAINJAR_KEY], FBootFields->FMainJar); + FBootFields->FMainJar = FilePath::FixPathForPlatform(FBootFields->FMainJar); + + // Main Module. + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_MAINMODULE_KEY], FBootFields->FMainModule); + + // Classpath. + // 1. If the provided class path contains main jar then only use + // provided class path. + // 2. If class path provided by config file is empty then add main jar. + // 3. If main jar is not in provided class path then add it. + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_CLASSPATH_KEY], FBootFields->FClassPath); + FBootFields->FClassPath = + FilePath::FixPathSeparatorForPlatform(FBootFields->FClassPath); + + if (FBootFields->FClassPath.empty() == true) { + FBootFields->FClassPath = GetMainJar(); + } else if (FBootFields->FClassPath.find(GetMainJar()) == TString::npos) { + FBootFields->FClassPath = GetMainJar() + + FilePath::PathSeparator() + FBootFields->FClassPath; + } + + // Modulepath. + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_MODULEPATH_KEY], FBootFields->FModulePath); + FBootFields->FModulePath = + FilePath::FixPathSeparatorForPlatform(FBootFields->FModulePath); + + // Main Class. + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_MAINCLASSNAME_KEY], FBootFields->FMainClassName); + + // Splash Screen. + if (config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_SPLASH_KEY], + FBootFields->FSplashScreenFileName) == true) { + FBootFields->FSplashScreenFileName = + FilePath::IncludeTrailingSeparator(GetPackageAppDirectory()) + + FilePath::FixPathForPlatform(FBootFields->FSplashScreenFileName); + + if (FilePath::FileExists(FBootFields->FSplashScreenFileName) == false) { + FBootFields->FSplashScreenFileName = _T(""); + } + } + + // Runtime. + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[JAVA_RUNTIME_KEY], FBootFields->FJavaRuntimeDirectory); + + // Read jvmargs. + PromoteAppCDSState(config); + ReadJavaOptions(config); + + // Read args if none were passed in. + if (FBootFields->FArgs.size() == 0) { + OrderedMap args; + + if (config->GetSection(keys[CONFIG_SECTION_ARGOPTIONS], args) == true) { + FBootFields->FArgs = Helpers::MapToNameValueList(args); + } + } + + // Auto Memory. + TString autoMemory; + + if (config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_APP_MEMORY], autoMemory) == true) { + if (autoMemory == _T("auto") || autoMemory == _T("100%")) { + FBootFields->FMemoryState = PackageBootFields::msAuto; + FBootFields->FMemorySize = platform.GetMemorySize(); + } else if (autoMemory.length() == 2 && isdigit(autoMemory[0]) && + autoMemory[1] == '%') { + FBootFields->FMemoryState = PackageBootFields::msAuto; + FBootFields->FMemorySize = + StringToPercentageOfNumber(autoMemory.substr(0, 1), + platform.GetMemorySize()); + } else if (autoMemory.length() == 3 && isdigit(autoMemory[0]) && + isdigit(autoMemory[1]) && autoMemory[2] == '%') { + FBootFields->FMemoryState = PackageBootFields::msAuto; + FBootFields->FMemorySize = + StringToPercentageOfNumber(autoMemory.substr(0, 2), + platform.GetMemorySize()); + } else { + FBootFields->FMemoryState = PackageBootFields::msManual; + FBootFields->FMemorySize = 0; + } + } + + // Debug + TString debug; + if (config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_APP_DEBUG], debug) == true) { + FBootFields->FArgs.push_back(debug); + } +} + +void Package::Clear() { + FreeBootFields(); + FInitialized = false; +} + +// This is the only location that the AppCDS state should be modified except +// by command line arguments provided by the user. +// +// The state of AppCDS is as follows: +// +// -> cdsUninitialized +// -> cdsGenCache If -Xappcds:generatecache +// -> cdsDisabled If -Xappcds:off +// -> cdsEnabled If "AppCDSJavaOptions" section is present +// -> cdsAuto If "AppCDSJavaOptions" section is present and +// app.appcds.cache=auto +// -> cdsDisabled Default +// +void Package::PromoteAppCDSState(ISectionalPropertyContainer* Config) { + Platform& platform = Platform::GetInstance(); + std::map keys = platform.GetKeys(); + + // The AppCDS state can change at this point. + switch (platform.GetAppCDSState()) { + case cdsEnabled: + case cdsAuto: + case cdsDisabled: + case cdsGenCache: { + // Do nothing. + break; + } + + case cdsUninitialized: { + if (Config->ContainsSection( + keys[CONFIG_SECTION_APPCDSJAVAOPTIONS]) == true) { + // If the AppCDS section is present then enable AppCDS. + TString appCDSCacheValue; + + // If running with AppCDS enabled, and the configuration has + // been setup so "auto" is enabled, then + // the launcher will attempt to generate the cache file + // automatically and run the application. + if (Config->GetValue(keys[CONFIG_SECTION_APPLICATION], + _T("app.appcds.cache"), appCDSCacheValue) == true && + appCDSCacheValue == _T("auto")) { + platform.SetAppCDSState(cdsAuto); + } + else { + platform.SetAppCDSState(cdsEnabled); + } + } else { + + platform.SetAppCDSState(cdsDisabled); + } + } + } +} + +void Package::ReadJavaOptions(ISectionalPropertyContainer* Config) { + Platform& platform = Platform::GetInstance(); + std::map keys = platform.GetKeys(); + + // Evaluate based on the current AppCDS state. + switch (platform.GetAppCDSState()) { + case cdsUninitialized: { + throw Exception(_T("Internal Error")); + } + + case cdsDisabled: { + Config->GetSection(keys[CONFIG_SECTION_JAVAOPTIONS], + FBootFields->FJavaOptions); + break; + } + + case cdsGenCache: { + Config->GetSection(keys[ + CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS], + FBootFields->FJavaOptions); + break; + } + + case cdsAuto: + case cdsEnabled: { + if (Config->GetValue(keys[CONFIG_SECTION_APPCDSJAVAOPTIONS], + _T( "-XX:SharedArchiveFile"), + FBootFields->FAppCDSCacheFileName) == true) { + // File names may contain the incorrect path separators. + // The cache file name must be corrected at this point. + if (FBootFields->FAppCDSCacheFileName.empty() == false) { + IniFile* iniConfig = dynamic_cast(Config); + + if (iniConfig != NULL) { + FBootFields->FAppCDSCacheFileName = + FilePath::FixPathForPlatform( + FBootFields->FAppCDSCacheFileName); + iniConfig->SetValue(keys[ + CONFIG_SECTION_APPCDSJAVAOPTIONS], + _T( "-XX:SharedArchiveFile"), + FBootFields->FAppCDSCacheFileName); + } + } + + Config->GetSection(keys[CONFIG_SECTION_APPCDSJAVAOPTIONS], + FBootFields->FJavaOptions); + } + + break; + } + } +} + +void Package::SetCommandLineArguments(int argc, TCHAR* argv[]) { + if (argc > 0) { + std::list args; + + // Prepare app arguments. Skip value at index 0 - + // this is path to executable. + FBootFields->FCommandName = argv[0]; + + // Path to executable is at 0 index so start at index 1. + for (int index = 1; index < argc; index++) { + TString arg = argv[index]; + +#ifdef DEBUG + if (arg == _T("-debug")) { + FDebugging = dsNative; + } + + if (arg == _T("-javadebug")) { + FDebugging = dsJava; + } +#endif //DEBUG +#ifdef MAC + if (arg.find(_T("-psn_"), 0) != TString::npos) { + Platform& platform = Platform::GetInstance(); + + if (platform.IsMainThread() == true) { +#ifdef DEBUG + printf("%s\n", arg.c_str()); +#endif //DEBUG + continue; + } + } + + if (arg == _T("-NSDocumentRevisionsDebugMode")) { + // Ignore -NSDocumentRevisionsDebugMode and + // the following YES/NO + index++; + continue; + } +#endif //MAC + + args.push_back(arg); + } + + if (args.size() > 0) { + FBootFields->FArgs = args; + } + } +} + +Package& Package::GetInstance() { + static Package instance; + // Guaranteed to be destroyed. Instantiated on first use. + return instance; +} + +Package::~Package(void) { + FreeBootFields(); +} + +void Package::FreeBootFields() { + if (FBootFields != NULL) { + delete FBootFields; + FBootFields = NULL; + } +} + +OrderedMap Package::GetJavaOptions() { + return FBootFields->FJavaOptions; +} + +std::vector GetKeysThatAreNotDuplicates(OrderedMap &Defaults, OrderedMap &Overrides) { + std::vector result; + std::vector overrideKeys = Overrides.GetKeys(); + + for (size_t index = 0; index < overrideKeys.size(); index++) { + TString overridesKey = overrideKeys[index]; + TString overridesValue; + TString defaultValue; + + if ((Defaults.ContainsKey(overridesKey) == false) || + (Defaults.GetValue(overridesKey, defaultValue) == true && + Overrides.GetValue(overridesKey, overridesValue) == true && + defaultValue != overridesValue)) { + result.push_back(overridesKey); + } + } + + return result; +} + +OrderedMap CreateOrderedMapFromKeyList(OrderedMap &Map, std::vector &Keys) { + OrderedMap result; + + for (size_t index = 0; index < Keys.size(); index++) { + TString key = Keys[index]; + TString value; + + if (Map.GetValue(key, value) == true) { + result.Append(key, value); + } + } + + return result; +} + +std::vector GetKeysThatAreNotOverridesOfDefaultValues( + OrderedMap &Defaults, OrderedMap &Overrides) { + std::vector result; + std::vector keys = Overrides.GetKeys(); + + for (unsigned int index = 0; index< keys.size(); index++) { + TString key = keys[index]; + + if (Defaults.ContainsKey(key) == true) { + try { + TString value = Overrides[key]; + Defaults[key] = value; + } + catch (std::out_of_range &) { + } + } + else { + result.push_back(key); + } + } + + return result; +} + +std::list Package::GetArgs() { + assert(FBootFields != NULL); + return FBootFields->FArgs; +} + +TString Package::GetPackageRootDirectory() { + assert(FBootFields != NULL); + return FBootFields->FPackageRootDirectory; +} + +TString Package::GetPackageAppDirectory() { + assert(FBootFields != NULL); + return FBootFields->FPackageAppDirectory; +} + +TString Package::GetPackageLauncherDirectory() { + assert(FBootFields != NULL); + return FBootFields->FPackageLauncherDirectory; +} + +TString Package::GetAppDataDirectory() { + assert(FBootFields != NULL); + return FBootFields->FAppDataDirectory; +} + +TString Package::GetAppCDSCacheDirectory() { + if (FAppCDSCacheDirectory.empty()) { + Platform& platform = Platform::GetInstance(); + FAppCDSCacheDirectory = FilePath::IncludeTrailingSeparator( + platform.GetAppDataDirectory()) + + FilePath::IncludeTrailingSeparator( + GetPackageAppDataDirectory()) + _T("cache"); + + Macros& macros = Macros::GetInstance(); + FAppCDSCacheDirectory = macros.ExpandMacros(FAppCDSCacheDirectory); + FAppCDSCacheDirectory = + FilePath::FixPathForPlatform(FAppCDSCacheDirectory); + } + + return FAppCDSCacheDirectory; +} + +TString Package::GetAppCDSCacheFileName() { + assert(FBootFields != NULL); + + if (FBootFields->FAppCDSCacheFileName.empty() == false) { + Macros& macros = Macros::GetInstance(); + FBootFields->FAppCDSCacheFileName = + macros.ExpandMacros(FBootFields->FAppCDSCacheFileName); + FBootFields->FAppCDSCacheFileName = + FilePath::FixPathForPlatform(FBootFields->FAppCDSCacheFileName); + } + + return FBootFields->FAppCDSCacheFileName; +} + +TString Package::GetPackageAppDataDirectory() { + assert(FBootFields != NULL); + return FBootFields->FPackageAppDataDirectory; +} + +TString Package::GetClassPath() { + assert(FBootFields != NULL); + return FBootFields->FClassPath; +} + +TString Package::GetModulePath() { + assert(FBootFields != NULL); + return FBootFields->FModulePath; +} + +TString Package::GetMainJar() { + assert(FBootFields != NULL); + return FBootFields->FMainJar; +} + +TString Package::GetMainModule() { + assert(FBootFields != NULL); + return FBootFields->FMainModule; +} + +TString Package::GetMainClassName() { + assert(FBootFields != NULL); + return FBootFields->FMainClassName; +} + +TString Package::GetJavaLibraryFileName() { + assert(FBootFields != NULL); + + if (FBootFields->FJavaLibraryFileName.empty() == true) { + Platform& platform = Platform::GetInstance(); + Macros& macros = Macros::GetInstance(); + TString jvmRuntimePath = macros.ExpandMacros(GetJavaRuntimeDirectory()); + FBootFields->FJavaLibraryFileName = + platform.GetBundledJavaLibraryFileName(jvmRuntimePath); + } + + return FBootFields->FJavaLibraryFileName; +} + +TString Package::GetJavaRuntimeDirectory() { + assert(FBootFields != NULL); + return FBootFields->FJavaRuntimeDirectory; +} + +TString Package::GetSplashScreenFileName() { + assert(FBootFields != NULL); + return FBootFields->FSplashScreenFileName; +} + +bool Package::HasSplashScreen() { + assert(FBootFields != NULL); + return FilePath::FileExists(FBootFields->FSplashScreenFileName); +} + +TString Package::GetCommandName() { + assert(FBootFields != NULL); + return FBootFields->FCommandName; +} + +TPlatformNumber Package::GetMemorySize() { + assert(FBootFields != NULL); + return FBootFields->FMemorySize; +} + +PackageBootFields::MemoryState Package::GetMemoryState() { + assert(FBootFields != NULL); + return FBootFields->FMemoryState; +} + +DebugState Package::Debugging() { + return FDebugging; +} --- old/src/jdk.jpackage/share/native/libapplauncher/Package.h 2019-11-18 21:06:32.040260400 -0500 +++ /dev/null 2019-11-18 21:06:33.000000000 -0500 @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef PACKAGE_H -#define PACKAGE_H - - -#include "Platform.h" -#include "PlatformString.h" -#include "FilePath.h" -#include "PropertyFile.h" - -#include -#include - -class PackageBootFields { -public: - enum MemoryState {msManual, msAuto}; - -public: - OrderedMap FJavaOptions; - std::list FArgs; - - TString FPackageRootDirectory; - TString FPackageAppDirectory; - TString FPackageLauncherDirectory; - TString FAppDataDirectory; - TString FPackageAppDataDirectory; - TString FClassPath; - TString FModulePath; - TString FMainJar; - TString FMainModule; - TString FMainClassName; - TString FJavaRuntimeDirectory; - TString FJavaLibraryFileName; - TString FSplashScreenFileName; - bool FUseJavaPreferences; - TString FCommandName; - - TString FAppCDSCacheFileName; - - TPlatformNumber FMemorySize; - MemoryState FMemoryState; -}; - - -class Package { -private: - Package(Package const&); // Don't Implement. - void operator=(Package const&); // Don't implement - -private: - bool FInitialized; - PackageBootFields* FBootFields; - TString FAppCDSCacheDirectory; - - DebugState FDebugging; - - Package(void); - - TString GetMainJar(); - void ReadJavaOptions(ISectionalPropertyContainer* Config); - void PromoteAppCDSState(ISectionalPropertyContainer* Config); - -public: - static Package& GetInstance(); - ~Package(void); - - void Initialize(); - void Clear(); - void FreeBootFields(); - - void SetCommandLineArguments(int argc, TCHAR* argv[]); - - OrderedMap GetJavaOptions(); - TString GetMainModule(); - - std::list GetArgs(); - - TString GetPackageRootDirectory(); - TString GetPackageAppDirectory(); - TString GetPackageLauncherDirectory(); - TString GetAppDataDirectory(); - - TString GetAppCDSCacheDirectory(); - TString GetAppCDSCacheFileName(); - - TString GetPackageAppDataDirectory(); - TString GetClassPath(); - TString GetModulePath(); - TString GetMainClassName(); - TString GetJavaLibraryFileName(); - TString GetJavaRuntimeDirectory(); - TString GetSplashScreenFileName(); - bool HasSplashScreen(); - TString GetCommandName(); - - TPlatformNumber GetMemorySize(); - PackageBootFields::MemoryState GetMemoryState(); - - DebugState Debugging(); -}; - -#endif // PACKAGE_H --- /dev/null 2019-11-18 21:06:33.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Package.h 2019-11-18 21:06:28.742246400 -0500 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef PACKAGE_H +#define PACKAGE_H + + +#include "Platform.h" +#include "PlatformString.h" +#include "FilePath.h" +#include "PropertyFile.h" + +#include +#include + +class PackageBootFields { +public: + enum MemoryState {msManual, msAuto}; + +public: + OrderedMap FJavaOptions; + std::list FArgs; + + TString FPackageRootDirectory; + TString FPackageAppDirectory; + TString FPackageLauncherDirectory; + TString FAppDataDirectory; + TString FPackageAppDataDirectory; + TString FClassPath; + TString FModulePath; + TString FMainJar; + TString FMainModule; + TString FMainClassName; + TString FJavaRuntimeDirectory; + TString FJavaLibraryFileName; + TString FSplashScreenFileName; + bool FUseJavaPreferences; + TString FCommandName; + + TString FAppCDSCacheFileName; + + TPlatformNumber FMemorySize; + MemoryState FMemoryState; +}; + + +class Package { +private: + Package(Package const&); // Don't Implement. + void operator=(Package const&); // Don't implement + +private: + bool FInitialized; + PackageBootFields* FBootFields; + TString FAppCDSCacheDirectory; + + DebugState FDebugging; + + Package(void); + + TString GetMainJar(); + void ReadJavaOptions(ISectionalPropertyContainer* Config); + void PromoteAppCDSState(ISectionalPropertyContainer* Config); + +public: + static Package& GetInstance(); + ~Package(void); + + void Initialize(); + void Clear(); + void FreeBootFields(); + + void SetCommandLineArguments(int argc, TCHAR* argv[]); + + OrderedMap GetJavaOptions(); + TString GetMainModule(); + + std::list GetArgs(); + + TString GetPackageRootDirectory(); + TString GetPackageAppDirectory(); + TString GetPackageLauncherDirectory(); + TString GetAppDataDirectory(); + + TString GetAppCDSCacheDirectory(); + TString GetAppCDSCacheFileName(); + + TString GetPackageAppDataDirectory(); + TString GetClassPath(); + TString GetModulePath(); + TString GetMainClassName(); + TString GetJavaLibraryFileName(); + TString GetJavaRuntimeDirectory(); + TString GetSplashScreenFileName(); + bool HasSplashScreen(); + TString GetCommandName(); + + TPlatformNumber GetMemorySize(); + PackageBootFields::MemoryState GetMemoryState(); + + DebugState Debugging(); +}; + +#endif // PACKAGE_H --- old/src/jdk.jpackage/share/native/libapplauncher/Platform.cpp 2019-11-18 21:06:42.495280900 -0500 +++ /dev/null 2019-11-18 21:06:44.000000000 -0500 @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Platform.h" -#include "Messages.h" -#include "PlatformString.h" -#include "FilePath.h" - -#include -#include - -#ifdef WINDOWS -#include "WindowsPlatform.h" -#endif // WINDOWS -#ifdef LINUX -#include "LinuxPlatform.h" -#endif // LINUX -#ifdef MAC -#include "MacPlatform.h" -#endif // MAC - -Platform& Platform::GetInstance() { -#ifdef WINDOWS - static WindowsPlatform instance; -#endif // WINDOWS - -#ifdef LINUX - static LinuxPlatform instance; -#endif // LINUX - -#ifdef MAC - static MacPlatform instance; -#endif // MAC - - return instance; -} - -TString Platform::GetConfigFileName() { - TString result; - TString basedir = GetPackageAppDirectory(); - - if (basedir.empty() == false) { - basedir = FilePath::IncludeTrailingSeparator(basedir); - TString appConfig = basedir + GetAppName() + _T(".cfg"); - - if (FilePath::FileExists(appConfig) == true) { - result = appConfig; - } - else { - result = basedir + _T("package.cfg"); - - if (FilePath::FileExists(result) == false) { - result = _T(""); - } - } - } - - return result; -} - -std::list Platform::LoadFromFile(TString FileName) { - std::list result; - - if (FilePath::FileExists(FileName) == true) { - std::wifstream stream(FileName.data()); - InitStreamLocale(&stream); - - if (stream.is_open() == true) { - while (stream.eof() == false) { - std::wstring line; - std::getline(stream, line); - - // # at the first character will comment out the line. - if (line.empty() == false && line[0] != '#') { - result.push_back(PlatformString(line).toString()); - } - } - } - } - - return result; -} - -void Platform::SaveToFile(TString FileName, std::list Contents, bool ownerOnly) { - TString path = FilePath::ExtractFilePath(FileName); - - if (FilePath::DirectoryExists(path) == false) { - FilePath::CreateDirectory(path, ownerOnly); - } - - std::wofstream stream(FileName.data()); - InitStreamLocale(&stream); - - FilePath::ChangePermissions(FileName.data(), ownerOnly); - - if (stream.is_open() == true) { - for (std::list::const_iterator iterator = - Contents.begin(); iterator != Contents.end(); iterator++) { - TString line = *iterator; - stream << PlatformString(line).toUnicodeString() << std::endl; - } - } -} - -std::map Platform::GetKeys() { - std::map keys; - keys.insert(std::map::value_type(CONFIG_VERSION, - _T("app.version"))); - keys.insert(std::map::value_type(CONFIG_MAINJAR_KEY, - _T("app.mainjar"))); - keys.insert(std::map::value_type(CONFIG_MAINMODULE_KEY, - _T("app.mainmodule"))); - keys.insert(std::map::value_type(CONFIG_MAINCLASSNAME_KEY, - _T("app.mainclass"))); - keys.insert(std::map::value_type(CONFIG_CLASSPATH_KEY, - _T("app.classpath"))); - keys.insert(std::map::value_type(CONFIG_MODULEPATH_KEY, - _T("app.modulepath"))); - keys.insert(std::map::value_type(APP_NAME_KEY, - _T("app.name"))); - keys.insert(std::map::value_type(JAVA_RUNTIME_KEY, - _T("app.runtime"))); - keys.insert(std::map::value_type(JPACKAGE_APP_DATA_DIR, - _T("app.identifier"))); - keys.insert(std::map::value_type(CONFIG_SPLASH_KEY, - _T("app.splash"))); - keys.insert(std::map::value_type(CONFIG_APP_MEMORY, - _T("app.memory"))); - keys.insert(std::map::value_type(CONFIG_APP_DEBUG, - _T("app.debug"))); - keys.insert(std::map::value_type(CONFIG_APPLICATION_INSTANCE, - _T("app.application.instance"))); - keys.insert(std::map::value_type(CONFIG_SECTION_APPLICATION, - _T("Application"))); - keys.insert(std::map::value_type(CONFIG_SECTION_JAVAOPTIONS, - _T("JavaOptions"))); - keys.insert(std::map::value_type(CONFIG_SECTION_APPCDSJAVAOPTIONS, - _T("AppCDSJavaOptions"))); - keys.insert(std::map::value_type(CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS, - _T("AppCDSGenerateCacheJavaOptions"))); - keys.insert(std::map::value_type(CONFIG_SECTION_ARGOPTIONS, - _T("ArgOptions"))); - - return keys; -} --- /dev/null 2019-11-18 21:06:44.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Platform.cpp 2019-11-18 21:06:39.034536600 -0500 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Platform.h" +#include "Messages.h" +#include "PlatformString.h" +#include "FilePath.h" + +#include +#include + +#ifdef WINDOWS +#include "WindowsPlatform.h" +#endif // WINDOWS +#ifdef LINUX +#include "LinuxPlatform.h" +#endif // LINUX +#ifdef MAC +#include "MacPlatform.h" +#endif // MAC + +Platform& Platform::GetInstance() { +#ifdef WINDOWS + static WindowsPlatform instance; +#endif // WINDOWS + +#ifdef LINUX + static LinuxPlatform instance; +#endif // LINUX + +#ifdef MAC + static MacPlatform instance; +#endif // MAC + + return instance; +} + +TString Platform::GetConfigFileName() { + TString result; + TString basedir = GetPackageAppDirectory(); + + if (basedir.empty() == false) { + basedir = FilePath::IncludeTrailingSeparator(basedir); + TString appConfig = basedir + GetAppName() + _T(".cfg"); + + if (FilePath::FileExists(appConfig) == true) { + result = appConfig; + } + else { + result = basedir + _T("package.cfg"); + + if (FilePath::FileExists(result) == false) { + result = _T(""); + } + } + } + + return result; +} + +std::list Platform::LoadFromFile(TString FileName) { + std::list result; + + if (FilePath::FileExists(FileName) == true) { + std::wifstream stream(FileName.data()); + InitStreamLocale(&stream); + + if (stream.is_open() == true) { + while (stream.eof() == false) { + std::wstring line; + std::getline(stream, line); + + // # at the first character will comment out the line. + if (line.empty() == false && line[0] != '#') { + result.push_back(PlatformString(line).toString()); + } + } + } + } + + return result; +} + +void Platform::SaveToFile(TString FileName, std::list Contents, bool ownerOnly) { + TString path = FilePath::ExtractFilePath(FileName); + + if (FilePath::DirectoryExists(path) == false) { + FilePath::CreateDirectory(path, ownerOnly); + } + + std::wofstream stream(FileName.data()); + InitStreamLocale(&stream); + + FilePath::ChangePermissions(FileName.data(), ownerOnly); + + if (stream.is_open() == true) { + for (std::list::const_iterator iterator = + Contents.begin(); iterator != Contents.end(); iterator++) { + TString line = *iterator; + stream << PlatformString(line).toUnicodeString() << std::endl; + } + } +} + +std::map Platform::GetKeys() { + std::map keys; + keys.insert(std::map::value_type(CONFIG_VERSION, + _T("app.version"))); + keys.insert(std::map::value_type(CONFIG_MAINJAR_KEY, + _T("app.mainjar"))); + keys.insert(std::map::value_type(CONFIG_MAINMODULE_KEY, + _T("app.mainmodule"))); + keys.insert(std::map::value_type(CONFIG_MAINCLASSNAME_KEY, + _T("app.mainclass"))); + keys.insert(std::map::value_type(CONFIG_CLASSPATH_KEY, + _T("app.classpath"))); + keys.insert(std::map::value_type(CONFIG_MODULEPATH_KEY, + _T("app.modulepath"))); + keys.insert(std::map::value_type(APP_NAME_KEY, + _T("app.name"))); + keys.insert(std::map::value_type(JAVA_RUNTIME_KEY, + _T("app.runtime"))); + keys.insert(std::map::value_type(JPACKAGE_APP_DATA_DIR, + _T("app.identifier"))); + keys.insert(std::map::value_type(CONFIG_SPLASH_KEY, + _T("app.splash"))); + keys.insert(std::map::value_type(CONFIG_APP_MEMORY, + _T("app.memory"))); + keys.insert(std::map::value_type(CONFIG_APP_DEBUG, + _T("app.debug"))); + keys.insert(std::map::value_type(CONFIG_APPLICATION_INSTANCE, + _T("app.application.instance"))); + keys.insert(std::map::value_type(CONFIG_SECTION_APPLICATION, + _T("Application"))); + keys.insert(std::map::value_type(CONFIG_SECTION_JAVAOPTIONS, + _T("JavaOptions"))); + keys.insert(std::map::value_type(CONFIG_SECTION_APPCDSJAVAOPTIONS, + _T("AppCDSJavaOptions"))); + keys.insert(std::map::value_type(CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS, + _T("AppCDSGenerateCacheJavaOptions"))); + keys.insert(std::map::value_type(CONFIG_SECTION_ARGOPTIONS, + _T("ArgOptions"))); + + return keys; +} --- old/src/jdk.jpackage/share/native/libapplauncher/Platform.h 2019-11-18 21:06:52.911520200 -0500 +++ /dev/null 2019-11-18 21:06:54.000000000 -0500 @@ -1,266 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef PLATFORM_H -#define PLATFORM_H - -#include "PlatformDefs.h" -#include "Properties.h" -#include "OrderedMap.h" -#include "Library.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -// Config file sections -#define CONFIG_SECTION_APPLICATION _T("CONFIG_SECTION_APPLICATION") -#define CONFIG_SECTION_JAVAOPTIONS _T("CONFIG_SECTION_JAVAOPTIONS") -#define CONFIG_SECTION_APPCDSJAVAOPTIONS _T("CONFIG_SECTION_APPCDSJAVAOPTIONS") -#define CONFIG_SECTION_ARGOPTIONS _T("CONFIG_SECTION_ARGOPTIONS") -#define CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS \ - _T("CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS") - -// Config file keys. -#define CONFIG_VERSION _T("CONFIG_VERSION") -#define CONFIG_MAINJAR_KEY _T("CONFIG_MAINJAR_KEY") -#define CONFIG_MAINMODULE_KEY _T("CONFIG_MAINMODULE_KEY") -#define CONFIG_MAINCLASSNAME_KEY _T("CONFIG_MAINCLASSNAME_KEY") -#define CONFIG_CLASSPATH_KEY _T("CONFIG_CLASSPATH_KEY") -#define CONFIG_MODULEPATH_KEY _T("CONFIG_MODULEPATH_KEY") -#define APP_NAME_KEY _T("APP_NAME_KEY") -#define CONFIG_SPLASH_KEY _T("CONFIG_SPLASH_KEY") -#define CONFIG_APP_MEMORY _T("CONFIG_APP_MEMORY") -#define CONFIG_APP_DEBUG _T("CONFIG_APP_DEBUG") -#define CONFIG_APPLICATION_INSTANCE _T("CONFIG_APPLICATION_INSTANCE") - -#define JAVA_RUNTIME_KEY _T("JAVA_RUNTIME_KEY") -#define JPACKAGE_APP_DATA_DIR _T("CONFIG_APP_IDENTIFIER") - -struct WideString { - size_t length; - wchar_t* data; - - WideString() { length = 0; data = NULL; } -}; - -struct MultibyteString { - size_t length; - char* data; - - MultibyteString() { length = 0; data = NULL; } -}; - -class Process { -protected: - std::list FOutput; - -public: - Process() { - Output.SetInstance(this); - Input.SetInstance(this); - } - - virtual ~Process() {} - - virtual bool IsRunning() = 0; - virtual bool Terminate() = 0; - virtual bool Execute(const TString Application, - const std::vector Arguments, bool AWait = false) = 0; - virtual bool Wait() = 0; - virtual TProcessID GetProcessID() = 0; - - virtual std::list GetOutput() { return FOutput; } - virtual void SetInput(TString Value) = 0; - - ReadProperty, &Process::GetOutput> Output; - WriteProperty Input; -}; - - -template -class AutoFreePtr { -private: - T* FObject; - -public: - AutoFreePtr() { - FObject = NULL; - } - - AutoFreePtr(T* Value) { - FObject = Value; - } - - ~AutoFreePtr() { - if (FObject != NULL) { - delete FObject; - } - } - - operator T* () const { - return FObject; - } - - T& operator* () const { - return *FObject; - } - - T* operator->() const { - return FObject; - } - - T** operator&() { - return &FObject; - } - - T* operator=(const T * rhs) { - FObject = rhs; - return FObject; - } -}; - -enum DebugState {dsNone, dsNative, dsJava}; -enum MessageResponse {mrOK, mrCancel}; -enum AppCDSState {cdsUninitialized, cdsDisabled, - cdsEnabled, cdsAuto, cdsGenCache}; - -class Platform { -private: - AppCDSState FAppCDSState; - -protected: - Platform(void): FAppCDSState(cdsUninitialized) { - } - -public: - AppCDSState GetAppCDSState() { return FAppCDSState; } - void SetAppCDSState(AppCDSState Value) { FAppCDSState = Value; } - - static Platform& GetInstance(); - - virtual ~Platform(void) {} - -public: - virtual void ShowMessage(TString title, TString description) = 0; - virtual void ShowMessage(TString description) = 0; - virtual MessageResponse ShowResponseMessage(TString title, - TString description) = 0; - - virtual void SetCurrentDirectory(TString Value) = 0; - - // Caller must free result using delete[]. - virtual TCHAR* ConvertStringToFileSystemString(TCHAR* Source, - bool &release) = 0; - - // Caller must free result using delete[]. - virtual TCHAR* ConvertFileSystemStringToString(TCHAR* Source, - bool &release) = 0; - - // Returns: - // Windows=C:\Users\\AppData\Local - // Linux=~/.local - // Mac=~/Library/Application Support - virtual TString GetAppDataDirectory() = 0; - - virtual TString GetPackageAppDirectory() = 0; - virtual TString GetPackageLauncherDirectory() = 0; - virtual TString GetPackageRuntimeBinDirectory() = 0; - virtual TString GetAppName() = 0; - - virtual TString GetConfigFileName(); - - virtual TString GetBundledJavaLibraryFileName(TString RuntimePath) = 0; - - // Caller must free result. - virtual ISectionalPropertyContainer* GetConfigFile(TString FileName) = 0; - - virtual TString GetModuleFileName() = 0; - virtual TString GetPackageRootDirectory() = 0; - - virtual Module LoadLibrary(TString FileName) = 0; - virtual void FreeLibrary(Module Module) = 0; - virtual Procedure GetProcAddress(Module Module, std::string MethodName) = 0; - - // Caller must free result. - virtual Process* CreateProcess() = 0; - - virtual bool IsMainThread() = 0; - - // Returns megabytes. - virtual TPlatformNumber GetMemorySize() = 0; - - virtual std::map GetKeys(); - - virtual void InitStreamLocale(wios *stream) = 0; - virtual std::list LoadFromFile(TString FileName); - virtual void SaveToFile(TString FileName, - std::list Contents, bool ownerOnly); - - virtual TString GetTempDirectory() = 0; - - virtual void addPlatformDependencies(JavaLibrary *pJavaLibrary) = 0; - -public: - // String helpers - // Caller must free result using delete[]. - static void CopyString(char *Destination, - size_t NumberOfElements, const char *Source); - - // Caller must free result using delete[]. - static void CopyString(wchar_t *Destination, - size_t NumberOfElements, const wchar_t *Source); - - static WideString MultibyteStringToWideString(const char* value); - static MultibyteString WideStringToMultibyteString(const wchar_t* value); -}; - -class Exception: public std::exception { -private: - TString FMessage; - -protected: - void SetMessage(const TString Message) { - FMessage = Message; - } - -public: - explicit Exception() : exception() {} - explicit Exception(const TString Message) : exception() { - SetMessage(Message); - } - virtual ~Exception() throw() {} - - TString GetMessage() { return FMessage; } -}; - -#endif // PLATFORM_H --- /dev/null 2019-11-18 21:06:54.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Platform.h 2019-11-18 21:06:49.676559000 -0500 @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef PLATFORM_H +#define PLATFORM_H + +#include "PlatformDefs.h" +#include "Properties.h" +#include "OrderedMap.h" +#include "Library.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +// Config file sections +#define CONFIG_SECTION_APPLICATION _T("CONFIG_SECTION_APPLICATION") +#define CONFIG_SECTION_JAVAOPTIONS _T("CONFIG_SECTION_JAVAOPTIONS") +#define CONFIG_SECTION_APPCDSJAVAOPTIONS _T("CONFIG_SECTION_APPCDSJAVAOPTIONS") +#define CONFIG_SECTION_ARGOPTIONS _T("CONFIG_SECTION_ARGOPTIONS") +#define CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS \ + _T("CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS") + +// Config file keys. +#define CONFIG_VERSION _T("CONFIG_VERSION") +#define CONFIG_MAINJAR_KEY _T("CONFIG_MAINJAR_KEY") +#define CONFIG_MAINMODULE_KEY _T("CONFIG_MAINMODULE_KEY") +#define CONFIG_MAINCLASSNAME_KEY _T("CONFIG_MAINCLASSNAME_KEY") +#define CONFIG_CLASSPATH_KEY _T("CONFIG_CLASSPATH_KEY") +#define CONFIG_MODULEPATH_KEY _T("CONFIG_MODULEPATH_KEY") +#define APP_NAME_KEY _T("APP_NAME_KEY") +#define CONFIG_SPLASH_KEY _T("CONFIG_SPLASH_KEY") +#define CONFIG_APP_MEMORY _T("CONFIG_APP_MEMORY") +#define CONFIG_APP_DEBUG _T("CONFIG_APP_DEBUG") +#define CONFIG_APPLICATION_INSTANCE _T("CONFIG_APPLICATION_INSTANCE") + +#define JAVA_RUNTIME_KEY _T("JAVA_RUNTIME_KEY") +#define JPACKAGE_APP_DATA_DIR _T("CONFIG_APP_IDENTIFIER") + +struct WideString { + size_t length; + wchar_t* data; + + WideString() { length = 0; data = NULL; } +}; + +struct MultibyteString { + size_t length; + char* data; + + MultibyteString() { length = 0; data = NULL; } +}; + +class Process { +protected: + std::list FOutput; + +public: + Process() { + Output.SetInstance(this); + Input.SetInstance(this); + } + + virtual ~Process() {} + + virtual bool IsRunning() = 0; + virtual bool Terminate() = 0; + virtual bool Execute(const TString Application, + const std::vector Arguments, bool AWait = false) = 0; + virtual bool Wait() = 0; + virtual TProcessID GetProcessID() = 0; + + virtual std::list GetOutput() { return FOutput; } + virtual void SetInput(TString Value) = 0; + + ReadProperty, &Process::GetOutput> Output; + WriteProperty Input; +}; + + +template +class AutoFreePtr { +private: + T* FObject; + +public: + AutoFreePtr() { + FObject = NULL; + } + + AutoFreePtr(T* Value) { + FObject = Value; + } + + ~AutoFreePtr() { + if (FObject != NULL) { + delete FObject; + } + } + + operator T* () const { + return FObject; + } + + T& operator* () const { + return *FObject; + } + + T* operator->() const { + return FObject; + } + + T** operator&() { + return &FObject; + } + + T* operator=(const T * rhs) { + FObject = rhs; + return FObject; + } +}; + +enum DebugState {dsNone, dsNative, dsJava}; +enum MessageResponse {mrOK, mrCancel}; +enum AppCDSState {cdsUninitialized, cdsDisabled, + cdsEnabled, cdsAuto, cdsGenCache}; + +class Platform { +private: + AppCDSState FAppCDSState; + +protected: + Platform(void): FAppCDSState(cdsUninitialized) { + } + +public: + AppCDSState GetAppCDSState() { return FAppCDSState; } + void SetAppCDSState(AppCDSState Value) { FAppCDSState = Value; } + + static Platform& GetInstance(); + + virtual ~Platform(void) {} + +public: + virtual void ShowMessage(TString title, TString description) = 0; + virtual void ShowMessage(TString description) = 0; + virtual MessageResponse ShowResponseMessage(TString title, + TString description) = 0; + + // Caller must free result using delete[]. + virtual TCHAR* ConvertStringToFileSystemString(TCHAR* Source, + bool &release) = 0; + + // Caller must free result using delete[]. + virtual TCHAR* ConvertFileSystemStringToString(TCHAR* Source, + bool &release) = 0; + + // Returns: + // Windows=C:\Users\\AppData\Local + // Linux=~/.local + // Mac=~/Library/Application Support + virtual TString GetAppDataDirectory() = 0; + + virtual TString GetPackageAppDirectory() = 0; + virtual TString GetPackageLauncherDirectory() = 0; + virtual TString GetPackageRuntimeBinDirectory() = 0; + virtual TString GetAppName() = 0; + + virtual TString GetConfigFileName(); + + virtual TString GetBundledJavaLibraryFileName(TString RuntimePath) = 0; + + // Caller must free result. + virtual ISectionalPropertyContainer* GetConfigFile(TString FileName) = 0; + + virtual TString GetModuleFileName() = 0; + virtual TString GetPackageRootDirectory() = 0; + + virtual Module LoadLibrary(TString FileName) = 0; + virtual void FreeLibrary(Module Module) = 0; + virtual Procedure GetProcAddress(Module Module, std::string MethodName) = 0; + + // Caller must free result. + virtual Process* CreateProcess() = 0; + + virtual bool IsMainThread() = 0; + + // Returns megabytes. + virtual TPlatformNumber GetMemorySize() = 0; + + virtual std::map GetKeys(); + + virtual void InitStreamLocale(wios *stream) = 0; + virtual std::list LoadFromFile(TString FileName); + virtual void SaveToFile(TString FileName, + std::list Contents, bool ownerOnly); + + virtual TString GetTempDirectory() = 0; + + virtual void addPlatformDependencies(JavaLibrary *pJavaLibrary) = 0; + +public: + // String helpers + // Caller must free result using delete[]. + static void CopyString(char *Destination, + size_t NumberOfElements, const char *Source); + + // Caller must free result using delete[]. + static void CopyString(wchar_t *Destination, + size_t NumberOfElements, const wchar_t *Source); + + static WideString MultibyteStringToWideString(const char* value); + static MultibyteString WideStringToMultibyteString(const wchar_t* value); +}; + +class Exception: public std::exception { +private: + TString FMessage; + +protected: + void SetMessage(const TString Message) { + FMessage = Message; + } + +public: + explicit Exception() : exception() {} + explicit Exception(const TString Message) : exception() { + SetMessage(Message); + } + virtual ~Exception() throw() {} + + TString GetMessage() { return FMessage; } +}; + +#endif // PLATFORM_H --- old/src/jdk.jpackage/share/native/libapplauncher/PlatformString.cpp 2019-11-18 21:07:13.329811000 -0500 +++ /dev/null 2019-11-18 21:07:14.000000000 -0500 @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "PlatformString.h" - -#include "Helpers.h" - -#include -#include -#include -#include -#include -#include - -#include "jni.h" - -void PlatformString::initialize() { - FWideTStringToFree = NULL; - FLength = 0; - FData = NULL; -} - -PlatformString::PlatformString(void) { - initialize(); -} - -PlatformString::~PlatformString(void) { - if (FData != NULL) { - delete[] FData; - } - - if (FWideTStringToFree != NULL) { - delete[] FWideTStringToFree; - } -} - -PlatformString::PlatformString(const PlatformString &value) { - initialize(); - FLength = value.FLength; - FData = new char[FLength + 1]; - Platform::CopyString(FData, FLength + 1, value.FData); -} - -PlatformString::PlatformString(const char* value) { - initialize(); - FLength = strlen(value); - FData = new char[FLength + 1]; - Platform::CopyString(FData, FLength + 1, value); -} - -PlatformString::PlatformString(size_t Value) { - initialize(); - - std::stringstream ss; - std::string s; - ss << Value; - s = ss.str(); - - FLength = strlen(s.c_str()); - FData = new char[FLength + 1]; - Platform::CopyString(FData, FLength + 1, s.c_str()); -} - -PlatformString::PlatformString(const wchar_t* value) { - initialize(); - MultibyteString temp = Platform::WideStringToMultibyteString(value); - FLength = temp.length; - FData = temp.data; -} - -PlatformString::PlatformString(const std::string &value) { - initialize(); - const char* lvalue = value.data(); - FLength = value.size(); - FData = new char[FLength + 1]; - Platform::CopyString(FData, FLength + 1, lvalue); -} - -PlatformString::PlatformString(const std::wstring &value) { - initialize(); - const wchar_t* lvalue = value.data(); - MultibyteString temp = Platform::WideStringToMultibyteString(lvalue); - FLength = temp.length; - FData = temp.data; -} - -TString PlatformString::Format(const TString value, ...) { - TString result = value; - - va_list arglist; - va_start(arglist, value); - - while (1) { - size_t pos = result.find(_T("%s"), 0); - - if (pos == TString::npos) { - break; - } - else { - TCHAR* arg = va_arg(arglist, TCHAR*); - - if (arg == NULL) { - break; - } - else { - result.replace(pos, StringLength(_T("%s")), arg); - } - } - } - - va_end(arglist); - - return result; -} - -size_t PlatformString::length() { - return FLength; -} - -char* PlatformString::c_str() { - return FData; -} - -char* PlatformString::toMultibyte() { - return FData; -} - -wchar_t* PlatformString::toWideString() { - WideString result = Platform::MultibyteStringToWideString(FData); - - if (result.data != NULL) { - if (FWideTStringToFree != NULL) { - delete [] FWideTStringToFree; - } - - FWideTStringToFree = result.data; - } - - return result.data; -} - -std::wstring PlatformString::toUnicodeString() { - std::wstring result; - wchar_t* data = toWideString(); - - if (FLength != 0 && data != NULL) { - // NOTE: Cleanup of result is handled by PlatformString destructor. - result = data; - } - - return result; -} - -std::string PlatformString::toStdString() { - std::string result; - char* data = toMultibyte(); - - if (FLength > 0 && data != NULL) { - result = data; - } - - return result; -} - -TCHAR* PlatformString::toPlatformString() { -#ifdef _UNICODE - return toWideString(); -#else - return c_str(); -#endif //_UNICODE -} - -TString PlatformString::toString() { -#ifdef _UNICODE - return toUnicodeString(); -#else - return toStdString(); -#endif //_UNICODE -} - -PlatformString::operator char* () { - return c_str(); -} - -PlatformString::operator wchar_t* () { - return toWideString(); -} - -PlatformString::operator std::wstring () { - return toUnicodeString(); -} - -char* PlatformString::duplicate(const char* Value) { - size_t length = strlen(Value); - char* result = new char[length + 1]; - Platform::CopyString(result, length + 1, Value); - return result; -} - -wchar_t* PlatformString::duplicate(const wchar_t* Value) { - size_t length = wcslen(Value); - wchar_t* result = new wchar_t[length + 1]; - Platform::CopyString(result, length + 1, Value); - return result; -} --- /dev/null 2019-11-18 21:07:15.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/PlatformString.cpp 2019-11-18 21:07:09.904126600 -0500 @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "PlatformString.h" + +#include "Helpers.h" + +#include +#include +#include +#include +#include +#include + +#include "jni.h" + +void PlatformString::initialize() { + FWideTStringToFree = NULL; + FLength = 0; + FData = NULL; +} + +PlatformString::PlatformString(void) { + initialize(); +} + +PlatformString::~PlatformString(void) { + if (FData != NULL) { + delete[] FData; + } + + if (FWideTStringToFree != NULL) { + delete[] FWideTStringToFree; + } +} + +PlatformString::PlatformString(const PlatformString &value) { + initialize(); + FLength = value.FLength; + FData = new char[FLength + 1]; + Platform::CopyString(FData, FLength + 1, value.FData); +} + +PlatformString::PlatformString(const char* value) { + initialize(); + FLength = strlen(value); + FData = new char[FLength + 1]; + Platform::CopyString(FData, FLength + 1, value); +} + +PlatformString::PlatformString(size_t Value) { + initialize(); + + std::stringstream ss; + std::string s; + ss << Value; + s = ss.str(); + + FLength = strlen(s.c_str()); + FData = new char[FLength + 1]; + Platform::CopyString(FData, FLength + 1, s.c_str()); +} + +PlatformString::PlatformString(const wchar_t* value) { + initialize(); + MultibyteString temp = Platform::WideStringToMultibyteString(value); + FLength = temp.length; + FData = temp.data; +} + +PlatformString::PlatformString(const std::string &value) { + initialize(); + const char* lvalue = value.data(); + FLength = value.size(); + FData = new char[FLength + 1]; + Platform::CopyString(FData, FLength + 1, lvalue); +} + +PlatformString::PlatformString(const std::wstring &value) { + initialize(); + const wchar_t* lvalue = value.data(); + MultibyteString temp = Platform::WideStringToMultibyteString(lvalue); + FLength = temp.length; + FData = temp.data; +} + +TString PlatformString::Format(const TString value, ...) { + TString result = value; + + va_list arglist; + va_start(arglist, value); + + while (1) { + size_t pos = result.find(_T("%s"), 0); + + if (pos == TString::npos) { + break; + } + else { + TCHAR* arg = va_arg(arglist, TCHAR*); + + if (arg == NULL) { + break; + } + else { + result.replace(pos, StringLength(_T("%s")), arg); + } + } + } + + va_end(arglist); + + return result; +} + +size_t PlatformString::length() { + return FLength; +} + +char* PlatformString::c_str() { + return FData; +} + +char* PlatformString::toMultibyte() { + return FData; +} + +wchar_t* PlatformString::toWideString() { + WideString result = Platform::MultibyteStringToWideString(FData); + + if (result.data != NULL) { + if (FWideTStringToFree != NULL) { + delete [] FWideTStringToFree; + } + + FWideTStringToFree = result.data; + } + + return result.data; +} + +std::wstring PlatformString::toUnicodeString() { + std::wstring result; + wchar_t* data = toWideString(); + + if (FLength != 0 && data != NULL) { + // NOTE: Cleanup of result is handled by PlatformString destructor. + result = data; + } + + return result; +} + +std::string PlatformString::toStdString() { + std::string result; + char* data = toMultibyte(); + + if (FLength > 0 && data != NULL) { + result = data; + } + + return result; +} + +TCHAR* PlatformString::toPlatformString() { +#ifdef _UNICODE + return toWideString(); +#else + return c_str(); +#endif //_UNICODE +} + +TString PlatformString::toString() { +#ifdef _UNICODE + return toUnicodeString(); +#else + return toStdString(); +#endif //_UNICODE +} + +PlatformString::operator char* () { + return c_str(); +} + +PlatformString::operator wchar_t* () { + return toWideString(); +} + +PlatformString::operator std::wstring () { + return toUnicodeString(); +} + +char* PlatformString::duplicate(const char* Value) { + size_t length = strlen(Value); + char* result = new char[length + 1]; + Platform::CopyString(result, length + 1, Value); + return result; +} + +wchar_t* PlatformString::duplicate(const wchar_t* Value) { + size_t length = wcslen(Value); + wchar_t* result = new wchar_t[length + 1]; + Platform::CopyString(result, length + 1, Value); + return result; +} --- old/src/jdk.jpackage/share/native/libapplauncher/PlatformString.h 2019-11-18 21:07:23.794140700 -0500 +++ /dev/null 2019-11-18 21:07:25.000000000 -0500 @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef PLATFORMSTRING_H -#define PLATFORMSTRING_H - - -#include -#include -#include -#include - -#include "jni.h" -#include "Platform.h" - - -template -class DynamicBuffer { -private: - T* FData; - size_t FSize; - -public: - DynamicBuffer(size_t Size) { - FSize = 0; - FData = NULL; - Resize(Size); - } - - ~DynamicBuffer() { - delete[] FData; - } - - T* GetData() { return FData; } - size_t GetSize() { return FSize; } - - bool Resize(size_t Size) { - FSize = Size; - - if (FData != NULL) { - delete[] FData; - FData = NULL; - } - - if (FSize != 0) { - FData = new T[FSize]; - if (FData != NULL) { - Zero(); - } else { - return false; - } - } - - return true; - } - - void Zero() { - memset(FData, 0, FSize * sizeof(T)); - } - - T& operator[](size_t index) { - return FData[index]; - } -}; - -class PlatformString { -private: - char* FData; // Stored as UTF-8 - size_t FLength; - wchar_t* FWideTStringToFree; - - void initialize(); - -// Prohibit Heap-Based PlatformStrings -private: - static void *operator new(size_t size); - static void operator delete(void *ptr); - -public: - PlatformString(void); - PlatformString(const PlatformString &value); - PlatformString(const char* value); - PlatformString(const wchar_t* value); - PlatformString(const std::string &value); - PlatformString(const std::wstring &value); - PlatformString(size_t Value); - - static TString Format(const TString value, ...); - - ~PlatformString(void); - - size_t length(); - - char* c_str(); - char* toMultibyte(); - wchar_t* toWideString(); - std::wstring toUnicodeString(); - std::string toStdString(); - TCHAR* toPlatformString(); - TString toString(); - - operator char* (); - operator wchar_t* (); - operator std::wstring (); - - // Caller must free result using delete[]. - static char* duplicate(const char* Value); - - // Caller must free result using delete[]. - static wchar_t* duplicate(const wchar_t* Value); -}; - - -#endif // PLATFORMSTRING_H --- /dev/null 2019-11-18 21:07:25.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/PlatformString.h 2019-11-18 21:07:20.404366000 -0500 @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef PLATFORMSTRING_H +#define PLATFORMSTRING_H + + +#include +#include +#include +#include + +#include "jni.h" +#include "Platform.h" + + +template +class DynamicBuffer { +private: + T* FData; + size_t FSize; + +public: + DynamicBuffer(size_t Size) { + FSize = 0; + FData = NULL; + Resize(Size); + } + + ~DynamicBuffer() { + delete[] FData; + } + + T* GetData() { return FData; } + size_t GetSize() { return FSize; } + + bool Resize(size_t Size) { + FSize = Size; + + if (FData != NULL) { + delete[] FData; + FData = NULL; + } + + if (FSize != 0) { + FData = new T[FSize]; + if (FData != NULL) { + Zero(); + } else { + return false; + } + } + + return true; + } + + void Zero() { + memset(FData, 0, FSize * sizeof(T)); + } + + T& operator[](size_t index) { + return FData[index]; + } +}; + +class PlatformString { +private: + char* FData; // Stored as UTF-8 + size_t FLength; + wchar_t* FWideTStringToFree; + + void initialize(); + +// Prohibit Heap-Based PlatformStrings +private: + static void *operator new(size_t size); + static void operator delete(void *ptr); + +public: + PlatformString(void); + PlatformString(const PlatformString &value); + PlatformString(const char* value); + PlatformString(const wchar_t* value); + PlatformString(const std::string &value); + PlatformString(const std::wstring &value); + PlatformString(size_t Value); + + static TString Format(const TString value, ...); + + ~PlatformString(void); + + size_t length(); + + char* c_str(); + char* toMultibyte(); + wchar_t* toWideString(); + std::wstring toUnicodeString(); + std::string toStdString(); + TCHAR* toPlatformString(); + TString toString(); + + operator char* (); + operator wchar_t* (); + operator std::wstring (); + + // Caller must free result using delete[]. + static char* duplicate(const char* Value); + + // Caller must free result using delete[]. + static wchar_t* duplicate(const wchar_t* Value); +}; + + +#endif // PLATFORMSTRING_H --- old/src/jdk.jpackage/share/native/libapplauncher/Properties.h 2019-11-18 21:07:34.241258600 -0500 +++ /dev/null 2019-11-18 21:07:35.000000000 -0500 @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef PROPERTIES_H -#define PROPERTIES_H - -#include "PlatformDefs.h" -#include "OrderedMap.h" - -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include - -//using namespace std; - -template -class Property { -private: - ObjectType* FObject; - -public: - Property() { - FObject = NULL; - } - - void SetInstance(ObjectType* Value) { - FObject = Value; - } - - // To set the value using the set method. - ValueType operator =(const ValueType& Value) { - assert(FObject != NULL); - (FObject->*setter)(Value); - return Value; - } - - // The Property class is treated as the internal type. - operator ValueType() { - assert(FObject != NULL); - return (FObject->*getter)(); - } -}; - -template -class ReadProperty { -private: - ObjectType* FObject; - -public: - ReadProperty() { - FObject = NULL; - } - - void SetInstance(ObjectType* Value) { - FObject = Value; - } - - // The Property class is treated as the internal type. - operator ValueType() { - assert(FObject != NULL); - return (FObject->*getter)(); - } -}; - -template -class WriteProperty { -private: - ObjectType* FObject; - -public: - WriteProperty() { - FObject = NULL; - } - - void SetInstance(ObjectType* Value) { - FObject = Value; - } - - // To set the value using the set method. - ValueType operator =(const ValueType& Value) { - assert(FObject != NULL); - (FObject->*setter)(Value); - return Value; - } -}; - -template -class StaticProperty { -public: - StaticProperty() { - } - - // To set the value using the set method. - ValueType operator =(const ValueType& Value) { - (*getter)(Value); - return Value; - } - - // The Property class is treated as the internal type which is the getter. - operator ValueType() { - return (*setter)(); - } -}; - -template -class StaticReadProperty { -public: - StaticReadProperty() { - } - - // The Property class is treated as the internal type which is the getter. - operator ValueType() { - return (*getter)(); - } -}; - -template -class StaticWriteProperty { -public: - StaticWriteProperty() { - } - - // To set the value using the set method. - ValueType operator =(const ValueType& Value) { - (*setter)(Value); - return Value; - } -}; - -class IPropertyContainer { -public: - IPropertyContainer(void) {} - virtual ~IPropertyContainer(void) {} - - virtual bool GetValue(const TString Key, TString& Value) = 0; - virtual size_t GetCount() = 0; -}; - -class ISectionalPropertyContainer { -public: - ISectionalPropertyContainer(void) {} - virtual ~ISectionalPropertyContainer(void) {} - - virtual bool GetValue(const TString SectionName, - const TString Key, TString& Value) = 0; - virtual bool ContainsSection(const TString SectionName) = 0; - virtual bool GetSection(const TString SectionName, - OrderedMap &Data) = 0; -}; - -#endif // PROPERTIES_H - --- /dev/null 2019-11-18 21:07:35.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/Properties.h 2019-11-18 21:07:30.853046300 -0500 @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef PROPERTIES_H +#define PROPERTIES_H + +#include "PlatformDefs.h" +#include "OrderedMap.h" + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +//using namespace std; + +template +class Property { +private: + ObjectType* FObject; + +public: + Property() { + FObject = NULL; + } + + void SetInstance(ObjectType* Value) { + FObject = Value; + } + + // To set the value using the set method. + ValueType operator =(const ValueType& Value) { + assert(FObject != NULL); + (FObject->*setter)(Value); + return Value; + } + + // The Property class is treated as the internal type. + operator ValueType() { + assert(FObject != NULL); + return (FObject->*getter)(); + } +}; + +template +class ReadProperty { +private: + ObjectType* FObject; + +public: + ReadProperty() { + FObject = NULL; + } + + void SetInstance(ObjectType* Value) { + FObject = Value; + } + + // The Property class is treated as the internal type. + operator ValueType() { + assert(FObject != NULL); + return (FObject->*getter)(); + } +}; + +template +class WriteProperty { +private: + ObjectType* FObject; + +public: + WriteProperty() { + FObject = NULL; + } + + void SetInstance(ObjectType* Value) { + FObject = Value; + } + + // To set the value using the set method. + ValueType operator =(const ValueType& Value) { + assert(FObject != NULL); + (FObject->*setter)(Value); + return Value; + } +}; + +template +class StaticProperty { +public: + StaticProperty() { + } + + // To set the value using the set method. + ValueType operator =(const ValueType& Value) { + (*getter)(Value); + return Value; + } + + // The Property class is treated as the internal type which is the getter. + operator ValueType() { + return (*setter)(); + } +}; + +template +class StaticReadProperty { +public: + StaticReadProperty() { + } + + // The Property class is treated as the internal type which is the getter. + operator ValueType() { + return (*getter)(); + } +}; + +template +class StaticWriteProperty { +public: + StaticWriteProperty() { + } + + // To set the value using the set method. + ValueType operator =(const ValueType& Value) { + (*setter)(Value); + return Value; + } +}; + +class IPropertyContainer { +public: + IPropertyContainer(void) {} + virtual ~IPropertyContainer(void) {} + + virtual bool GetValue(const TString Key, TString& Value) = 0; + virtual size_t GetCount() = 0; +}; + +class ISectionalPropertyContainer { +public: + ISectionalPropertyContainer(void) {} + virtual ~ISectionalPropertyContainer(void) {} + + virtual bool GetValue(const TString SectionName, + const TString Key, TString& Value) = 0; + virtual bool ContainsSection(const TString SectionName) = 0; + virtual bool GetSection(const TString SectionName, + OrderedMap &Data) = 0; +}; + +#endif // PROPERTIES_H + --- old/src/jdk.jpackage/share/native/libapplauncher/PropertyFile.cpp 2019-11-18 21:07:44.418872300 -0500 +++ /dev/null 2019-11-18 21:07:45.000000000 -0500 @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "PropertyFile.h" - -#include "Helpers.h" -#include "FilePath.h" - -#include - - -PropertyFile::PropertyFile(void) : IPropertyContainer() { - FReadOnly = false; - FModified = false; -} - -PropertyFile::PropertyFile(const TString FileName) : IPropertyContainer() { - FReadOnly = true; - FModified = false; - LoadFromFile(FileName); -} - -PropertyFile::PropertyFile(OrderedMap Value) { - FData.Append(Value); -} - -PropertyFile::PropertyFile(PropertyFile &Value) { - FData = Value.FData; - FReadOnly = Value.FReadOnly; - FModified = Value.FModified; -} - -PropertyFile::~PropertyFile(void) { - FData.Clear(); -} - -void PropertyFile::SetModified(bool Value) { - FModified = Value; -} - -bool PropertyFile::IsModified() { - return FModified; -} - -bool PropertyFile::GetReadOnly() { - return FReadOnly; -} - -void PropertyFile::SetReadOnly(bool Value) { - FReadOnly = Value; -} - -bool PropertyFile::LoadFromFile(const TString FileName) { - bool result = false; - Platform& platform = Platform::GetInstance(); - - std::list contents = platform.LoadFromFile(FileName); - - if (contents.empty() == false) { - for (std::list::const_iterator iterator = contents.begin(); - iterator != contents.end(); iterator++) { - TString line = *iterator; - TString name; - TString value; - - if (Helpers::SplitOptionIntoNameValue(line, name, value) == true) { - FData.Append(name, value); - } - } - - SetModified(false); - result = true; - } - - return result; -} - -bool PropertyFile::SaveToFile(const TString FileName, bool ownerOnly) { - bool result = false; - - if (GetReadOnly() == false && IsModified()) { - std::list contents; - std::vector keys = FData.GetKeys(); - - for (size_t index = 0; index < keys.size(); index++) { - TString name = keys[index]; - - try { - TString value;// = FData[index]; - - if (FData.GetValue(name, value) == true) { - TString line = name + _T('=') + value; - contents.push_back(line); - } - } - catch (std::out_of_range &) { - } - } - - Platform& platform = Platform::GetInstance(); - platform.SaveToFile(FileName, contents, ownerOnly); - - SetModified(false); - result = true; - } - - return result; -} - -bool PropertyFile::GetValue(const TString Key, TString& Value) { - return FData.GetValue(Key, Value); -} - -bool PropertyFile::SetValue(const TString Key, TString Value) { - bool result = false; - - if (GetReadOnly() == false) { - FData.SetValue(Key, Value); - SetModified(true); - result = true; - } - - return result; -} - -bool PropertyFile::RemoveKey(const TString Key) { - bool result = false; - - if (GetReadOnly() == false) { - result = FData.RemoveByKey(Key); - - if (result == true) { - SetModified(true); - } - } - - return result; -} - -size_t PropertyFile::GetCount() { - return FData.Count(); -} - -OrderedMap PropertyFile::GetData() { - return FData; -} --- /dev/null 2019-11-18 21:07:46.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/PropertyFile.cpp 2019-11-18 21:07:41.183134700 -0500 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "PropertyFile.h" + +#include "Helpers.h" +#include "FilePath.h" + +#include + + +PropertyFile::PropertyFile(void) : IPropertyContainer() { + FReadOnly = false; + FModified = false; +} + +PropertyFile::PropertyFile(const TString FileName) : IPropertyContainer() { + FReadOnly = true; + FModified = false; + LoadFromFile(FileName); +} + +PropertyFile::PropertyFile(OrderedMap Value) { + FData.Append(Value); +} + +PropertyFile::PropertyFile(PropertyFile &Value) { + FData = Value.FData; + FReadOnly = Value.FReadOnly; + FModified = Value.FModified; +} + +PropertyFile::~PropertyFile(void) { + FData.Clear(); +} + +void PropertyFile::SetModified(bool Value) { + FModified = Value; +} + +bool PropertyFile::IsModified() { + return FModified; +} + +bool PropertyFile::GetReadOnly() { + return FReadOnly; +} + +void PropertyFile::SetReadOnly(bool Value) { + FReadOnly = Value; +} + +bool PropertyFile::LoadFromFile(const TString FileName) { + bool result = false; + Platform& platform = Platform::GetInstance(); + + std::list contents = platform.LoadFromFile(FileName); + + if (contents.empty() == false) { + for (std::list::const_iterator iterator = contents.begin(); + iterator != contents.end(); iterator++) { + TString line = *iterator; + TString name; + TString value; + + if (Helpers::SplitOptionIntoNameValue(line, name, value) == true) { + FData.Append(name, value); + } + } + + SetModified(false); + result = true; + } + + return result; +} + +bool PropertyFile::SaveToFile(const TString FileName, bool ownerOnly) { + bool result = false; + + if (GetReadOnly() == false && IsModified()) { + std::list contents; + std::vector keys = FData.GetKeys(); + + for (size_t index = 0; index < keys.size(); index++) { + TString name = keys[index]; + + try { + TString value;// = FData[index]; + + if (FData.GetValue(name, value) == true) { + TString line = name + _T('=') + value; + contents.push_back(line); + } + } + catch (std::out_of_range &) { + } + } + + Platform& platform = Platform::GetInstance(); + platform.SaveToFile(FileName, contents, ownerOnly); + + SetModified(false); + result = true; + } + + return result; +} + +bool PropertyFile::GetValue(const TString Key, TString& Value) { + return FData.GetValue(Key, Value); +} + +bool PropertyFile::SetValue(const TString Key, TString Value) { + bool result = false; + + if (GetReadOnly() == false) { + FData.SetValue(Key, Value); + SetModified(true); + result = true; + } + + return result; +} + +bool PropertyFile::RemoveKey(const TString Key) { + bool result = false; + + if (GetReadOnly() == false) { + result = FData.RemoveByKey(Key); + + if (result == true) { + SetModified(true); + } + } + + return result; +} + +size_t PropertyFile::GetCount() { + return FData.Count(); +} + +OrderedMap PropertyFile::GetData() { + return FData; +} --- old/src/jdk.jpackage/share/native/libapplauncher/PropertyFile.h 2019-11-18 21:07:55.322656100 -0500 +++ /dev/null 2019-11-18 21:07:56.000000000 -0500 @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef PROPERTYFILE_H -#define PROPERTYFILE_H - -#include "Platform.h" -#include "Helpers.h" - - -class PropertyFile : public IPropertyContainer { -private: - bool FReadOnly; - bool FModified; - OrderedMap FData; - - void SetModified(bool Value); - -public: - PropertyFile(void); - PropertyFile(const TString FileName); - PropertyFile(OrderedMap Value); - PropertyFile(PropertyFile &Value); - virtual ~PropertyFile(void); - - bool IsModified(); - bool GetReadOnly(); - void SetReadOnly(bool Value); - - bool LoadFromFile(const TString FileName); - bool SaveToFile(const TString FileName, bool ownerOnly = true); - - bool SetValue(const TString Key, TString Value); - bool RemoveKey(const TString Key); - - OrderedMap GetData(); - - // IPropertyContainer - virtual bool GetValue(const TString Key, TString& Value); - virtual size_t GetCount(); -}; - -#endif // PROPERTYFILE_H --- /dev/null 2019-11-18 21:07:57.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/PropertyFile.h 2019-11-18 21:07:51.732291700 -0500 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef PROPERTYFILE_H +#define PROPERTYFILE_H + +#include "Platform.h" +#include "Helpers.h" + + +class PropertyFile : public IPropertyContainer { +private: + bool FReadOnly; + bool FModified; + OrderedMap FData; + + void SetModified(bool Value); + +public: + PropertyFile(void); + PropertyFile(const TString FileName); + PropertyFile(OrderedMap Value); + PropertyFile(PropertyFile &Value); + virtual ~PropertyFile(void); + + bool IsModified(); + bool GetReadOnly(); + void SetReadOnly(bool Value); + + bool LoadFromFile(const TString FileName); + bool SaveToFile(const TString FileName, bool ownerOnly = true); + + bool SetValue(const TString Key, TString Value); + bool RemoveKey(const TString Key); + + OrderedMap GetData(); + + // IPropertyContainer + virtual bool GetValue(const TString Key, TString& Value); + virtual size_t GetCount(); +}; + +#endif // PROPERTYFILE_H --- old/src/jdk.jpackage/share/native/libapplauncher/main.cpp 2019-11-18 21:08:05.760464300 -0500 +++ /dev/null 2019-11-18 21:08:07.000000000 -0500 @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Platform.h" -#include "PlatformString.h" -#include "FilePath.h" -#include "PropertyFile.h" -#include "JavaVirtualMachine.h" -#include "Package.h" -#include "Macros.h" -#include "Messages.h" - -#include -#include -#include - -/* -This is the app launcher program for application packaging on Windows, Mac, - and Linux. - -Basic approach: - - Launcher (jpackageapplauncher) is executable that loads - applauncher.dll/libapplauncher.dylib/libapplauncher.so - and calls start_launcher below. - - Reads app/package.cfg or Info.plist or app/.cfg for application - launch configuration (package.cfg is property file). - - Load Java with requested Java settings (bundled client Java if availble, - server or installed Java otherwise). - - Wait for Java to exit and then exit from Main - - To debug application by passing command line argument. - - Application folder is added to the library path (so LoadLibrary()) works. - -Limitations and future work: - - Running Java code in primordial thread may cause problems - (example: can not use custom stack size). - Solution used by java launcher is to create a new thread to invoke Java. - See CR 6316197 for more information. -*/ - -extern "C" { - - JNIEXPORT bool start_launcher(int argc, TCHAR* argv[]) { - bool result = false; - bool parentProcess = true; - - // Platform must be initialize first. - Platform& platform = Platform::GetInstance(); - - try { - for (int index = 0; index < argc; index++) { - TString argument = argv[index]; - - if (argument == _T("-Xappcds:generatecache")) { - platform.SetAppCDSState(cdsGenCache); - } - else if (argument == _T("-Xappcds:off")) { - platform.SetAppCDSState(cdsDisabled); - } - else if (argument == _T("-Xapp:child")) { - parentProcess = false; - } - } - - // Package must be initialized after Platform is fully initialized. - Package& package = Package::GetInstance(); - Macros::Initialize(); - package.SetCommandLineArguments(argc, argv); - platform.SetCurrentDirectory(package.GetPackageAppDirectory()); - - switch (platform.GetAppCDSState()) { - case cdsDisabled: - case cdsUninitialized: - case cdsEnabled: { - break; - } - - case cdsGenCache: { - TString cacheDirectory = package.GetAppCDSCacheDirectory(); - - if (FilePath::DirectoryExists(cacheDirectory) == false) { - FilePath::CreateDirectory(cacheDirectory, true); - } else { - TString cacheFileName = - package.GetAppCDSCacheFileName(); - if (FilePath::FileExists(cacheFileName) == true) { - FilePath::DeleteFile(cacheFileName); - } - } - - break; - } - - case cdsAuto: { - TString cacheFileName = package.GetAppCDSCacheFileName(); - - if (parentProcess == true && - FilePath::FileExists(cacheFileName) == false) { - AutoFreePtr process = platform.CreateProcess(); - std::vector args; - args.push_back(_T("-Xappcds:generatecache")); - args.push_back(_T("-Xapp:child")); - process->Execute( - platform.GetModuleFileName(), args, true); - - if (FilePath::FileExists(cacheFileName) == false) { - // Cache does not exist after trying to generate it, - // so run without cache. - platform.SetAppCDSState(cdsDisabled); - package.Clear(); - package.Initialize(); - } - } - - break; - } - } - - // Validation - switch (platform.GetAppCDSState()) { - case cdsDisabled: - case cdsGenCache: { - // Do nothing. - break; - } - - case cdsEnabled: - case cdsAuto: { - TString cacheFileName = - package.GetAppCDSCacheFileName(); - - if (FilePath::FileExists(cacheFileName) == false) { - Messages& messages = Messages::GetInstance(); - TString message = PlatformString::Format( - messages.GetMessage( - APPCDS_CACHE_FILE_NOT_FOUND), - cacheFileName.data()); - throw Exception(message); - } - break; - } - - case cdsUninitialized: { - platform.ShowMessage(_T("Internal Error")); - break; - } - } - - // Run App - result = RunVM(); - } catch (Exception &e) { - platform.ShowMessage(e.GetMessage()); - } - - return result; - } - - JNIEXPORT void stop_launcher() { - } -} --- /dev/null 2019-11-18 21:08:07.000000000 -0500 +++ new/src/jdk.incubator.jpackage/share/native/libapplauncher/main.cpp 2019-11-18 21:08:02.501168300 -0500 @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Platform.h" +#include "PlatformString.h" +#include "FilePath.h" +#include "PropertyFile.h" +#include "JavaVirtualMachine.h" +#include "Package.h" +#include "Macros.h" +#include "Messages.h" + +#include +#include +#include + +/* +This is the app launcher program for application packaging on Windows, Mac, + and Linux. + +Basic approach: + - Launcher (jpackageapplauncher) is executable that loads + applauncher.dll/libapplauncher.dylib/libapplauncher.so + and calls start_launcher below. + - Reads app/package.cfg or Info.plist or app/.cfg for application + launch configuration (package.cfg is property file). + - Load Java with requested Java settings (bundled client Java if availble, + server or installed Java otherwise). + - Wait for Java to exit and then exit from Main + - To debug application by passing command line argument. + - Application folder is added to the library path (so LoadLibrary()) works. + +Limitations and future work: + - Running Java code in primordial thread may cause problems + (example: can not use custom stack size). + Solution used by java launcher is to create a new thread to invoke Java. + See CR 6316197 for more information. +*/ + +extern "C" { + + JNIEXPORT bool start_launcher(int argc, TCHAR* argv[]) { + bool result = false; + bool parentProcess = true; + + // Platform must be initialize first. + Platform& platform = Platform::GetInstance(); + + try { + for (int index = 0; index < argc; index++) { + TString argument = argv[index]; + + if (argument == _T("-Xappcds:generatecache")) { + platform.SetAppCDSState(cdsGenCache); + } + else if (argument == _T("-Xappcds:off")) { + platform.SetAppCDSState(cdsDisabled); + } + else if (argument == _T("-Xapp:child")) { + parentProcess = false; + } + } + + // Package must be initialized after Platform is fully initialized. + Package& package = Package::GetInstance(); + Macros::Initialize(); + package.SetCommandLineArguments(argc, argv); + + switch (platform.GetAppCDSState()) { + case cdsDisabled: + case cdsUninitialized: + case cdsEnabled: { + break; + } + + case cdsGenCache: { + TString cacheDirectory = package.GetAppCDSCacheDirectory(); + + if (FilePath::DirectoryExists(cacheDirectory) == false) { + FilePath::CreateDirectory(cacheDirectory, true); + } else { + TString cacheFileName = + package.GetAppCDSCacheFileName(); + if (FilePath::FileExists(cacheFileName) == true) { + FilePath::DeleteFile(cacheFileName); + } + } + + break; + } + + case cdsAuto: { + TString cacheFileName = package.GetAppCDSCacheFileName(); + + if (parentProcess == true && + FilePath::FileExists(cacheFileName) == false) { + AutoFreePtr process = platform.CreateProcess(); + std::vector args; + args.push_back(_T("-Xappcds:generatecache")); + args.push_back(_T("-Xapp:child")); + process->Execute( + platform.GetModuleFileName(), args, true); + + if (FilePath::FileExists(cacheFileName) == false) { + // Cache does not exist after trying to generate it, + // so run without cache. + platform.SetAppCDSState(cdsDisabled); + package.Clear(); + package.Initialize(); + } + } + + break; + } + } + + // Validation + switch (platform.GetAppCDSState()) { + case cdsDisabled: + case cdsGenCache: { + // Do nothing. + break; + } + + case cdsEnabled: + case cdsAuto: { + TString cacheFileName = + package.GetAppCDSCacheFileName(); + + if (FilePath::FileExists(cacheFileName) == false) { + Messages& messages = Messages::GetInstance(); + TString message = PlatformString::Format( + messages.GetMessage( + APPCDS_CACHE_FILE_NOT_FOUND), + cacheFileName.data()); + throw Exception(message); + } + break; + } + + case cdsUninitialized: { + platform.ShowMessage(_T("Internal Error")); + break; + } + } + + // Run App + result = RunVM(); + } catch (Exception &e) { + platform.ShowMessage(e.GetMessage()); + } + + return result; + } + + JNIEXPORT void stop_launcher() { + } +} --- old/src/jdk.jpackage/unix/native/libapplauncher/FileAttribute.h 2019-11-18 21:08:26.215088000 -0500 +++ /dev/null 2019-11-18 21:08:27.000000000 -0500 @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef FILEATTRIBUTE_H -#define FILEATTRIBUTE_H - -enum FileAttribute { - faBlockSpecial, - faCharacterSpecial, - faFIFOSpecial, - faNormal, - faDirectory, - faSymbolicLink, - faSocket, - - // Owner - faReadOnly, - faWriteOnly, - faReadWrite, - faExecute, - - // Group - faGroupReadOnly, - faGroupWriteOnly, - faGroupReadWrite, - faGroupExecute, - - // Others - faOthersReadOnly, - faOthersWriteOnly, - faOthersReadWrite, - faOthersExecute, - - faHidden -}; - -#endif // FILEATTRIBUTE_H --- /dev/null 2019-11-18 21:08:27.000000000 -0500 +++ new/src/jdk.incubator.jpackage/unix/native/libapplauncher/FileAttribute.h 2019-11-18 21:08:22.921970500 -0500 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef FILEATTRIBUTE_H +#define FILEATTRIBUTE_H + +enum FileAttribute { + faBlockSpecial, + faCharacterSpecial, + faFIFOSpecial, + faNormal, + faDirectory, + faSymbolicLink, + faSocket, + + // Owner + faReadOnly, + faWriteOnly, + faReadWrite, + faExecute, + + // Group + faGroupReadOnly, + faGroupWriteOnly, + faGroupReadWrite, + faGroupExecute, + + // Others + faOthersReadOnly, + faOthersWriteOnly, + faOthersReadWrite, + faOthersExecute, + + faHidden +}; + +#endif // FILEATTRIBUTE_H --- old/src/jdk.jpackage/unix/native/libapplauncher/FileAttributes.cpp 2019-11-18 21:08:36.737552300 -0500 +++ /dev/null 2019-11-18 21:08:38.000000000 -0500 @@ -1,331 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "FileAttributes.h" - -#include -#include -#include - -FileAttributes::FileAttributes(const TString FileName, bool FollowLink) { - FFileName = FileName; - FFollowLink = FollowLink; - ReadAttributes(); -} - -bool FileAttributes::WriteAttributes() { - bool result = false; - - mode_t attributes = 0; - - for (std::vector::const_iterator iterator = - FAttributes.begin(); - iterator != FAttributes.end(); iterator++) { - switch (*iterator) { - case faBlockSpecial: - { - attributes |= S_IFBLK; - break; - } - case faCharacterSpecial: - { - attributes |= S_IFCHR; - break; - } - case faFIFOSpecial: - { - attributes |= S_IFIFO; - break; - } - case faNormal: - { - attributes |= S_IFREG; - break; - } - case faDirectory: - { - attributes |= S_IFDIR; - break; - } - case faSymbolicLink: - { - attributes |= S_IFLNK; - break; - } - case faSocket: - { - attributes |= S_IFSOCK; - break; - } - - // Owner - case faReadOnly: - { - attributes |= S_IRUSR; - break; - } - case faWriteOnly: - { - attributes |= S_IWUSR; - break; - } - case faReadWrite: - { - attributes |= S_IRUSR; - attributes |= S_IWUSR; - break; - } - case faExecute: - { - attributes |= S_IXUSR; - break; - } - - // Group - case faGroupReadOnly: - { - attributes |= S_IRGRP; - break; - } - case faGroupWriteOnly: - { - attributes |= S_IWGRP; - break; - } - case faGroupReadWrite: - { - attributes |= S_IRGRP; - attributes |= S_IWGRP; - break; - } - case faGroupExecute: - { - attributes |= S_IXGRP; - break; - } - - // Others - case faOthersReadOnly: - { - attributes |= S_IROTH; - break; - } - case faOthersWriteOnly: - { - attributes |= S_IWOTH; - break; - } - case faOthersReadWrite: - { - attributes |= S_IROTH; - attributes |= S_IWOTH; - break; - } - case faOthersExecute: - { - attributes |= S_IXOTH; - break; - } - default: - break; - } - } - - if (chmod(FFileName.data(), attributes) == 0) { - result = true; - } - - return result; -} - -#define S_ISRUSR(m) (((m) & S_IRWXU) == S_IRUSR) -#define S_ISWUSR(m) (((m) & S_IRWXU) == S_IWUSR) -#define S_ISXUSR(m) (((m) & S_IRWXU) == S_IXUSR) - -#define S_ISRGRP(m) (((m) & S_IRWXG) == S_IRGRP) -#define S_ISWGRP(m) (((m) & S_IRWXG) == S_IWGRP) -#define S_ISXGRP(m) (((m) & S_IRWXG) == S_IXGRP) - -#define S_ISROTH(m) (((m) & S_IRWXO) == S_IROTH) -#define S_ISWOTH(m) (((m) & S_IRWXO) == S_IWOTH) -#define S_ISXOTH(m) (((m) & S_IRWXO) == S_IXOTH) - -bool FileAttributes::ReadAttributes() { - bool result = false; - - struct stat status; - - if (stat(StringToFileSystemString(FFileName), &status) == 0) { - result = true; - - if (S_ISBLK(status.st_mode) != 0) { - FAttributes.push_back(faBlockSpecial); - } - if (S_ISCHR(status.st_mode) != 0) { - FAttributes.push_back(faCharacterSpecial); - } - if (S_ISFIFO(status.st_mode) != 0) { - FAttributes.push_back(faFIFOSpecial); - } - if (S_ISREG(status.st_mode) != 0) { - FAttributes.push_back(faNormal); - } - if (S_ISDIR(status.st_mode) != 0) { - FAttributes.push_back(faDirectory); - } - if (S_ISLNK(status.st_mode) != 0) { - FAttributes.push_back(faSymbolicLink); - } - if (S_ISSOCK(status.st_mode) != 0) { - FAttributes.push_back(faSocket); - } - - // Owner - if (S_ISRUSR(status.st_mode) != 0) { - if (S_ISWUSR(status.st_mode) != 0) { - FAttributes.push_back(faReadWrite); - } else { - FAttributes.push_back(faReadOnly); - } - } else if (S_ISWUSR(status.st_mode) != 0) { - FAttributes.push_back(faWriteOnly); - } - - if (S_ISXUSR(status.st_mode) != 0) { - FAttributes.push_back(faExecute); - } - - // Group - if (S_ISRGRP(status.st_mode) != 0) { - if (S_ISWGRP(status.st_mode) != 0) { - FAttributes.push_back(faGroupReadWrite); - } else { - FAttributes.push_back(faGroupReadOnly); - } - } else if (S_ISWGRP(status.st_mode) != 0) { - FAttributes.push_back(faGroupWriteOnly); - } - - if (S_ISXGRP(status.st_mode) != 0) { - FAttributes.push_back(faGroupExecute); - } - - - // Others - if (S_ISROTH(status.st_mode) != 0) { - if (S_ISWOTH(status.st_mode) != 0) { - FAttributes.push_back(faOthersReadWrite); - } else { - FAttributes.push_back(faOthersReadOnly); - } - } else if (S_ISWOTH(status.st_mode) != 0) { - FAttributes.push_back(faOthersWriteOnly); - } - - if (S_ISXOTH(status.st_mode) != 0) { - FAttributes.push_back(faOthersExecute); - } - - if (FFileName.size() > 0 && FFileName[0] == '.') { - FAttributes.push_back(faHidden); - } - } - - return result; -} - -bool FileAttributes::Valid(const FileAttribute Value) { - bool result = false; - - switch (Value) { - case faReadWrite: - case faWriteOnly: - case faExecute: - - case faGroupReadWrite: - case faGroupWriteOnly: - case faGroupReadOnly: - case faGroupExecute: - - case faOthersReadWrite: - case faOthersWriteOnly: - case faOthersReadOnly: - case faOthersExecute: - - case faReadOnly: - result = true; - break; - - default: - break; - } - - return result; -} - -void FileAttributes::Append(FileAttribute Value) { - if (Valid(Value) == true) { - if ((Value == faReadOnly && Contains(faWriteOnly) == true) || - (Value == faWriteOnly && Contains(faReadOnly) == true)) { - Value = faReadWrite; - } - - FAttributes.push_back(Value); - WriteAttributes(); - } -} - -bool FileAttributes::Contains(FileAttribute Value) { - bool result = false; - - std::vector::const_iterator iterator = - std::find(FAttributes.begin(), FAttributes.end(), Value); - - if (iterator != FAttributes.end()) { - result = true; - } - - return result; -} - -void FileAttributes::Remove(FileAttribute Value) { - if (Valid(Value) == true) { - if (Value == faReadOnly && Contains(faReadWrite) == true) { - Append(faWriteOnly); - Remove(faReadWrite); - } else if (Value == faWriteOnly && Contains(faReadWrite) == true) { - Append(faReadOnly); - Remove(faReadWrite); - } - - std::vector::iterator iterator = - std::find(FAttributes.begin(), FAttributes.end(), Value); - - if (iterator != FAttributes.end()) { - FAttributes.erase(iterator); - WriteAttributes(); - } - } -} --- /dev/null 2019-11-18 21:08:38.000000000 -0500 +++ new/src/jdk.incubator.jpackage/unix/native/libapplauncher/FileAttributes.cpp 2019-11-18 21:08:33.216123000 -0500 @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "FileAttributes.h" + +#include +#include +#include + +FileAttributes::FileAttributes(const TString FileName, bool FollowLink) { + FFileName = FileName; + FFollowLink = FollowLink; + ReadAttributes(); +} + +bool FileAttributes::WriteAttributes() { + bool result = false; + + mode_t attributes = 0; + + for (std::vector::const_iterator iterator = + FAttributes.begin(); + iterator != FAttributes.end(); iterator++) { + switch (*iterator) { + case faBlockSpecial: + { + attributes |= S_IFBLK; + break; + } + case faCharacterSpecial: + { + attributes |= S_IFCHR; + break; + } + case faFIFOSpecial: + { + attributes |= S_IFIFO; + break; + } + case faNormal: + { + attributes |= S_IFREG; + break; + } + case faDirectory: + { + attributes |= S_IFDIR; + break; + } + case faSymbolicLink: + { + attributes |= S_IFLNK; + break; + } + case faSocket: + { + attributes |= S_IFSOCK; + break; + } + + // Owner + case faReadOnly: + { + attributes |= S_IRUSR; + break; + } + case faWriteOnly: + { + attributes |= S_IWUSR; + break; + } + case faReadWrite: + { + attributes |= S_IRUSR; + attributes |= S_IWUSR; + break; + } + case faExecute: + { + attributes |= S_IXUSR; + break; + } + + // Group + case faGroupReadOnly: + { + attributes |= S_IRGRP; + break; + } + case faGroupWriteOnly: + { + attributes |= S_IWGRP; + break; + } + case faGroupReadWrite: + { + attributes |= S_IRGRP; + attributes |= S_IWGRP; + break; + } + case faGroupExecute: + { + attributes |= S_IXGRP; + break; + } + + // Others + case faOthersReadOnly: + { + attributes |= S_IROTH; + break; + } + case faOthersWriteOnly: + { + attributes |= S_IWOTH; + break; + } + case faOthersReadWrite: + { + attributes |= S_IROTH; + attributes |= S_IWOTH; + break; + } + case faOthersExecute: + { + attributes |= S_IXOTH; + break; + } + default: + break; + } + } + + if (chmod(FFileName.data(), attributes) == 0) { + result = true; + } + + return result; +} + +#define S_ISRUSR(m) (((m) & S_IRWXU) == S_IRUSR) +#define S_ISWUSR(m) (((m) & S_IRWXU) == S_IWUSR) +#define S_ISXUSR(m) (((m) & S_IRWXU) == S_IXUSR) + +#define S_ISRGRP(m) (((m) & S_IRWXG) == S_IRGRP) +#define S_ISWGRP(m) (((m) & S_IRWXG) == S_IWGRP) +#define S_ISXGRP(m) (((m) & S_IRWXG) == S_IXGRP) + +#define S_ISROTH(m) (((m) & S_IRWXO) == S_IROTH) +#define S_ISWOTH(m) (((m) & S_IRWXO) == S_IWOTH) +#define S_ISXOTH(m) (((m) & S_IRWXO) == S_IXOTH) + +bool FileAttributes::ReadAttributes() { + bool result = false; + + struct stat status; + + if (stat(StringToFileSystemString(FFileName), &status) == 0) { + result = true; + + if (S_ISBLK(status.st_mode) != 0) { + FAttributes.push_back(faBlockSpecial); + } + if (S_ISCHR(status.st_mode) != 0) { + FAttributes.push_back(faCharacterSpecial); + } + if (S_ISFIFO(status.st_mode) != 0) { + FAttributes.push_back(faFIFOSpecial); + } + if (S_ISREG(status.st_mode) != 0) { + FAttributes.push_back(faNormal); + } + if (S_ISDIR(status.st_mode) != 0) { + FAttributes.push_back(faDirectory); + } + if (S_ISLNK(status.st_mode) != 0) { + FAttributes.push_back(faSymbolicLink); + } + if (S_ISSOCK(status.st_mode) != 0) { + FAttributes.push_back(faSocket); + } + + // Owner + if (S_ISRUSR(status.st_mode) != 0) { + if (S_ISWUSR(status.st_mode) != 0) { + FAttributes.push_back(faReadWrite); + } else { + FAttributes.push_back(faReadOnly); + } + } else if (S_ISWUSR(status.st_mode) != 0) { + FAttributes.push_back(faWriteOnly); + } + + if (S_ISXUSR(status.st_mode) != 0) { + FAttributes.push_back(faExecute); + } + + // Group + if (S_ISRGRP(status.st_mode) != 0) { + if (S_ISWGRP(status.st_mode) != 0) { + FAttributes.push_back(faGroupReadWrite); + } else { + FAttributes.push_back(faGroupReadOnly); + } + } else if (S_ISWGRP(status.st_mode) != 0) { + FAttributes.push_back(faGroupWriteOnly); + } + + if (S_ISXGRP(status.st_mode) != 0) { + FAttributes.push_back(faGroupExecute); + } + + + // Others + if (S_ISROTH(status.st_mode) != 0) { + if (S_ISWOTH(status.st_mode) != 0) { + FAttributes.push_back(faOthersReadWrite); + } else { + FAttributes.push_back(faOthersReadOnly); + } + } else if (S_ISWOTH(status.st_mode) != 0) { + FAttributes.push_back(faOthersWriteOnly); + } + + if (S_ISXOTH(status.st_mode) != 0) { + FAttributes.push_back(faOthersExecute); + } + + if (FFileName.size() > 0 && FFileName[0] == '.') { + FAttributes.push_back(faHidden); + } + } + + return result; +} + +bool FileAttributes::Valid(const FileAttribute Value) { + bool result = false; + + switch (Value) { + case faReadWrite: + case faWriteOnly: + case faExecute: + + case faGroupReadWrite: + case faGroupWriteOnly: + case faGroupReadOnly: + case faGroupExecute: + + case faOthersReadWrite: + case faOthersWriteOnly: + case faOthersReadOnly: + case faOthersExecute: + + case faReadOnly: + result = true; + break; + + default: + break; + } + + return result; +} + +void FileAttributes::Append(FileAttribute Value) { + if (Valid(Value) == true) { + if ((Value == faReadOnly && Contains(faWriteOnly) == true) || + (Value == faWriteOnly && Contains(faReadOnly) == true)) { + Value = faReadWrite; + } + + FAttributes.push_back(Value); + WriteAttributes(); + } +} + +bool FileAttributes::Contains(FileAttribute Value) { + bool result = false; + + std::vector::const_iterator iterator = + std::find(FAttributes.begin(), FAttributes.end(), Value); + + if (iterator != FAttributes.end()) { + result = true; + } + + return result; +} + +void FileAttributes::Remove(FileAttribute Value) { + if (Valid(Value) == true) { + if (Value == faReadOnly && Contains(faReadWrite) == true) { + Append(faWriteOnly); + Remove(faReadWrite); + } else if (Value == faWriteOnly && Contains(faReadWrite) == true) { + Append(faReadOnly); + Remove(faReadWrite); + } + + std::vector::iterator iterator = + std::find(FAttributes.begin(), FAttributes.end(), Value); + + if (iterator != FAttributes.end()) { + FAttributes.erase(iterator); + WriteAttributes(); + } + } +} --- old/src/jdk.jpackage/unix/native/libapplauncher/FilePath.cpp 2019-11-18 21:08:47.095503000 -0500 +++ /dev/null 2019-11-18 21:08:48.000000000 -0500 @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "PlatformDefs.h" -#include "FilePath.h" - -#include -#include -#include - -bool FilePath::FileExists(const TString FileName) { - bool result = false; - struct stat buf; - - if ((stat(StringToFileSystemString(FileName), &buf) == 0) && - (S_ISREG(buf.st_mode) != 0)) { - result = true; - } - - return result; -} - -bool FilePath::DirectoryExists(const TString DirectoryName) { - bool result = false; - - struct stat buf; - - if ((stat(StringToFileSystemString(DirectoryName), &buf) == 0) && - (S_ISDIR(buf.st_mode) != 0)) { - result = true; - } - - return result; -} - -bool FilePath::DeleteFile(const TString FileName) { - bool result = false; - - if (FileExists(FileName) == true) { - if (unlink(StringToFileSystemString(FileName)) == 0) { - result = true; - } - } - - return result; -} - -bool FilePath::DeleteDirectory(const TString DirectoryName) { - bool result = false; - - if (DirectoryExists(DirectoryName) == true) { - if (unlink(StringToFileSystemString(DirectoryName)) == 0) { - result = true; - } - } - - return result; -} - -TString FilePath::IncludeTrailingSeparator(const TString value) { - TString result = value; - - if (value.size() > 0) { - TString::iterator i = result.end(); - i--; - - if (*i != TRAILING_PATHSEPARATOR) { - result += TRAILING_PATHSEPARATOR; - } - } - - return result; -} - -TString FilePath::IncludeTrailingSeparator(const char* value) { - TString lvalue = PlatformString(value).toString(); - return IncludeTrailingSeparator(lvalue); -} - -TString FilePath::IncludeTrailingSeparator(const wchar_t* value) { - TString lvalue = PlatformString(value).toString(); - return IncludeTrailingSeparator(lvalue); -} - -TString FilePath::ExtractFilePath(TString Path) { - return dirname(StringToFileSystemString(Path)); -} - -TString FilePath::ExtractFileExt(TString Path) { - TString result; - size_t dot = Path.find_last_of('.'); - - if (dot != TString::npos) { - result = Path.substr(dot, Path.size() - dot); - } - - return result; -} - -TString FilePath::ExtractFileName(TString Path) { - return basename(StringToFileSystemString(Path)); -} - -TString FilePath::ChangeFileExt(TString Path, TString Extension) { - TString result; - size_t dot = Path.find_last_of('.'); - - if (dot != TString::npos) { - result = Path.substr(0, dot) + Extension; - } - - if (result.empty() == true) { - result = Path; - } - - return result; -} - -TString FilePath::FixPathForPlatform(TString Path) { - TString result = Path; - std::replace(result.begin(), result.end(), - BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR); - return result; -} - -TString FilePath::FixPathSeparatorForPlatform(TString Path) { - TString result = Path; - std::replace(result.begin(), result.end(), - BAD_PATH_SEPARATOR, PATH_SEPARATOR); - return result; -} - -TString FilePath::PathSeparator() { - TString result; - result = PATH_SEPARATOR; - return result; -} - -bool FilePath::CreateDirectory(TString Path, bool ownerOnly) { - bool result = false; - - std::list paths; - TString lpath = Path; - - while (lpath.empty() == false && DirectoryExists(lpath) == false) { - paths.push_front(lpath); - lpath = ExtractFilePath(lpath); - } - - for (std::list::iterator iterator = paths.begin(); - iterator != paths.end(); iterator++) { - lpath = *iterator; - - mode_t mode = S_IRWXU; - if (!ownerOnly) { - mode |= S_IRWXG | S_IROTH | S_IXOTH; - } - if (mkdir(StringToFileSystemString(lpath), mode) == 0) { - result = true; - } else { - result = false; - break; - } - } - - return result; -} - -void FilePath::ChangePermissions(TString FileName, bool ownerOnly) { - mode_t mode = S_IRWXU; - if (!ownerOnly) { - mode |= S_IRWXG | S_IROTH | S_IXOTH; - } - chmod(FileName.data(), mode); -} --- /dev/null 2019-11-18 21:08:48.000000000 -0500 +++ new/src/jdk.incubator.jpackage/unix/native/libapplauncher/FilePath.cpp 2019-11-18 21:08:43.742872800 -0500 @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "PlatformDefs.h" +#include "FilePath.h" + +#include +#include +#include + +bool FilePath::FileExists(const TString FileName) { + bool result = false; + struct stat buf; + + if ((stat(StringToFileSystemString(FileName), &buf) == 0) && + (S_ISREG(buf.st_mode) != 0)) { + result = true; + } + + return result; +} + +bool FilePath::DirectoryExists(const TString DirectoryName) { + bool result = false; + + struct stat buf; + + if ((stat(StringToFileSystemString(DirectoryName), &buf) == 0) && + (S_ISDIR(buf.st_mode) != 0)) { + result = true; + } + + return result; +} + +bool FilePath::DeleteFile(const TString FileName) { + bool result = false; + + if (FileExists(FileName) == true) { + if (unlink(StringToFileSystemString(FileName)) == 0) { + result = true; + } + } + + return result; +} + +bool FilePath::DeleteDirectory(const TString DirectoryName) { + bool result = false; + + if (DirectoryExists(DirectoryName) == true) { + if (unlink(StringToFileSystemString(DirectoryName)) == 0) { + result = true; + } + } + + return result; +} + +TString FilePath::IncludeTrailingSeparator(const TString value) { + TString result = value; + + if (value.size() > 0) { + TString::iterator i = result.end(); + i--; + + if (*i != TRAILING_PATHSEPARATOR) { + result += TRAILING_PATHSEPARATOR; + } + } + + return result; +} + +TString FilePath::IncludeTrailingSeparator(const char* value) { + TString lvalue = PlatformString(value).toString(); + return IncludeTrailingSeparator(lvalue); +} + +TString FilePath::IncludeTrailingSeparator(const wchar_t* value) { + TString lvalue = PlatformString(value).toString(); + return IncludeTrailingSeparator(lvalue); +} + +TString FilePath::ExtractFilePath(TString Path) { + return dirname(StringToFileSystemString(Path)); +} + +TString FilePath::ExtractFileExt(TString Path) { + TString result; + size_t dot = Path.find_last_of('.'); + + if (dot != TString::npos) { + result = Path.substr(dot, Path.size() - dot); + } + + return result; +} + +TString FilePath::ExtractFileName(TString Path) { + return basename(StringToFileSystemString(Path)); +} + +TString FilePath::ChangeFileExt(TString Path, TString Extension) { + TString result; + size_t dot = Path.find_last_of('.'); + + if (dot != TString::npos) { + result = Path.substr(0, dot) + Extension; + } + + if (result.empty() == true) { + result = Path; + } + + return result; +} + +TString FilePath::FixPathForPlatform(TString Path) { + TString result = Path; + std::replace(result.begin(), result.end(), + BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR); + return result; +} + +TString FilePath::FixPathSeparatorForPlatform(TString Path) { + TString result = Path; + std::replace(result.begin(), result.end(), + BAD_PATH_SEPARATOR, PATH_SEPARATOR); + return result; +} + +TString FilePath::PathSeparator() { + TString result; + result = PATH_SEPARATOR; + return result; +} + +bool FilePath::CreateDirectory(TString Path, bool ownerOnly) { + bool result = false; + + std::list paths; + TString lpath = Path; + + while (lpath.empty() == false && DirectoryExists(lpath) == false) { + paths.push_front(lpath); + lpath = ExtractFilePath(lpath); + } + + for (std::list::iterator iterator = paths.begin(); + iterator != paths.end(); iterator++) { + lpath = *iterator; + + mode_t mode = S_IRWXU; + if (!ownerOnly) { + mode |= S_IRWXG | S_IROTH | S_IXOTH; + } + if (mkdir(StringToFileSystemString(lpath), mode) == 0) { + result = true; + } else { + result = false; + break; + } + } + + return result; +} + +void FilePath::ChangePermissions(TString FileName, bool ownerOnly) { + mode_t mode = S_IRWXU; + if (!ownerOnly) { + mode |= S_IRWXG | S_IROTH | S_IXOTH; + } + chmod(FileName.data(), mode); +} --- old/src/jdk.jpackage/unix/native/libapplauncher/PosixPlatform.cpp 2019-11-18 21:08:57.522591600 -0500 +++ /dev/null 2019-11-18 21:08:58.000000000 -0500 @@ -1,320 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "PosixPlatform.h" - -#include "PlatformString.h" -#include "FilePath.h" -#include "Helpers.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -PosixPlatform::PosixPlatform(void) { -} - -PosixPlatform::~PosixPlatform(void) { -} - -TString PosixPlatform::GetTempDirectory() { - struct passwd* pw = getpwuid(getuid()); - TString homedir(pw->pw_dir); - homedir += getTmpDirString(); - if (!FilePath::DirectoryExists(homedir)) { - if (!FilePath::CreateDirectory(homedir, false)) { - homedir.clear(); - } - } - - return homedir; -} - -TString PosixPlatform::fixName(const TString& name) { - TString fixedName(name); - const TString chars("?:*<>/\\"); - for (TString::const_iterator it = chars.begin(); it != chars.end(); it++) { - fixedName.erase(std::remove(fixedName.begin(), - fixedName.end(), *it), fixedName.end()); - } - return fixedName; -} - -MessageResponse PosixPlatform::ShowResponseMessage(TString title, - TString description) { - MessageResponse result = mrCancel; - - printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(), - PlatformString(description).toPlatformString()); - fflush(stdout); - - std::string input; - std::cin >> input; - - if (input == "Y") { - result = mrOK; - } - - return result; -} - -void PosixPlatform::SetCurrentDirectory(TString Value) { - chdir(StringToFileSystemString(Value)); -} - -Module PosixPlatform::LoadLibrary(TString FileName) { - return dlopen(StringToFileSystemString(FileName), RTLD_LAZY); -} - -void PosixPlatform::FreeLibrary(Module AModule) { - dlclose(AModule); -} - -Procedure PosixPlatform::GetProcAddress(Module AModule, - std::string MethodName) { - return dlsym(AModule, PlatformString(MethodName)); -} - -Process* PosixPlatform::CreateProcess() { - return new PosixProcess(); -} - -void PosixPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) { -} - -void Platform::CopyString(char *Destination, - size_t NumberOfElements, const char *Source) { - strncpy(Destination, Source, NumberOfElements); - - if (NumberOfElements > 0) { - Destination[NumberOfElements - 1] = '\0'; - } -} - -void Platform::CopyString(wchar_t *Destination, - size_t NumberOfElements, const wchar_t *Source) { - wcsncpy(Destination, Source, NumberOfElements); - - if (NumberOfElements > 0) { - Destination[NumberOfElements - 1] = '\0'; - } -} - -// Owner must free the return value. - -MultibyteString Platform::WideStringToMultibyteString( - const wchar_t* value) { - MultibyteString result; - size_t count = 0; - - if (value == NULL) { - return result; - } - - count = wcstombs(NULL, value, 0); - if (count > 0) { - result.data = new char[count + 1]; - result.data[count] = '\0'; - result.length = count; - wcstombs(result.data, value, count); - } - - return result; -} - -// Owner must free the return value. - -WideString Platform::MultibyteStringToWideString(const char* value) { - WideString result; - size_t count = 0; - - if (value == NULL) { - return result; - } - - count = mbstowcs(NULL, value, 0); - if (count > 0) { - result.data = new wchar_t[count + 1]; - result.data[count] = '\0'; - result.length = count; - mbstowcs(result.data, value, count); - } - - return result; -} - -void PosixPlatform::InitStreamLocale(wios *stream) { - // Nothing to do for POSIX platforms. -} - -PosixProcess::PosixProcess() : Process() { - FChildPID = 0; - FRunning = false; - FOutputHandle = 0; - FInputHandle = 0; -} - -PosixProcess::~PosixProcess() { - Terminate(); -} - -bool PosixProcess::ReadOutput() { - bool result = false; - - if (FOutputHandle != 0 && IsRunning() == true) { - char buffer[4096] = {0}; - - ssize_t count = read(FOutputHandle, buffer, sizeof (buffer)); - - if (count == -1) { - if (errno == EINTR) { - // continue; - } else { - perror("read"); - exit(1); - } - } else if (count == 0) { - // break; - } else { - if (buffer[count - 1] == EOF) { - buffer[count - 1] = '\0'; - } - - std::list output = Helpers::StringToArray(buffer); - FOutput.splice(FOutput.end(), output, output.begin(), output.end()); - result = true; - } - } - - return false; -} - -bool PosixProcess::IsRunning() { - bool result = false; - - if (kill(FChildPID, 0) == 0) { - result = true; - } - - return result; -} - -bool PosixProcess::Terminate() { - bool result = false; - - if (IsRunning() == true && FRunning == true) { - FRunning = false; - Cleanup(); - int status = kill(FChildPID, SIGTERM); - - if (status == 0) { - result = true; - } else { -#ifdef DEBUG - if (errno == EINVAL) { - printf("Kill error: The value of the sig argument is an invalid or unsupported signal number."); - } else if (errno == EPERM) { - printf("Kill error: The process does not have permission to send the signal to any receiving process."); - } else if (errno == ESRCH) { - printf("Kill error: No process or process group can be found corresponding to that specified by pid."); - } -#endif // DEBUG - if (IsRunning() == true) { - status = kill(FChildPID, SIGKILL); - - if (status == 0) { - result = true; - } - } - } - } - - return result; -} - -bool PosixProcess::Wait() { - bool result = false; - - int status = 0; - pid_t wpid = 0; - - wpid = wait(&status); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - if (errno != EINTR) { - status = -1; - } - } - -#ifdef DEBUG - if (WIFEXITED(status)) { - printf("child exited, status=%d\n", WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - printf("child killed (signal %d)\n", WTERMSIG(status)); - } else if (WIFSTOPPED(status)) { - printf("child stopped (signal %d)\n", WSTOPSIG(status)); -#ifdef WIFCONTINUED // Not all implementations support this - } else if (WIFCONTINUED(status)) { - printf("child continued\n"); -#endif // WIFCONTINUED - } else { // Non-standard case -- may never happen - printf("Unexpected status (0x%x)\n", status); - } -#endif // DEBUG - - if (wpid != -1) { - result = true; - } - - return result; -} - -TProcessID PosixProcess::GetProcessID() { - return FChildPID; -} - -void PosixProcess::SetInput(TString Value) { - if (FInputHandle != 0) { - write(FInputHandle, Value.data(), Value.size()); - } -} - -std::list PosixProcess::GetOutput() { - ReadOutput(); - return Process::GetOutput(); -} --- /dev/null 2019-11-18 21:08:59.000000000 -0500 +++ new/src/jdk.incubator.jpackage/unix/native/libapplauncher/PosixPlatform.cpp 2019-11-18 21:08:54.283741100 -0500 @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "PosixPlatform.h" + +#include "PlatformString.h" +#include "FilePath.h" +#include "Helpers.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +PosixPlatform::PosixPlatform(void) { +} + +PosixPlatform::~PosixPlatform(void) { +} + +TString PosixPlatform::GetTempDirectory() { + struct passwd* pw = getpwuid(getuid()); + TString homedir(pw->pw_dir); + homedir += getTmpDirString(); + if (!FilePath::DirectoryExists(homedir)) { + if (!FilePath::CreateDirectory(homedir, false)) { + homedir.clear(); + } + } + + return homedir; +} + +TString PosixPlatform::fixName(const TString& name) { + TString fixedName(name); + const TString chars("?:*<>/\\"); + for (TString::const_iterator it = chars.begin(); it != chars.end(); it++) { + fixedName.erase(std::remove(fixedName.begin(), + fixedName.end(), *it), fixedName.end()); + } + return fixedName; +} + +MessageResponse PosixPlatform::ShowResponseMessage(TString title, + TString description) { + MessageResponse result = mrCancel; + + printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(), + PlatformString(description).toPlatformString()); + fflush(stdout); + + std::string input; + std::cin >> input; + + if (input == "Y") { + result = mrOK; + } + + return result; +} + +Module PosixPlatform::LoadLibrary(TString FileName) { + return dlopen(StringToFileSystemString(FileName), RTLD_LAZY); +} + +void PosixPlatform::FreeLibrary(Module AModule) { + dlclose(AModule); +} + +Procedure PosixPlatform::GetProcAddress(Module AModule, + std::string MethodName) { + return dlsym(AModule, PlatformString(MethodName)); +} + +Process* PosixPlatform::CreateProcess() { + return new PosixProcess(); +} + +void PosixPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) { +} + +void Platform::CopyString(char *Destination, + size_t NumberOfElements, const char *Source) { + strncpy(Destination, Source, NumberOfElements); + + if (NumberOfElements > 0) { + Destination[NumberOfElements - 1] = '\0'; + } +} + +void Platform::CopyString(wchar_t *Destination, + size_t NumberOfElements, const wchar_t *Source) { + wcsncpy(Destination, Source, NumberOfElements); + + if (NumberOfElements > 0) { + Destination[NumberOfElements - 1] = '\0'; + } +} + +// Owner must free the return value. + +MultibyteString Platform::WideStringToMultibyteString( + const wchar_t* value) { + MultibyteString result; + size_t count = 0; + + if (value == NULL) { + return result; + } + + count = wcstombs(NULL, value, 0); + if (count > 0) { + result.data = new char[count + 1]; + result.data[count] = '\0'; + result.length = count; + wcstombs(result.data, value, count); + } + + return result; +} + +// Owner must free the return value. + +WideString Platform::MultibyteStringToWideString(const char* value) { + WideString result; + size_t count = 0; + + if (value == NULL) { + return result; + } + + count = mbstowcs(NULL, value, 0); + if (count > 0) { + result.data = new wchar_t[count + 1]; + result.data[count] = '\0'; + result.length = count; + mbstowcs(result.data, value, count); + } + + return result; +} + +void PosixPlatform::InitStreamLocale(wios *stream) { + // Nothing to do for POSIX platforms. +} + +PosixProcess::PosixProcess() : Process() { + FChildPID = 0; + FRunning = false; + FOutputHandle = 0; + FInputHandle = 0; +} + +PosixProcess::~PosixProcess() { + Terminate(); +} + +bool PosixProcess::ReadOutput() { + bool result = false; + + if (FOutputHandle != 0 && IsRunning() == true) { + char buffer[4096] = {0}; + + ssize_t count = read(FOutputHandle, buffer, sizeof (buffer)); + + if (count == -1) { + if (errno == EINTR) { + // continue; + } else { + perror("read"); + exit(1); + } + } else if (count == 0) { + // break; + } else { + std::list output = Helpers::StringToArray(buffer); + FOutput.splice(FOutput.end(), output, output.begin(), output.end()); + result = true; + } + } + + return false; +} + +bool PosixProcess::IsRunning() { + bool result = false; + + if (kill(FChildPID, 0) == 0) { + result = true; + } + + return result; +} + +bool PosixProcess::Terminate() { + bool result = false; + + if (IsRunning() == true && FRunning == true) { + FRunning = false; + Cleanup(); + int status = kill(FChildPID, SIGTERM); + + if (status == 0) { + result = true; + } else { +#ifdef DEBUG + if (errno == EINVAL) { + printf("Kill error: The value of the sig argument is an invalid or unsupported signal number."); + } else if (errno == EPERM) { + printf("Kill error: The process does not have permission to send the signal to any receiving process."); + } else if (errno == ESRCH) { + printf("Kill error: No process or process group can be found corresponding to that specified by pid."); + } +#endif // DEBUG + if (IsRunning() == true) { + status = kill(FChildPID, SIGKILL); + + if (status == 0) { + result = true; + } + } + } + } + + return result; +} + +bool PosixProcess::Wait() { + bool result = false; + + int status = 0; + pid_t wpid = 0; + + wpid = wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + if (errno != EINTR) { + status = -1; + } + } + +#ifdef DEBUG + if (WIFEXITED(status)) { + printf("child exited, status=%d\n", WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + printf("child killed (signal %d)\n", WTERMSIG(status)); + } else if (WIFSTOPPED(status)) { + printf("child stopped (signal %d)\n", WSTOPSIG(status)); +#ifdef WIFCONTINUED // Not all implementations support this + } else if (WIFCONTINUED(status)) { + printf("child continued\n"); +#endif // WIFCONTINUED + } else { // Non-standard case -- may never happen + printf("Unexpected status (0x%x)\n", status); + } +#endif // DEBUG + + if (wpid != -1) { + result = true; + } + + return result; +} + +TProcessID PosixProcess::GetProcessID() { + return FChildPID; +} + +void PosixProcess::SetInput(TString Value) { + if (FInputHandle != 0) { + if (write(FInputHandle, Value.data(), Value.size()) < 0) { + throw Exception(_T("Internal Error - write failed")); + } + } +} + +std::list PosixProcess::GetOutput() { + ReadOutput(); + return Process::GetOutput(); +} --- old/src/jdk.jpackage/unix/native/libapplauncher/PosixPlatform.h 2019-11-18 21:09:18.305670600 -0500 +++ /dev/null 2019-11-18 21:09:19.000000000 -0500 @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef POSIXPLATFORM_H -#define POSIXPLATFORM_H - -#include "Platform.h" -#include - -class PosixPlatform : virtual public Platform { -protected: - - TString fixName(const TString& name); - - virtual TString getTmpDirString() = 0; - -public: - PosixPlatform(void); - virtual ~PosixPlatform(void); - -public: - virtual MessageResponse ShowResponseMessage(TString title, - TString description); - - virtual void SetCurrentDirectory(TString Value); - - virtual Module LoadLibrary(TString FileName); - virtual void FreeLibrary(Module AModule); - virtual Procedure GetProcAddress(Module AModule, std::string MethodName); - - virtual Process* CreateProcess(); - virtual TString GetTempDirectory(); - void InitStreamLocale(wios *stream); - void addPlatformDependencies(JavaLibrary *pJavaLibrary); -}; - -class PosixProcess : public Process { -private: - pid_t FChildPID; - sigset_t saveblock; - int FOutputHandle; - int FInputHandle; - struct sigaction savintr, savequit; - bool FRunning; - - void Cleanup(); - bool ReadOutput(); - -public: - PosixProcess(); - virtual ~PosixProcess(); - - virtual bool IsRunning(); - virtual bool Terminate(); - virtual bool Execute(const TString Application, - const std::vector Arguments, bool AWait = false); - virtual bool Wait(); - virtual TProcessID GetProcessID(); - virtual void SetInput(TString Value); - virtual std::list GetOutput(); -}; - -#endif // POSIXPLATFORM_H --- /dev/null 2019-11-18 21:09:19.000000000 -0500 +++ new/src/jdk.incubator.jpackage/unix/native/libapplauncher/PosixPlatform.h 2019-11-18 21:09:14.806201200 -0500 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef POSIXPLATFORM_H +#define POSIXPLATFORM_H + +#include "Platform.h" +#include + +class PosixPlatform : virtual public Platform { +protected: + + TString fixName(const TString& name); + + virtual TString getTmpDirString() = 0; + +public: + PosixPlatform(void); + virtual ~PosixPlatform(void); + +public: + virtual MessageResponse ShowResponseMessage(TString title, + TString description); + + virtual Module LoadLibrary(TString FileName); + virtual void FreeLibrary(Module AModule); + virtual Procedure GetProcAddress(Module AModule, std::string MethodName); + + virtual Process* CreateProcess(); + virtual TString GetTempDirectory(); + void InitStreamLocale(wios *stream); + void addPlatformDependencies(JavaLibrary *pJavaLibrary); +}; + +class PosixProcess : public Process { +private: + pid_t FChildPID; + sigset_t saveblock; + int FOutputHandle; + int FInputHandle; + struct sigaction savintr, savequit; + bool FRunning; + + void Cleanup(); + bool ReadOutput(); + +public: + PosixProcess(); + virtual ~PosixProcess(); + + virtual bool IsRunning(); + virtual bool Terminate(); + virtual bool Execute(const TString Application, + const std::vector Arguments, bool AWait = false); + virtual bool Wait(); + virtual TProcessID GetProcessID(); + virtual void SetInput(TString Value); + virtual std::list GetOutput(); +}; + +#endif // POSIXPLATFORM_H --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java 2019-11-18 21:09:38.640371700 -0500 +++ /dev/null 2019-11-18 21:09:40.000000000 -0500 @@ -1,253 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.nio.file.Path; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.ResourceBundle; - -import static jdk.jpackage.internal.WindowsBundlerParam.*; -import static jdk.jpackage.internal.WinMsiBundler.WIN_APP_IMAGE; - -public class WinAppBundler extends AbstractImageBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.WinResources"); - - static final BundlerParamInfo ICON_ICO = - new StandardBundlerParam<>( - "icon.ico", - File.class, - params -> { - File f = ICON.fetchFrom(params); - if (f != null && !f.getName().toLowerCase().endsWith(".ico")) { - Log.error(MessageFormat.format( - I18N.getString("message.icon-not-ico"), f)); - return null; - } - return f; - }, - (s, p) -> new File(s)); - - @Override - public boolean validate(Map params) - throws UnsupportedPlatformException, ConfigException { - try { - if (params == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - - return doValidate(params); - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - // to be used by chained bundlers, e.g. by EXE bundler to avoid - // skipping validation if p.type does not include "image" - private boolean doValidate(Map p) - throws UnsupportedPlatformException, ConfigException { - if (Platform.getPlatform() != Platform.WINDOWS) { - throw new UnsupportedPlatformException(); - } - - imageBundleValidation(p); - - if (StandardBundlerParam.getPredefinedAppImage(p) != null) { - return true; - } - - // Make sure that jpackage.exe exists. - File tool = new File( - System.getProperty("java.home") + "\\bin\\jpackage.exe"); - - if (!tool.exists()) { - throw new ConfigException( - I18N.getString("error.no-windows-resources"), - I18N.getString("error.no-windows-resources.advice")); - } - - return true; - } - - private static boolean usePredefineAppName(Map p) { - return (PREDEFINED_APP_IMAGE.fetchFrom(p) != null); - } - - private static String appName; - synchronized static String getAppName( - Map p) { - // If we building from predefined app image, then we should use names - // from image and not from CLI. - if (usePredefineAppName(p)) { - if (appName == null) { - // Use WIN_APP_IMAGE here, since we already copy pre-defined - // image to WIN_APP_IMAGE - File appImageDir = WIN_APP_IMAGE.fetchFrom(p); - - File appDir = new File(appImageDir.toString() + "\\app"); - File [] files = appDir.listFiles( - (File dir, String name) -> name.endsWith(".cfg")); - if (files == null || files.length == 0) { - String name = APP_NAME.fetchFrom(p); - Path exePath = appImageDir.toPath().resolve(name + ".exe"); - Path icoPath = appImageDir.toPath().resolve(name + ".ico"); - if (exePath.toFile().exists() && - icoPath.toFile().exists()) { - return name; - } else { - throw new RuntimeException(MessageFormat.format( - I18N.getString("error.cannot-find-launcher"), - appImageDir)); - } - } else { - appName = files[0].getName(); - int index = appName.indexOf("."); - if (index != -1) { - appName = appName.substring(0, index); - } - if (files.length > 1) { - Log.error(MessageFormat.format(I18N.getString( - "message.multiple-launchers"), appName)); - } - } - return appName; - } else { - return appName; - } - } - - return APP_NAME.fetchFrom(p); - } - - public static String getLauncherName(Map p) { - return getAppName(p) + ".exe"; - } - - public static String getLauncherCfgName(Map p) { - return "app\\" + getAppName(p) +".cfg"; - } - - public boolean bundle(Map p, File outputDirectory) - throws PackagerException { - return doBundle(p, outputDirectory, false) != null; - } - - File doBundle(Map p, File outputDirectory, - boolean dependentTask) throws PackagerException { - if (StandardBundlerParam.isRuntimeInstaller(p)) { - return PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); - } else { - return doAppBundle(p, outputDirectory, dependentTask); - } - } - - File doAppBundle(Map p, File outputDirectory, - boolean dependentTask) throws PackagerException { - try { - File rootDirectory = createRoot(p, outputDirectory, dependentTask, - APP_NAME.fetchFrom(p)); - AbstractAppImageBuilder appBuilder = - new WindowsAppImageBuilder(p, outputDirectory.toPath()); - if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) { - JLinkBundlerHelper.execute(p, appBuilder); - } else { - StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder); - } - if (!dependentTask) { - Log.verbose(MessageFormat.format( - I18N.getString("message.result-dir"), - outputDirectory.getAbsolutePath())); - } - return rootDirectory; - } catch (PackagerException pe) { - throw pe; - } catch (Exception e) { - Log.verbose(e); - throw new PackagerException(e); - } - } - - @Override - public String getName() { - return I18N.getString("app.bundler.name"); - } - - @Override - public String getDescription() { - return I18N.getString("app.bundler.description"); - } - - @Override - public String getID() { - return "windows.app"; - } - - @Override - public String getBundleType() { - return "IMAGE"; - } - - @Override - public Collection> getBundleParameters() { - return getAppBundleParameters(); - } - - public static Collection> getAppBundleParameters() { - return Arrays.asList( - APP_NAME, - APP_RESOURCES, - ARGUMENTS, - CLASSPATH, - ICON_ICO, - JAVA_OPTIONS, - MAIN_CLASS, - MAIN_JAR, - VERSION, - VERBOSE - ); - } - - @Override - public File execute(Map params, - File outputParentDir) throws PackagerException { - return doBundle(params, outputParentDir, false); - } - - @Override - public boolean supported(boolean platformInstaller) { - return (Platform.getPlatform() == Platform.WINDOWS); - } - -} --- /dev/null 2019-11-18 21:09:40.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WinAppBundler.java 2019-11-18 21:09:35.186710800 -0500 @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.*; + +import static jdk.incubator.jpackage.internal.WindowsBundlerParam.*; + +public class WinAppBundler extends AbstractImageBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.WinResources"); + + static final BundlerParamInfo ICON_ICO = + new StandardBundlerParam<>( + "icon.ico", + File.class, + params -> { + File f = ICON.fetchFrom(params); + if (f != null && !f.getName().toLowerCase().endsWith(".ico")) { + Log.error(MessageFormat.format( + I18N.getString("message.icon-not-ico"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + + @Override + public boolean validate(Map params) + throws ConfigException { + try { + Objects.requireNonNull(params); + return doValidate(params); + } catch (RuntimeException re) { + if (re.getCause() instanceof ConfigException) { + throw (ConfigException) re.getCause(); + } else { + throw new ConfigException(re); + } + } + } + + // to be used by chained bundlers, e.g. by EXE bundler to avoid + // skipping validation if p.type does not include "image" + private boolean doValidate(Map p) + throws ConfigException { + + imageBundleValidation(p); + return true; + } + + public boolean bundle(Map p, File outputDirectory) + throws PackagerException { + return doBundle(p, outputDirectory, false) != null; + } + + File doBundle(Map p, File outputDirectory, + boolean dependentTask) throws PackagerException { + if (StandardBundlerParam.isRuntimeInstaller(p)) { + return PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); + } else { + return doAppBundle(p, outputDirectory, dependentTask); + } + } + + File doAppBundle(Map p, File outputDirectory, + boolean dependentTask) throws PackagerException { + try { + File rootDirectory = createRoot(p, outputDirectory, dependentTask, + APP_NAME.fetchFrom(p)); + AbstractAppImageBuilder appBuilder = + new WindowsAppImageBuilder(p, outputDirectory.toPath()); + if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) { + JLinkBundlerHelper.execute(p, appBuilder); + } else { + StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder); + } + if (!dependentTask) { + Log.verbose(MessageFormat.format( + I18N.getString("message.result-dir"), + outputDirectory.getAbsolutePath())); + } + return rootDirectory; + } catch (PackagerException pe) { + throw pe; + } catch (Exception e) { + Log.verbose(e); + throw new PackagerException(e); + } + } + + @Override + public String getName() { + return I18N.getString("app.bundler.name"); + } + + @Override + public String getID() { + return "windows.app"; + } + + @Override + public String getBundleType() { + return "IMAGE"; + } + + @Override + public File execute(Map params, + File outputParentDir) throws PackagerException { + return doBundle(params, outputParentDir, false); + } + + @Override + public boolean supported(boolean platformInstaller) { + return true; + } + + @Override + public boolean isDefault() { + return false; + } + +} --- /dev/null 2019-11-18 21:09:58.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WinExeBundler.java 2019-11-18 21:09:55.840205500 -0500 @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.*; + +public class WinExeBundler extends AbstractBundler { + + static { + System.loadLibrary("jpackage"); + } + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.WinResources"); + + public static final BundlerParamInfo APP_BUNDLER + = new WindowsBundlerParam<>( + "win.app.bundler", + WinAppBundler.class, + params -> new WinAppBundler(), + null); + + public static final BundlerParamInfo EXE_IMAGE_DIR + = new WindowsBundlerParam<>( + "win.exe.imageDir", + File.class, + params -> { + File imagesRoot = IMAGES_ROOT.fetchFrom(params); + if (!imagesRoot.exists()) { + imagesRoot.mkdirs(); + } + return new File(imagesRoot, "win-exe.image"); + }, + (s, p) -> null); + + private final static String EXE_WRAPPER_NAME = "msiwrapper.exe"; + + @Override + public String getName() { + return getString("exe.bundler.name"); + } + + @Override + public String getID() { + return "exe"; + } + + @Override + public String getBundleType() { + return "INSTALLER"; + } + + @Override + public File execute(Map params, + File outputParentDir) throws PackagerException { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported(boolean platformInstaller) { + return msiBundler.supported(platformInstaller); + } + + @Override + public boolean isDefault() { + return true; + } + + @Override + public boolean validate(Map params) + throws ConfigException { + return msiBundler.validate(params); + } + + public File bundle(Map params, File outdir) + throws PackagerException { + + IOUtils.writableOutputDir(outdir.toPath()); + + File exeImageDir = EXE_IMAGE_DIR.fetchFrom(params); + + // Write msi to temporary directory. + File msi = msiBundler.bundle(params, exeImageDir); + + try { + new ScriptRunner() + .setDirectory(msi.toPath().getParent()) + .setResourceCategoryId("resource.post-msi-script") + .setScriptNameSuffix("post-msi") + .setEnvironmentVariable("JpMsiFile", msi.getAbsolutePath().toString()) + .run(params); + + return buildEXE(msi, outdir); + } catch (IOException ex) { + Log.verbose(ex); + throw new PackagerException(ex); + } + } + + private File buildEXE(File msi, File outdir) + throws IOException { + + Log.verbose(MessageFormat.format( + getString("message.outputting-to-location"), + outdir.getAbsolutePath())); + + // Copy template msi wrapper next to msi file + String exePath = msi.getAbsolutePath(); + exePath = exePath.substring(0, exePath.lastIndexOf('.')) + ".exe"; + try (InputStream is = OverridableResource.readDefault(EXE_WRAPPER_NAME)) { + Files.copy(is, Path.of(exePath)); + } + // Embed msi in msi wrapper exe. + embedMSI(exePath, msi.getAbsolutePath()); + + Path dstExePath = Paths.get(outdir.getAbsolutePath(), + Path.of(exePath).getFileName().toString()); + Files.deleteIfExists(dstExePath); + + Files.copy(Path.of(exePath), dstExePath); + + Log.verbose(MessageFormat.format( + getString("message.output-location"), + outdir.getAbsolutePath())); + + return dstExePath.toFile(); + } + + private static String getString(String key) + throws MissingResourceException { + return I18N.getString(key); + } + + private final WinMsiBundler msiBundler = new WinMsiBundler(); + + private static native int embedMSI(String exePath, String msiPath); +} --- /dev/null 2019-11-18 21:10:09.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WinMsiBundler.java 2019-11-18 21:10:06.404478000 -0500 @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +import static jdk.incubator.jpackage.internal.WindowsBundlerParam.*; + +/** + * WinMsiBundler + * + * Produces .msi installer from application image. Uses WiX Toolkit to build + * .msi installer. + *

+ * {@link #execute} method creates a number of source files with the description + * of installer to be processed by WiX tools. Generated source files are stored + * in "config" subdirectory next to "app" subdirectory in the root work + * directory. The following WiX source files are generated: + *

    + *
  • main.wxs. Main source file with the installer description + *
  • bundle.wxf. Source file with application and Java run-time directory tree + * description. + *
+ *

+ * main.wxs file is a copy of main.wxs resource from + * jdk.incubator.jpackage.internal.resources package. It is parametrized with the + * following WiX variables: + *

    + *
  • JpAppName. Name of the application. Set to the value of --name command + * line option + *
  • JpAppVersion. Version of the application. Set to the value of + * --app-version command line option + *
  • JpAppVendor. Vendor of the application. Set to the value of --vendor + * command line option + *
  • JpAppDescription. Description of the application. Set to the value of + * --description command line option + *
  • JpProductCode. Set to product code UUID of the application. Random value + * generated by jpackage every time {@link #execute} method is called + *
  • JpProductUpgradeCode. Set to upgrade code UUID of the application. Random + * value generated by jpackage every time {@link #execute} method is called if + * --win-upgrade-uuid command line option is not specified. Otherwise this + * variable is set to the value of --win-upgrade-uuid command line option + *
  • JpAllowDowngrades. Set to "yes" if --win-upgrade-uuid command line option + * was specified. Undefined otherwise + *
  • JpLicenseRtf. Set to the value of --license-file command line option. + * Undefined is --license-file command line option was not specified + *
  • JpInstallDirChooser. Set to "yes" if --win-dir-chooser command line + * option was specified. Undefined otherwise + *
  • JpConfigDir. Absolute path to the directory with generated WiX source + * files. + *
  • JpIsSystemWide. Set to "yes" if --win-per-user-install command line + * option was not specified. Undefined otherwise + *
+ */ +public class WinMsiBundler extends AbstractBundler { + + public static final BundlerParamInfo APP_BUNDLER = + new WindowsBundlerParam<>( + "win.app.bundler", + WinAppBundler.class, + params -> new WinAppBundler(), + null); + + public static final BundlerParamInfo MSI_IMAGE_DIR = + new WindowsBundlerParam<>( + "win.msi.imageDir", + File.class, + params -> { + File imagesRoot = IMAGES_ROOT.fetchFrom(params); + if (!imagesRoot.exists()) imagesRoot.mkdirs(); + return new File(imagesRoot, "win-msi.image"); + }, + (s, p) -> null); + + public static final BundlerParamInfo WIN_APP_IMAGE = + new WindowsBundlerParam<>( + "win.app.image", + File.class, + null, + (s, p) -> null); + + public static final StandardBundlerParam MSI_SYSTEM_WIDE = + new StandardBundlerParam<>( + Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(), + Boolean.class, + params -> true, // MSIs default to system wide + // valueOf(null) is false, + // and we actually do want null + (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null + : Boolean.valueOf(s) + ); + + + public static final StandardBundlerParam PRODUCT_VERSION = + new StandardBundlerParam<>( + "win.msi.productVersion", + String.class, + VERSION::fetchFrom, + (s, p) -> s + ); + + private static final BundlerParamInfo UPGRADE_UUID = + new WindowsBundlerParam<>( + Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(), + String.class, + null, + (s, p) -> s); + + @Override + public String getName() { + return I18N.getString("msi.bundler.name"); + } + + @Override + public String getID() { + return "msi"; + } + + @Override + public String getBundleType() { + return "INSTALLER"; + } + + @Override + public File execute(Map params, + File outputParentDir) throws PackagerException { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported(boolean platformInstaller) { + try { + if (wixToolset == null) { + wixToolset = WixTool.toolset(); + } + return true; + } catch (ConfigException ce) { + Log.error(ce.getMessage()); + if (ce.getAdvice() != null) { + Log.error(ce.getAdvice()); + } + } catch (Exception e) { + Log.error(e.getMessage()); + } + return false; + } + + @Override + public boolean isDefault() { + return false; + } + + private static UUID getUpgradeCode(Map params) { + String upgradeCode = UPGRADE_UUID.fetchFrom(params); + if (upgradeCode != null) { + return UUID.fromString(upgradeCode); + } + return createNameUUID("UpgradeCode", params, List.of(VENDOR, APP_NAME)); + } + + private static UUID getProductCode(Map params) { + return createNameUUID("ProductCode", params, List.of(VENDOR, APP_NAME, + VERSION)); + } + + private static UUID createNameUUID(String prefix, + Map params, + List> components) { + String key = Stream.concat(Stream.of(prefix), components.stream().map( + c -> c.fetchFrom(params))).collect(Collectors.joining("/")); + return UUID.nameUUIDFromBytes(key.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public boolean validate(Map params) + throws ConfigException { + try { + if (wixToolset == null) { + wixToolset = WixTool.toolset(); + } + + try { + getUpgradeCode(params); + } catch (IllegalArgumentException ex) { + throw new ConfigException(ex); + } + + for (var toolInfo: wixToolset.values()) { + Log.verbose(MessageFormat.format(I18N.getString( + "message.tool-version"), toolInfo.path.getFileName(), + toolInfo.version)); + } + + wixSourcesBuilder.setWixVersion(wixToolset.get(WixTool.Light).version); + + wixSourcesBuilder.logWixFeatures(); + + /********* validate bundle parameters *************/ + + String version = PRODUCT_VERSION.fetchFrom(params); + if (!isVersionStringValid(version)) { + throw new ConfigException( + MessageFormat.format(I18N.getString( + "error.version-string-wrong-format"), version), + MessageFormat.format(I18N.getString( + "error.version-string-wrong-format.advice"), + PRODUCT_VERSION.getID())); + } + + // only one mime type per association, at least one file extension + List> associations = + FILE_ASSOCIATIONS.fetchFrom(params); + if (associations != null) { + for (int i = 0; i < associations.size(); i++) { + Map assoc = associations.get(i); + List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); + if (mimes.size() > 1) { + throw new ConfigException(MessageFormat.format( + I18N.getString("error.too-many-content-types-for-file-association"), i), + I18N.getString("error.too-many-content-types-for-file-association.advice")); + } + } + } + + return true; + } catch (RuntimeException re) { + if (re.getCause() instanceof ConfigException) { + throw (ConfigException) re.getCause(); + } else { + throw new ConfigException(re); + } + } + } + + // https://msdn.microsoft.com/en-us/library/aa370859%28v=VS.85%29.aspx + // The format of the string is as follows: + // major.minor.build + // The first field is the major version and has a maximum value of 255. + // The second field is the minor version and has a maximum value of 255. + // The third field is called the build version or the update version and + // has a maximum value of 65,535. + static boolean isVersionStringValid(String v) { + if (v == null) { + return true; + } + + String p[] = v.split("\\."); + if (p.length > 3) { + Log.verbose(I18N.getString( + "message.version-string-too-many-components")); + return false; + } + + try { + int val = Integer.parseInt(p[0]); + if (val < 0 || val > 255) { + Log.verbose(I18N.getString( + "error.version-string-major-out-of-range")); + return false; + } + if (p.length > 1) { + val = Integer.parseInt(p[1]); + if (val < 0 || val > 255) { + Log.verbose(I18N.getString( + "error.version-string-minor-out-of-range")); + return false; + } + } + if (p.length > 2) { + val = Integer.parseInt(p[2]); + if (val < 0 || val > 65535) { + Log.verbose(I18N.getString( + "error.version-string-build-out-of-range")); + return false; + } + } + } catch (NumberFormatException ne) { + Log.verbose(I18N.getString("error.version-string-part-not-number")); + Log.verbose(ne); + return false; + } + + return true; + } + + private void prepareProto(Map params) + throws PackagerException, IOException { + File appImage = StandardBundlerParam.getPredefinedAppImage(params); + File appDir = null; + + // we either have an application image or need to build one + if (appImage != null) { + appDir = new File(MSI_IMAGE_DIR.fetchFrom(params), + APP_NAME.fetchFrom(params)); + // copy everything from appImage dir into appDir/name + IOUtils.copyRecursive(appImage.toPath(), appDir.toPath()); + } else { + appDir = APP_BUNDLER.fetchFrom(params).doBundle(params, + MSI_IMAGE_DIR.fetchFrom(params), true); + } + + params.put(WIN_APP_IMAGE.getID(), appDir); + + String licenseFile = LICENSE_FILE.fetchFrom(params); + if (licenseFile != null) { + // need to copy license file to the working directory + // and convert to rtf if needed + File lfile = new File(licenseFile); + File destFile = new File(CONFIG_ROOT.fetchFrom(params), + lfile.getName()); + + IOUtils.copyFile(lfile, destFile); + destFile.setWritable(true); + ensureByMutationFileIsRTF(destFile); + } + } + + public File bundle(Map params, File outdir) + throws PackagerException { + + IOUtils.writableOutputDir(outdir.toPath()); + + Path imageDir = MSI_IMAGE_DIR.fetchFrom(params).toPath(); + try { + Files.createDirectories(imageDir); + + prepareProto(params); + + wixSourcesBuilder + .initFromParams(WIN_APP_IMAGE.fetchFrom(params).toPath(), params) + .createMainFragment(CONFIG_ROOT.fetchFrom(params).toPath().resolve( + "bundle.wxf")); + + Map wixVars = prepareMainProjectFile(params); + + new ScriptRunner() + .setDirectory(imageDir) + .setResourceCategoryId("resource.post-app-image-script") + .setScriptNameSuffix("post-image") + .setEnvironmentVariable("JpAppImageDir", imageDir.toAbsolutePath().toString()) + .run(params); + + return buildMSI(params, wixVars, outdir); + } catch (IOException ex) { + Log.verbose(ex); + throw new PackagerException(ex); + } + } + + Map prepareMainProjectFile( + Map params) throws IOException { + Map data = new HashMap<>(); + + final UUID productCode = getProductCode(params); + final UUID upgradeCode = getUpgradeCode(params); + + data.put("JpProductCode", productCode.toString()); + data.put("JpProductUpgradeCode", upgradeCode.toString()); + + Log.verbose(MessageFormat.format(I18N.getString("message.product-code"), + productCode)); + Log.verbose(MessageFormat.format(I18N.getString("message.upgrade-code"), + upgradeCode)); + + data.put("JpAllowUpgrades", "yes"); + + data.put("JpAppName", APP_NAME.fetchFrom(params)); + data.put("JpAppDescription", DESCRIPTION.fetchFrom(params)); + data.put("JpAppVendor", VENDOR.fetchFrom(params)); + data.put("JpAppVersion", PRODUCT_VERSION.fetchFrom(params)); + + final Path configDir = CONFIG_ROOT.fetchFrom(params).toPath(); + + data.put("JpConfigDir", configDir.toAbsolutePath().toString()); + + if (MSI_SYSTEM_WIDE.fetchFrom(params)) { + data.put("JpIsSystemWide", "yes"); + } + + String licenseFile = LICENSE_FILE.fetchFrom(params); + if (licenseFile != null) { + String lname = new File(licenseFile).getName(); + File destFile = new File(CONFIG_ROOT.fetchFrom(params), lname); + data.put("JpLicenseRtf", destFile.getAbsolutePath()); + } + + // Copy CA dll to include with installer + if (INSTALLDIR_CHOOSER.fetchFrom(params)) { + data.put("JpInstallDirChooser", "yes"); + String fname = "wixhelper.dll"; + try (InputStream is = OverridableResource.readDefault(fname)) { + Files.copy(is, Paths.get( + CONFIG_ROOT.fetchFrom(params).getAbsolutePath(), + fname)); + } + } + + // Copy l10n files. + for (String loc : Arrays.asList("en", "ja", "zh_CN")) { + String fname = "MsiInstallerStrings_" + loc + ".wxl"; + try (InputStream is = OverridableResource.readDefault(fname)) { + Files.copy(is, Paths.get( + CONFIG_ROOT.fetchFrom(params).getAbsolutePath(), + fname)); + } + } + + createResource("main.wxs", params) + .setCategory(I18N.getString("resource.main-wix-file")) + .saveToFile(configDir.resolve("main.wxs")); + + createResource("overrides.wxi", params) + .setCategory(I18N.getString("resource.overrides-wix-file")) + .saveToFile(configDir.resolve("overrides.wxi")); + + return data; + } + + private File buildMSI(Map params, + Map wixVars, File outdir) + throws IOException { + + File msiOut = new File( + outdir, INSTALLER_FILE_NAME.fetchFrom(params) + ".msi"); + + Log.verbose(MessageFormat.format(I18N.getString( + "message.preparing-msi-config"), msiOut.getAbsolutePath())); + + WixPipeline wixPipeline = new WixPipeline() + .setToolset(wixToolset.entrySet().stream().collect( + Collectors.toMap( + entry -> entry.getKey(), + entry -> entry.getValue().path))) + .setWixObjDir(TEMP_ROOT.fetchFrom(params).toPath().resolve("wixobj")) + .setWorkDir(WIN_APP_IMAGE.fetchFrom(params).toPath()) + .addSource(CONFIG_ROOT.fetchFrom(params).toPath().resolve("main.wxs"), wixVars) + .addSource(CONFIG_ROOT.fetchFrom(params).toPath().resolve("bundle.wxf"), null); + + Log.verbose(MessageFormat.format(I18N.getString( + "message.generating-msi"), msiOut.getAbsolutePath())); + + boolean enableLicenseUI = (LICENSE_FILE.fetchFrom(params) != null); + boolean enableInstalldirUI = INSTALLDIR_CHOOSER.fetchFrom(params); + + List lightArgs = new ArrayList<>(); + + if (!MSI_SYSTEM_WIDE.fetchFrom(params)) { + wixPipeline.addLightOptions("-sice:ICE91"); + } + if (enableLicenseUI || enableInstalldirUI) { + wixPipeline.addLightOptions("-ext", "WixUIExtension"); + } + + wixPipeline.addLightOptions("-loc", + CONFIG_ROOT.fetchFrom(params).toPath().resolve(I18N.getString( + "resource.wxl-file-name")).toAbsolutePath().toString()); + + // Only needed if we using CA dll, so Wix can find it + if (enableInstalldirUI) { + wixPipeline.addLightOptions("-b", CONFIG_ROOT.fetchFrom(params).getAbsolutePath()); + } + + wixPipeline.buildMsi(msiOut.toPath().toAbsolutePath()); + + return msiOut; + } + + public static void ensureByMutationFileIsRTF(File f) { + if (f == null || !f.isFile()) return; + + try { + boolean existingLicenseIsRTF = false; + + try (FileInputStream fin = new FileInputStream(f)) { + byte[] firstBits = new byte[7]; + + if (fin.read(firstBits) == firstBits.length) { + String header = new String(firstBits); + existingLicenseIsRTF = "{\\rtf1\\".equals(header); + } + } + + if (!existingLicenseIsRTF) { + List oldLicense = Files.readAllLines(f.toPath()); + try (Writer w = Files.newBufferedWriter( + f.toPath(), Charset.forName("Windows-1252"))) { + w.write("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033" + + "{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}}\n" + + "\\viewkind4\\uc1\\pard\\sa200\\sl276" + + "\\slmult1\\lang9\\fs20 "); + oldLicense.forEach(l -> { + try { + for (char c : l.toCharArray()) { + // 0x00 <= ch < 0x20 Escaped (\'hh) + // 0x20 <= ch < 0x80 Raw(non - escaped) char + // 0x80 <= ch <= 0xFF Escaped(\ 'hh) + // 0x5C, 0x7B, 0x7D (special RTF characters + // \,{,})Escaped(\'hh) + // ch > 0xff Escaped (\\ud###?) + if (c < 0x10) { + w.write("\\'0"); + w.write(Integer.toHexString(c)); + } else if (c > 0xff) { + w.write("\\ud"); + w.write(Integer.toString(c)); + // \\uc1 is in the header and in effect + // so we trail with a replacement char if + // the font lacks that character - '?' + w.write("?"); + } else if ((c < 0x20) || (c >= 0x80) || + (c == 0x5C) || (c == 0x7B) || + (c == 0x7D)) { + w.write("\\'"); + w.write(Integer.toHexString(c)); + } else { + w.write(c); + } + } + // blank lines are interpreted as paragraph breaks + if (l.length() < 1) { + w.write("\\par"); + } else { + w.write(" "); + } + w.write("\r\n"); + } catch (IOException e) { + Log.verbose(e); + } + }); + w.write("}\r\n"); + } + } + } catch (IOException e) { + Log.verbose(e); + } + + } + + private Map wixToolset; + private WixSourcesBuilder wixSourcesBuilder = new WixSourcesBuilder(); + +} --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsAppImageBuilder.java 2019-11-18 21:10:20.591064100 -0500 +++ /dev/null 2019-11-18 21:10:22.000000000 -0500 @@ -1,444 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.UncheckedIOException; -import java.io.Writer; -import java.io.BufferedWriter; -import java.io.FileWriter; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.nio.file.attribute.PosixFilePermission; -import java.text.MessageFormat; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.ResourceBundle; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Pattern; -import java.util.stream.Stream; - -import static jdk.jpackage.internal.StandardBundlerParam.*; - -public class WindowsAppImageBuilder extends AbstractAppImageBuilder { - - static { - System.loadLibrary("jpackage"); - } - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.WinResources"); - - private final static String LIBRARY_NAME = "applauncher.dll"; - private final static String REDIST_MSVCR = "vcruntimeVS_VER.dll"; - private final static String REDIST_MSVCP = "msvcpVS_VER.dll"; - - private final static String TEMPLATE_APP_ICON ="javalogo_white_48.ico"; - - private static final String EXECUTABLE_PROPERTIES_TEMPLATE = - "WinLauncher.template"; - - private final Path root; - private final Path appDir; - private final Path appModsDir; - private final Path runtimeDir; - private final Path mdir; - - private final Map params; - - public static final BundlerParamInfo REBRAND_EXECUTABLE = - new WindowsBundlerParam<>( - "win.launcher.rebrand", - Boolean.class, - params -> Boolean.TRUE, - (s, p) -> Boolean.valueOf(s)); - - public static final BundlerParamInfo ICON_ICO = - new StandardBundlerParam<>( - "icon.ico", - File.class, - params -> { - File f = ICON.fetchFrom(params); - if (f != null && !f.getName().toLowerCase().endsWith(".ico")) { - Log.error(MessageFormat.format( - I18N.getString("message.icon-not-ico"), f)); - return null; - } - return f; - }, - (s, p) -> new File(s)); - - public static final StandardBundlerParam CONSOLE_HINT = - new WindowsBundlerParam<>( - Arguments.CLIOptions.WIN_CONSOLE_HINT.getId(), - Boolean.class, - params -> false, - // valueOf(null) is false, - // and we actually do want null in some cases - (s, p) -> (s == null - || "null".equalsIgnoreCase(s)) ? true : Boolean.valueOf(s)); - - public WindowsAppImageBuilder(Map config, Path imageOutDir) - throws IOException { - super(config, - imageOutDir.resolve(APP_NAME.fetchFrom(config) + "/runtime")); - - Objects.requireNonNull(imageOutDir); - - this.params = config; - - this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params)); - this.appDir = root.resolve("app"); - this.appModsDir = appDir.resolve("mods"); - this.runtimeDir = root.resolve("runtime"); - this.mdir = runtimeDir.resolve("lib"); - Files.createDirectories(appDir); - Files.createDirectories(runtimeDir); - } - - public WindowsAppImageBuilder(String jreName, Path imageOutDir) - throws IOException { - super(null, imageOutDir.resolve(jreName)); - - Objects.requireNonNull(imageOutDir); - - this.params = null; - this.root = imageOutDir.resolve(jreName); - this.appDir = null; - this.appModsDir = null; - this.runtimeDir = root; - this.mdir = runtimeDir.resolve("lib"); - Files.createDirectories(runtimeDir); - } - - private Path destFile(String dir, String filename) { - return runtimeDir.resolve(dir).resolve(filename); - } - - private void writeEntry(InputStream in, Path dstFile) throws IOException { - Files.createDirectories(dstFile.getParent()); - Files.copy(in, dstFile); - } - - private void writeSymEntry(Path dstFile, Path target) throws IOException { - Files.createDirectories(dstFile.getParent()); - Files.createLink(dstFile, target); - } - - /** - * chmod ugo+x file - */ - private void setExecutable(Path file) { - try { - Set perms = - Files.getPosixFilePermissions(file); - perms.add(PosixFilePermission.OWNER_EXECUTE); - perms.add(PosixFilePermission.GROUP_EXECUTE); - perms.add(PosixFilePermission.OTHERS_EXECUTE); - Files.setPosixFilePermissions(file, perms); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - } - - private static void createUtf8File(File file, String content) - throws IOException { - try (OutputStream fout = new FileOutputStream(file); - Writer output = new OutputStreamWriter(fout, "UTF-8")) { - output.write(content); - } - } - - public static String getLauncherName(Map p) { - return APP_NAME.fetchFrom(p) + ".exe"; - } - - // Returns launcher resource name for launcher we need to use. - public static String getLauncherResourceName( - Map p) { - if (CONSOLE_HINT.fetchFrom(p)) { - return "jpackageapplauncher.exe"; - } else { - return "jpackageapplauncherw.exe"; - } - } - - public static String getLauncherCfgName(Map p) { - return "app/" + APP_NAME.fetchFrom(p) +".cfg"; - } - - private File getConfig_AppIcon(Map params) { - return new File(getConfigRoot(params), - APP_NAME.fetchFrom(params) + ".ico"); - } - - private File getConfig_ExecutableProperties( - Map params) { - return new File(getConfigRoot(params), - APP_NAME.fetchFrom(params) + ".properties"); - } - - File getConfigRoot(Map params) { - return CONFIG_ROOT.fetchFrom(params); - } - - @Override - public Path getAppDir() { - return appDir; - } - - @Override - public Path getAppModsDir() { - return appModsDir; - } - - @Override - public void prepareApplicationFiles() throws IOException { - Map originalParams = new HashMap<>(params); - File rootFile = root.toFile(); - if (!rootFile.isDirectory() && !rootFile.mkdirs()) { - throw new RuntimeException(MessageFormat.format(I18N.getString( - "error.cannot-create-output-dir"), rootFile.getAbsolutePath())); - } - if (!rootFile.canWrite()) { - throw new RuntimeException(MessageFormat.format( - I18N.getString("error.cannot-write-to-output-dir"), - rootFile.getAbsolutePath())); - } - // create the .exe launchers - createLauncherForEntryPoint(params); - - // copy the jars - copyApplication(params); - - // copy in the needed libraries - try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { - Files.copy(is_lib, root.resolve(LIBRARY_NAME)); - } - - copyMSVCDLLs(); - - // create the additional launcher(s), if any - List> entryPoints = - StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); - for (Map entryPoint : entryPoints) { - createLauncherForEntryPoint( - AddLauncherArguments.merge(originalParams, entryPoint)); - } - } - - @Override - public void prepareJreFiles() throws IOException {} - - private void copyMSVCDLLs() throws IOException { - AtomicReference ioe = new AtomicReference<>(); - try (Stream files = Files.list(runtimeDir.resolve("bin"))) { - files.filter(p -> Pattern.matches( - "^(vcruntime|msvcp|msvcr|ucrtbase|api-ms-win-).*\\.dll$", - p.toFile().getName().toLowerCase())) - .forEach(p -> { - try { - Files.copy(p, root.resolve((p.toFile().getName()))); - } catch (IOException e) { - ioe.set(e); - } - }); - } - - IOException e = ioe.get(); - if (e != null) { - throw e; - } - } - - // TODO: do we still need this? - private boolean copyMSVCDLLs(String VS_VER) throws IOException { - final InputStream REDIST_MSVCR_URL = getResourceAsStream( - REDIST_MSVCR.replaceAll("VS_VER", VS_VER)); - final InputStream REDIST_MSVCP_URL = getResourceAsStream( - REDIST_MSVCP.replaceAll("VS_VER", VS_VER)); - - if (REDIST_MSVCR_URL != null && REDIST_MSVCP_URL != null) { - Files.copy( - REDIST_MSVCR_URL, - root.resolve(REDIST_MSVCR.replaceAll("VS_VER", VS_VER))); - Files.copy( - REDIST_MSVCP_URL, - root.resolve(REDIST_MSVCP.replaceAll("VS_VER", VS_VER))); - return true; - } - - return false; - } - - private void validateValueAndPut( - Map data, String key, - BundlerParamInfo param, - Map params) { - String value = param.fetchFrom(params); - if (value.contains("\r") || value.contains("\n")) { - Log.error("Configuration Parameter " + param.getID() - + " contains multiple lines of text, ignore it"); - data.put(key, ""); - return; - } - data.put(key, value); - } - - protected void prepareExecutableProperties( - Map params) throws IOException { - Map data = new HashMap<>(); - - // mapping Java parameters in strings for version resource - validateValueAndPut(data, "COMPANY_NAME", VENDOR, params); - validateValueAndPut(data, "FILE_DESCRIPTION", DESCRIPTION, params); - validateValueAndPut(data, "FILE_VERSION", VERSION, params); - data.put("INTERNAL_NAME", getLauncherName(params)); - validateValueAndPut(data, "LEGAL_COPYRIGHT", COPYRIGHT, params); - data.put("ORIGINAL_FILENAME", getLauncherName(params)); - validateValueAndPut(data, "PRODUCT_NAME", APP_NAME, params); - validateValueAndPut(data, "PRODUCT_VERSION", VERSION, params); - - try (Writer w = Files.newBufferedWriter( - getConfig_ExecutableProperties(params).toPath(), - StandardCharsets.UTF_8)) { - String content = preprocessTextResource( - getConfig_ExecutableProperties(params).getName(), - I18N.getString("resource.executable-properties-template"), - EXECUTABLE_PROPERTIES_TEMPLATE, data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - } - } - - private void createLauncherForEntryPoint( - Map p) throws IOException { - - File launcherIcon = ICON_ICO.fetchFrom(p); - File icon = launcherIcon != null ? - launcherIcon : ICON_ICO.fetchFrom(params); - File iconTarget = getConfig_AppIcon(p); - - InputStream in = locateResource( - APP_NAME.fetchFrom(params) + ".ico", - "icon", - TEMPLATE_APP_ICON, - icon, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - - Files.copy(in, iconTarget.toPath(), - StandardCopyOption.REPLACE_EXISTING); - - writeCfgFile(p, root.resolve( - getLauncherCfgName(p)).toFile(), "$APPDIR\\runtime"); - - prepareExecutableProperties(p); - - // Copy executable root folder - Path executableFile = root.resolve(getLauncherName(p)); - try (InputStream is_launcher = - getResourceAsStream(getLauncherResourceName(p))) { - writeEntry(is_launcher, executableFile); - } - - File launcher = executableFile.toFile(); - launcher.setWritable(true, true); - - // Update branding of EXE file - if (REBRAND_EXECUTABLE.fetchFrom(p)) { - try { - String tempDirectory = WindowsDefender.getUserTempDirectory(); - if (Arguments.CLIOptions.context().userProvidedBuildRoot) { - tempDirectory = TEMP_ROOT.fetchFrom(p).getAbsolutePath(); - } - if (WindowsDefender.isThereAPotentialWindowsDefenderIssue( - tempDirectory)) { - Log.error(MessageFormat.format(I18N.getString( - "message.potential.windows.defender.issue"), - tempDirectory)); - } - - launcher.setWritable(true); - - if (iconTarget.exists()) { - iconSwap(iconTarget.getAbsolutePath(), - launcher.getAbsolutePath()); - } - - File executableProperties = getConfig_ExecutableProperties(p); - - if (executableProperties.exists()) { - if (versionSwap(executableProperties.getAbsolutePath(), - launcher.getAbsolutePath()) != 0) { - throw new RuntimeException(MessageFormat.format( - I18N.getString("error.version-swap"), - executableProperties.getAbsolutePath())); - } - } - } finally { - executableFile.toFile().setReadOnly(); - } - } - - Files.copy(iconTarget.toPath(), - root.resolve(APP_NAME.fetchFrom(p) + ".ico")); - } - - private void copyApplication(Map params) - throws IOException { - List appResourcesList = - APP_RESOURCES_LIST.fetchFrom(params); - if (appResourcesList == null) { - throw new RuntimeException("Null app resources?"); - } - for (RelativeFileSet appResources : appResourcesList) { - if (appResources == null) { - throw new RuntimeException("Null app resources?"); - } - File srcdir = appResources.getBaseDirectory(); - for (String fname : appResources.getIncludedFiles()) { - copyEntry(appDir, srcdir, fname); - } - } - } - - private static native int iconSwap(String iconTarget, String launcher); - - private static native int versionSwap(String executableProperties, String launcher); - -} --- /dev/null 2019-11-18 21:10:22.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WindowsAppImageBuilder.java 2019-11-18 21:10:17.143449800 -0500 @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.PosixFilePermission; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import static jdk.incubator.jpackage.internal.OverridableResource.createResource; + +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; + +public class WindowsAppImageBuilder extends AbstractAppImageBuilder { + + static { + System.loadLibrary("jpackage"); + } + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.WinResources"); + + private final static String LIBRARY_NAME = "applauncher.dll"; + private final static String REDIST_MSVCR = "vcruntimeVS_VER.dll"; + private final static String REDIST_MSVCP = "msvcpVS_VER.dll"; + + private final static String TEMPLATE_APP_ICON ="java48.ico"; + + private static final String EXECUTABLE_PROPERTIES_TEMPLATE = + "WinLauncher.template"; + + private final Path root; + private final Path appDir; + private final Path appModsDir; + private final Path runtimeDir; + private final Path mdir; + private final Path binDir; + + public static final BundlerParamInfo REBRAND_EXECUTABLE = + new WindowsBundlerParam<>( + "win.launcher.rebrand", + Boolean.class, + params -> Boolean.TRUE, + (s, p) -> Boolean.valueOf(s)); + + public static final BundlerParamInfo ICON_ICO = + new StandardBundlerParam<>( + "icon.ico", + File.class, + params -> { + File f = ICON.fetchFrom(params); + if (f != null && !f.getName().toLowerCase().endsWith(".ico")) { + Log.error(MessageFormat.format( + I18N.getString("message.icon-not-ico"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + + public static final StandardBundlerParam CONSOLE_HINT = + new WindowsBundlerParam<>( + Arguments.CLIOptions.WIN_CONSOLE_HINT.getId(), + Boolean.class, + params -> false, + // valueOf(null) is false, + // and we actually do want null in some cases + (s, p) -> (s == null + || "null".equalsIgnoreCase(s)) ? true : Boolean.valueOf(s)); + + public WindowsAppImageBuilder(Map params, Path imageOutDir) + throws IOException { + super(params, + imageOutDir.resolve(APP_NAME.fetchFrom(params) + "/runtime")); + + Objects.requireNonNull(imageOutDir); + + this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params)); + this.appDir = root.resolve("app"); + this.appModsDir = appDir.resolve("mods"); + this.runtimeDir = root.resolve("runtime"); + this.mdir = runtimeDir.resolve("lib"); + this.binDir = root; + Files.createDirectories(appDir); + Files.createDirectories(runtimeDir); + } + + private void writeEntry(InputStream in, Path dstFile) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.copy(in, dstFile); + } + + private static String getLauncherName(Map params) { + return APP_NAME.fetchFrom(params) + ".exe"; + } + + // Returns launcher resource name for launcher we need to use. + public static String getLauncherResourceName( + Map params) { + if (CONSOLE_HINT.fetchFrom(params)) { + return "jpackageapplauncher.exe"; + } else { + return "jpackageapplauncherw.exe"; + } + } + + public static String getLauncherCfgName( + Map params) { + return "app/" + APP_NAME.fetchFrom(params) +".cfg"; + } + + private File getConfig_AppIcon(Map params) { + return new File(getConfigRoot(params), + APP_NAME.fetchFrom(params) + ".ico"); + } + + private File getConfig_ExecutableProperties( + Map params) { + return new File(getConfigRoot(params), + APP_NAME.fetchFrom(params) + ".properties"); + } + + File getConfigRoot(Map params) { + return CONFIG_ROOT.fetchFrom(params); + } + + @Override + public Path getAppDir() { + return appDir; + } + + @Override + public Path getAppModsDir() { + return appModsDir; + } + + @Override + public void prepareApplicationFiles(Map params) + throws IOException { + Map originalParams = new HashMap<>(params); + + try { + IOUtils.writableOutputDir(root); + IOUtils.writableOutputDir(binDir); + } catch (PackagerException pe) { + throw new RuntimeException(pe); + } + AppImageFile.save(root, params); + + // create the .exe launchers + createLauncherForEntryPoint(params); + + // copy the jars + copyApplication(params); + + // copy in the needed libraries + try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { + Files.copy(is_lib, binDir.resolve(LIBRARY_NAME)); + } + + copyMSVCDLLs(); + + // create the additional launcher(s), if any + List> entryPoints = + StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); + for (Map entryPoint : entryPoints) { + createLauncherForEntryPoint( + AddLauncherArguments.merge(originalParams, entryPoint)); + } + } + + @Override + public void prepareJreFiles(Map params) + throws IOException {} + + private void copyMSVCDLLs() throws IOException { + AtomicReference ioe = new AtomicReference<>(); + try (Stream files = Files.list(runtimeDir.resolve("bin"))) { + files.filter(p -> Pattern.matches( + "^(vcruntime|msvcp|msvcr|ucrtbase|api-ms-win-).*\\.dll$", + p.toFile().getName().toLowerCase())) + .forEach(p -> { + try { + Files.copy(p, binDir.resolve((p.toFile().getName()))); + } catch (IOException e) { + ioe.set(e); + } + }); + } + + IOException e = ioe.get(); + if (e != null) { + throw e; + } + } + + private void validateValueAndPut( + Map data, String key, + BundlerParamInfo param, + Map params) { + String value = param.fetchFrom(params); + if (value.contains("\r") || value.contains("\n")) { + Log.error("Configuration Parameter " + param.getID() + + " contains multiple lines of text, ignore it"); + data.put(key, ""); + return; + } + data.put(key, value); + } + + protected void prepareExecutableProperties( + Map params) throws IOException { + + Map data = new HashMap<>(); + + // mapping Java parameters in strings for version resource + validateValueAndPut(data, "COMPANY_NAME", VENDOR, params); + validateValueAndPut(data, "FILE_DESCRIPTION", DESCRIPTION, params); + validateValueAndPut(data, "FILE_VERSION", VERSION, params); + data.put("INTERNAL_NAME", getLauncherName(params)); + validateValueAndPut(data, "LEGAL_COPYRIGHT", COPYRIGHT, params); + data.put("ORIGINAL_FILENAME", getLauncherName(params)); + validateValueAndPut(data, "PRODUCT_NAME", APP_NAME, params); + validateValueAndPut(data, "PRODUCT_VERSION", VERSION, params); + + createResource(EXECUTABLE_PROPERTIES_TEMPLATE, params) + .setCategory(I18N.getString("resource.executable-properties-template")) + .setSubstitutionData(data) + .saveToFile(getConfig_ExecutableProperties(params)); + } + + private void createLauncherForEntryPoint( + Map params) throws IOException { + + File iconTarget = getConfig_AppIcon(params); + + createResource(TEMPLATE_APP_ICON, params) + .setCategory("icon") + .setExternal(ICON_ICO.fetchFrom(params)) + .saveToFile(iconTarget); + + writeCfgFile(params, root.resolve( + getLauncherCfgName(params)).toFile()); + + prepareExecutableProperties(params); + + // Copy executable to bin folder + Path executableFile = binDir.resolve(getLauncherName(params)); + + try (InputStream is_launcher = + getResourceAsStream(getLauncherResourceName(params))) { + writeEntry(is_launcher, executableFile); + } + + File launcher = executableFile.toFile(); + launcher.setWritable(true, true); + + // Update branding of EXE file + if (REBRAND_EXECUTABLE.fetchFrom(params)) { + try { + String tempDirectory = WindowsDefender.getUserTempDirectory(); + if (Arguments.CLIOptions.context().userProvidedBuildRoot) { + tempDirectory = + TEMP_ROOT.fetchFrom(params).getAbsolutePath(); + } + if (WindowsDefender.isThereAPotentialWindowsDefenderIssue( + tempDirectory)) { + Log.verbose(MessageFormat.format(I18N.getString( + "message.potential.windows.defender.issue"), + tempDirectory)); + } + + launcher.setWritable(true); + + if (iconTarget.exists()) { + iconSwap(iconTarget.getAbsolutePath(), + launcher.getAbsolutePath()); + } + + File executableProperties = + getConfig_ExecutableProperties(params); + + if (executableProperties.exists()) { + if (versionSwap(executableProperties.getAbsolutePath(), + launcher.getAbsolutePath()) != 0) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.version-swap"), + executableProperties.getAbsolutePath())); + } + } + } finally { + executableFile.toFile().setExecutable(true); + executableFile.toFile().setReadOnly(); + } + } + + Files.copy(iconTarget.toPath(), + binDir.resolve(APP_NAME.fetchFrom(params) + ".ico")); + } + + private void copyApplication(Map params) + throws IOException { + List appResourcesList = + APP_RESOURCES_LIST.fetchFrom(params); + if (appResourcesList == null) { + throw new RuntimeException("Null app resources?"); + } + for (RelativeFileSet appResources : appResourcesList) { + if (appResources == null) { + throw new RuntimeException("Null app resources?"); + } + File srcdir = appResources.getBaseDirectory(); + for (String fname : appResources.getIncludedFiles()) { + copyEntry(appDir, srcdir, fname); + } + } + } + + private static native int iconSwap(String iconTarget, String launcher); + + private static native int versionSwap(String executableProperties, + String launcher); + +} --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsBundlerParam.java 2019-11-18 21:10:40.702859900 -0500 +++ /dev/null 2019-11-18 21:10:42.000000000 -0500 @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.text.MessageFormat; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.function.BiFunction; -import java.util.function.Function; - -class WindowsBundlerParam extends StandardBundlerParam { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.WinResources"); - - WindowsBundlerParam(String id, Class valueType, - Function, T> defaultValueFunction, - BiFunction, T> stringConverter) { - super(id, valueType, defaultValueFunction, stringConverter); - } - - static final BundlerParamInfo INSTALLER_FILE_NAME = - new StandardBundlerParam<> ( - "win.installerName", - String.class, - params -> { - String nm = APP_NAME.fetchFrom(params); - if (nm == null) return null; - - String version = VERSION.fetchFrom(params); - if (version == null) { - return nm; - } else { - return nm + "-" + version; - } - }, - (s, p) -> s); - - static final BundlerParamInfo APP_REGISTRY_NAME = - new StandardBundlerParam<> ( - Arguments.CLIOptions.WIN_REGISTRY_NAME.getId(), - String.class, - params -> { - String nm = APP_NAME.fetchFrom(params); - if (nm == null) return null; - - return nm.replaceAll("[^-a-zA-Z\\.0-9]", ""); - }, - (s, p) -> s); - - static final StandardBundlerParam MENU_GROUP = - new StandardBundlerParam<>( - Arguments.CLIOptions.WIN_MENU_GROUP.getId(), - String.class, - params -> I18N.getString("param.menu-group.default"), - (s, p) -> s - ); - - static final BundlerParamInfo INSTALLDIR_CHOOSER = - new StandardBundlerParam<> ( - Arguments.CLIOptions.WIN_DIR_CHOOSER.getId(), - Boolean.class, - params -> Boolean.FALSE, - (s, p) -> Boolean.valueOf(s) - ); - - static final BundlerParamInfo WINDOWS_INSTALL_DIR = - new StandardBundlerParam<>( - "windows-install-dir", - String.class, - params -> { - String dir = INSTALL_DIR.fetchFrom(params); - if (dir != null) { - if (dir.contains(":") || dir.contains("..")) { - Log.error(MessageFormat.format(I18N.getString( - "message.invalid.install.dir"), dir, - APP_NAME.fetchFrom(params))); - } else { - if (dir.startsWith("\\")) { - dir = dir.substring(1); - } - if (dir.endsWith("\\")) { - dir = dir.substring(0, dir.length() - 1); - } - return dir; - } - } - return APP_NAME.fetchFrom(params); // Default to app name - }, - (s, p) -> s - ); -} --- /dev/null 2019-11-18 21:10:42.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WindowsBundlerParam.java 2019-11-18 21:10:37.452264300 -0500 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.text.MessageFormat; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.function.BiFunction; +import java.util.function.Function; + +class WindowsBundlerParam extends StandardBundlerParam { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.incubator.jpackage.internal.resources.WinResources"); + + WindowsBundlerParam(String id, Class valueType, + Function, T> defaultValueFunction, + BiFunction, T> stringConverter) { + super(id, valueType, defaultValueFunction, stringConverter); + } + + static final BundlerParamInfo INSTALLER_FILE_NAME = + new StandardBundlerParam<> ( + "win.installerName", + String.class, + params -> { + String nm = APP_NAME.fetchFrom(params); + if (nm == null) return null; + + String version = VERSION.fetchFrom(params); + if (version == null) { + return nm; + } else { + return nm + "-" + version; + } + }, + (s, p) -> s); + + static final StandardBundlerParam MENU_GROUP = + new StandardBundlerParam<>( + Arguments.CLIOptions.WIN_MENU_GROUP.getId(), + String.class, + params -> I18N.getString("param.menu-group.default"), + (s, p) -> s + ); + + static final BundlerParamInfo INSTALLDIR_CHOOSER = + new StandardBundlerParam<> ( + Arguments.CLIOptions.WIN_DIR_CHOOSER.getId(), + Boolean.class, + params -> Boolean.FALSE, + (s, p) -> Boolean.valueOf(s) + ); + + static final BundlerParamInfo WINDOWS_INSTALL_DIR = + new StandardBundlerParam<>( + "windows-install-dir", + String.class, + params -> { + String dir = INSTALL_DIR.fetchFrom(params); + if (dir != null) { + if (dir.contains(":") || dir.contains("..")) { + Log.error(MessageFormat.format(I18N.getString( + "message.invalid.install.dir"), dir, + APP_NAME.fetchFrom(params))); + } else { + if (dir.startsWith("\\")) { + dir = dir.substring(1); + } + if (dir.endsWith("\\")) { + dir = dir.substring(0, dir.length() - 1); + } + return dir; + } + } + return APP_NAME.fetchFrom(params); // Default to app name + }, + (s, p) -> s + ); +} --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsDefender.java 2019-11-18 21:11:01.441529000 -0500 +++ /dev/null 2019-11-18 21:11:02.000000000 -0500 @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.List; - -final class WindowsDefender { - - private WindowsDefender() {} - - static final boolean isThereAPotentialWindowsDefenderIssue(String dir) { - boolean result = false; - - if (Platform.getPlatform() == Platform.WINDOWS && - Platform.getMajorVersion() == 10) { - - // If DisableRealtimeMonitoring is not enabled then there - // may be a problem. - if (!WindowsRegistry.readDisableRealtimeMonitoring() && - !isDirectoryInExclusionPath(dir)) { - result = true; - } - } - - return result; - } - - private static boolean isDirectoryInExclusionPath(String dir) { - boolean result = false; - // If the user temp directory is not found in the exclusion - // list then there may be a problem. - List paths = WindowsRegistry.readExclusionsPaths(); - for (String s : paths) { - if (WindowsRegistry.comparePaths(s, dir)) { - result = true; - break; - } - } - - return result; - } - - static final String getUserTempDirectory() { - String tempDirectory = System.getProperty("java.io.tmpdir"); - return tempDirectory; - } -} --- /dev/null 2019-11-18 21:11:03.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WindowsDefender.java 2019-11-18 21:10:58.141326700 -0500 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.util.List; + +final class WindowsDefender { + + private WindowsDefender() {} + + static final boolean isThereAPotentialWindowsDefenderIssue(String dir) { + boolean result = false; + + if (Platform.getPlatform() == Platform.WINDOWS && + Platform.getMajorVersion() == 10) { + + // If DisableRealtimeMonitoring is not enabled then there + // may be a problem. + if (!WindowsRegistry.readDisableRealtimeMonitoring() && + !isDirectoryInExclusionPath(dir)) { + result = true; + } + } + + return result; + } + + private static boolean isDirectoryInExclusionPath(String dir) { + boolean result = false; + // If the user temp directory is not found in the exclusion + // list then there may be a problem. + List paths = WindowsRegistry.readExclusionsPaths(); + for (String s : paths) { + if (WindowsRegistry.comparePaths(s, dir)) { + result = true; + break; + } + } + + return result; + } + + static final String getUserTempDirectory() { + String tempDirectory = System.getProperty("java.io.tmpdir"); + return tempDirectory; + } +} --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsRegistry.java 2019-11-18 21:11:21.836569700 -0500 +++ /dev/null 2019-11-18 21:11:23.000000000 -0500 @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.List; - -import static jdk.jpackage.internal.IOUtils.exec; - -final class WindowsRegistry { - - // Currently we only support HKEY_LOCAL_MACHINE. Native implementation will - // require support for additinal HKEY if needed. - private static final int HKEY_LOCAL_MACHINE = 1; - - static { - System.loadLibrary("jpackage"); - } - - private WindowsRegistry() {} - - /** - * Reads the registry value for DisableRealtimeMonitoring. - * @return true if DisableRealtimeMonitoring is set to 0x1, - * false otherwise. - */ - static final boolean readDisableRealtimeMonitoring() { - final String subKey = "Software\\Microsoft\\" - + "Windows Defender\\Real-Time Protection"; - final String value = "DisableRealtimeMonitoring"; - int result = readDwordValue(HKEY_LOCAL_MACHINE, subKey, value, 0); - return (result == 1); - } - - static final List readExclusionsPaths() { - List result = new ArrayList<>(); - final String subKey = "Software\\Microsoft\\" - + "Windows Defender\\Exclusions\\Paths"; - long lKey = openRegistryKey(HKEY_LOCAL_MACHINE, subKey); - if (lKey == 0) { - return result; - } - - String valueName; - int index = 0; - do { - valueName = enumRegistryValue(lKey, index); - if (valueName != null) { - result.add(valueName); - index++; - } - } while (valueName != null); - - closeRegistryKey(lKey); - - return result; - } - - /** - * Reads DWORD registry value. - * - * @param key one of HKEY predefine value - * @param subKey registry sub key - * @param value value to read - * @param defaultValue default value in case if subKey or value not found - * or any other errors occurred - * @return value's data only if it was read successfully, otherwise - * defaultValue - */ - private static native int readDwordValue(int key, String subKey, - String value, int defaultValue); - - /** - * Open registry key. - * - * @param key one of HKEY predefine value - * @param subKey registry sub key - * @return native handle to open key - */ - private static native long openRegistryKey(int key, String subKey); - - /** - * Enumerates the values for registry key. - * - * @param lKey native handle to open key returned by openRegistryKey - * @param index index of value starting from 0. Increment until this - * function returns NULL which means no more values. - * @return returns value or NULL if error or no more data - */ - private static native String enumRegistryValue(long lKey, int index); - - /** - * Close registry key. - * - * @param lKey native handle to open key returned by openRegistryKey - */ - private static native void closeRegistryKey(long lKey); - - /** - * Compares two Windows paths regardless case and if paths are short or long. - * - * @param path1 path to compare - * @param path2 path to compare - * @return true if paths point to same location - */ - public static native boolean comparePaths(String path1, String path2); -} --- /dev/null 2019-11-18 21:11:23.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WindowsRegistry.java 2019-11-18 21:11:18.465395400 -0500 @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +final class WindowsRegistry { + + // Currently we only support HKEY_LOCAL_MACHINE. Native implementation will + // require support for additinal HKEY if needed. + private static final int HKEY_LOCAL_MACHINE = 1; + + static { + System.loadLibrary("jpackage"); + } + + private WindowsRegistry() {} + + /** + * Reads the registry value for DisableRealtimeMonitoring. + * @return true if DisableRealtimeMonitoring is set to 0x1, + * false otherwise. + */ + static final boolean readDisableRealtimeMonitoring() { + final String subKey = "Software\\Microsoft\\" + + "Windows Defender\\Real-Time Protection"; + final String value = "DisableRealtimeMonitoring"; + int result = readDwordValue(HKEY_LOCAL_MACHINE, subKey, value, 0); + return (result == 1); + } + + static final List readExclusionsPaths() { + List result = new ArrayList<>(); + final String subKey = "Software\\Microsoft\\" + + "Windows Defender\\Exclusions\\Paths"; + long lKey = openRegistryKey(HKEY_LOCAL_MACHINE, subKey); + if (lKey == 0) { + return result; + } + + String valueName; + int index = 0; + do { + valueName = enumRegistryValue(lKey, index); + if (valueName != null) { + result.add(valueName); + index++; + } + } while (valueName != null); + + closeRegistryKey(lKey); + + return result; + } + + /** + * Reads DWORD registry value. + * + * @param key one of HKEY predefine value + * @param subKey registry sub key + * @param value value to read + * @param defaultValue default value in case if subKey or value not found + * or any other errors occurred + * @return value's data only if it was read successfully, otherwise + * defaultValue + */ + private static native int readDwordValue(int key, String subKey, + String value, int defaultValue); + + /** + * Open registry key. + * + * @param key one of HKEY predefine value + * @param subKey registry sub key + * @return native handle to open key + */ + private static native long openRegistryKey(int key, String subKey); + + /** + * Enumerates the values for registry key. + * + * @param lKey native handle to open key returned by openRegistryKey + * @param index index of value starting from 0. Increment until this + * function returns NULL which means no more values. + * @return returns value or NULL if error or no more data + */ + private static native String enumRegistryValue(long lKey, int index); + + /** + * Close registry key. + * + * @param lKey native handle to open key returned by openRegistryKey + */ + private static native void closeRegistryKey(long lKey); + + /** + * Compares two Windows paths regardless case and if paths + * are short or long. + * + * @param path1 path to compare + * @param path2 path to compare + * @return true if paths point to same location + */ + public static native boolean comparePaths(String path1, String path2); +} --- /dev/null 2019-11-18 21:11:41.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WixPipeline.java 2019-11-18 21:11:38.631516300 -0500 @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +/** + * WiX pipeline. Compiles and links WiX sources. + */ +public class WixPipeline { + WixPipeline() { + sources = new ArrayList<>(); + lightOptions = new ArrayList<>(); + } + + WixPipeline setToolset(Map v) { + toolset = v; + return this; + } + + WixPipeline setWixVariables(Map v) { + wixVariables = v; + return this; + } + + WixPipeline setWixObjDir(Path v) { + wixObjDir = v; + return this; + } + + WixPipeline setWorkDir(Path v) { + workDir = v; + return this; + } + + WixPipeline addSource(Path source, Map wixVariables) { + WixSource entry = new WixSource(); + entry.source = source; + entry.variables = wixVariables; + sources.add(entry); + return this; + } + + WixPipeline addLightOptions(String ... v) { + lightOptions.addAll(List.of(v)); + return this; + } + + void buildMsi(Path msi) throws IOException { + List wixObjs = new ArrayList<>(); + for (var source : sources) { + wixObjs.add(compile(source)); + } + + List lightCmdline = new ArrayList<>(List.of( + toolset.get(WixTool.Light).toString(), + "-nologo", + "-spdb", + "-ext", "WixUtilExtension", + "-out", msi.toString() + )); + + lightCmdline.addAll(lightOptions); + wixObjs.stream().map(Path::toString).forEach(lightCmdline::add); + + Files.createDirectories(msi.getParent()); + execute(lightCmdline); + } + + private Path compile(WixSource wixSource) throws IOException { + UnaryOperator adjustPath = path -> { + return workDir != null ? path.toAbsolutePath() : path; + }; + + Path wixObj = adjustPath.apply(wixObjDir).resolve(IOUtils.replaceSuffix( + wixSource.source.getFileName(), ".wixobj")); + + List cmdline = new ArrayList<>(List.of( + toolset.get(WixTool.Candle).toString(), + "-nologo", + adjustPath.apply(wixSource.source).toString(), + "-ext", "WixUtilExtension", + "-arch", "x64", + "-out", wixObj.toAbsolutePath().toString() + )); + + Map appliedVaribales = new HashMap<>(); + Stream.of(wixVariables, wixSource.variables) + .filter(Objects::nonNull) + .forEachOrdered(appliedVaribales::putAll); + + appliedVaribales.entrySet().stream().map(wixVar -> String.format("-d%s=%s", + wixVar.getKey(), wixVar.getValue())).forEachOrdered( + cmdline::add); + + execute(cmdline); + + return wixObj; + } + + private void execute(List cmdline) throws IOException { + Executor.of(new ProcessBuilder(cmdline).directory( + workDir != null ? workDir.toFile() : null)).executeExpectSuccess(); + } + + private final static class WixSource { + Path source; + Map variables; + } + + private Map toolset; + private Map wixVariables; + private List lightOptions; + private Path wixObjDir; + private Path workDir; + private List sources; +} --- /dev/null 2019-11-18 21:11:52.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WixSourcesBuilder.java 2019-11-18 21:11:49.614467000 -0500 @@ -0,0 +1,847 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.*; +import java.util.function.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import jdk.incubator.jpackage.internal.IOUtils.XmlConsumer; +import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; +import static jdk.incubator.jpackage.internal.WinMsiBundler.*; +import static jdk.incubator.jpackage.internal.WindowsBundlerParam.MENU_GROUP; +import static jdk.incubator.jpackage.internal.WindowsBundlerParam.WINDOWS_INSTALL_DIR; + +/** + * Creates application WiX source files. + */ +class WixSourcesBuilder { + + WixSourcesBuilder setWixVersion(DottedVersion v) { + wixVersion = v; + return this; + } + + WixSourcesBuilder initFromParams(Path appImageRoot, + Map params) { + Supplier appImageSupplier = () -> { + if (StandardBundlerParam.isRuntimeInstaller(params)) { + return ApplicationLayout.javaRuntime(); + } else { + return ApplicationLayout.platformAppImage(); + } + }; + + systemWide = MSI_SYSTEM_WIDE.fetchFrom(params); + + registryKeyPath = Path.of("Software", + VENDOR.fetchFrom(params), + APP_NAME.fetchFrom(params), + VERSION.fetchFrom(params)).toString(); + + installDir = (systemWide ? PROGRAM_FILES : LOCAL_PROGRAM_FILES).resolve( + WINDOWS_INSTALL_DIR.fetchFrom(params)); + + do { + ApplicationLayout layout = appImageSupplier.get(); + // Don't want AppImageFile.FILENAME in installed application. + // Register it with app image at a role without a match in installed + // app layout to exclude it from layout transformation. + layout.pathGroup().setPath(new Object(), + AppImageFile.getPathInAppImage(Path.of(""))); + + // Want absolute paths to source files in generated WiX sources. + // This is to handle scenario if sources would be processed from + // differnt current directory. + appImage = layout.resolveAt(appImageRoot.toAbsolutePath().normalize()); + } while (false); + + installedAppImage = appImageSupplier.get().resolveAt(INSTALLDIR); + + shortcutFolders = new HashSet<>(); + if (SHORTCUT_HINT.fetchFrom(params)) { + shortcutFolders.add(ShortcutsFolder.Desktop); + } + if (MENU_HINT.fetchFrom(params)) { + shortcutFolders.add(ShortcutsFolder.ProgramMenu); + } + + if (StandardBundlerParam.isRuntimeInstaller(params)) { + launcherPaths = Collections.emptyList(); + } else { + launcherPaths = AppImageFile.getLauncherNames(appImageRoot, params).stream() + .map(name -> installedAppImage.launchersDirectory().resolve(name)) + .map(WixSourcesBuilder::addExeSuffixToPath) + .collect(Collectors.toList()); + } + + programMenuFolderName = MENU_GROUP.fetchFrom(params); + + initFileAssociations(params); + + return this; + } + + void createMainFragment(Path file) throws IOException { + removeFolderItems = new HashMap<>(); + defaultedMimes = new HashSet<>(); + IOUtils.createXml(file, xml -> { + xml.writeStartElement("Wix"); + xml.writeDefaultNamespace("http://schemas.microsoft.com/wix/2006/wi"); + xml.writeNamespace("util", + "http://schemas.microsoft.com/wix/UtilExtension"); + + xml.writeStartElement("Fragment"); + + addFaComponentGroup(xml); + + addShortcutComponentGroup(xml); + + addFilesComponentGroup(xml); + + xml.writeEndElement(); // + + addIconsFragment(xml); + + xml.writeEndElement(); // + }); + } + + void logWixFeatures() { + if (wixVersion.compareTo("3.6") >= 0) { + Log.verbose(MessageFormat.format(I18N.getString( + "message.use-wix36-features"), wixVersion)); + } + } + + private void normalizeFileAssociation(FileAssociation fa) { + fa.launcherPath = addExeSuffixToPath( + installedAppImage.launchersDirectory().resolve(fa.launcherPath)); + + if (fa.iconPath != null && !fa.iconPath.toFile().exists()) { + fa.iconPath = null; + } + + if (fa.iconPath != null) { + fa.iconPath = fa.iconPath.toAbsolutePath(); + } + + // Filter out empty extensions. + fa.extensions = fa.extensions.stream().filter(Predicate.not( + String::isEmpty)).collect(Collectors.toList()); + } + + private static Path addExeSuffixToPath(Path path) { + return IOUtils.addSuffix(path, ".exe"); + } + + private Path getInstalledFaIcoPath(FileAssociation fa) { + String fname = String.format("fa_%s.ico", String.join("_", fa.extensions)); + return installedAppImage.destktopIntegrationDirectory().resolve(fname); + } + + private void initFileAssociations(Map params) { + associations = FileAssociation.fetchFrom(params).stream() + .peek(this::normalizeFileAssociation) + // Filter out file associations without extensions. + .filter(fa -> !fa.extensions.isEmpty()) + .collect(Collectors.toList()); + + associations.stream().filter(fa -> fa.iconPath != null).forEach(fa -> { + // Need to add fa icon in the image. + Object key = new Object(); + appImage.pathGroup().setPath(key, fa.iconPath); + installedAppImage.pathGroup().setPath(key, getInstalledFaIcoPath(fa)); + }); + } + + private static UUID createNameUUID(String str) { + return UUID.nameUUIDFromBytes(str.getBytes(StandardCharsets.UTF_8)); + } + + private static UUID createNameUUID(Path path, String role) { + if (path.isAbsolute() || !ROOT_DIRS.contains(path.getName(0))) { + throw throwInvalidPathException(path); + } + // Paths are case insensitive on Windows + String keyPath = path.toString().toLowerCase(); + if (role != null) { + keyPath = role + "@" + keyPath; + } + return createNameUUID(keyPath); + } + + /** + * Value for Id attribute of various WiX elements. + */ + enum Id { + File, + Folder("dir"), + Shortcut, + ProgId, + Icon, + CreateFolder("mkdir"), + RemoveFolder("rm"); + + Id() { + this.prefix = name().toLowerCase(); + } + + Id(String prefix) { + this.prefix = prefix; + } + + String of(Path path) { + if (this == Folder && KNOWN_DIRS.contains(path)) { + return path.getFileName().toString(); + } + + String result = of(path, prefix, name()); + + if (this == Icon) { + // Icon id constructed from UUID value is too long and triggers + // CNDL1000 warning, so use Java hash code instead. + result = String.format("%s%d", prefix, result.hashCode()).replace( + "-", "_"); + } + + return result; + } + + private static String of(Path path, String prefix, String role) { + Objects.requireNonNull(role); + Objects.requireNonNull(prefix); + return String.format("%s%s", prefix, + createNameUUID(path, role).toString().replace("-", "")); + } + + static String of(Path path, String prefix) { + return of(path, prefix, prefix); + } + + private final String prefix; + } + + enum Component { + File(cfg().file()), + Shortcut(cfg().file().withRegistryKeyPath()), + ProgId(cfg().file().withRegistryKeyPath()), + CreateFolder(cfg().withRegistryKeyPath()), + RemoveFolder(cfg().withRegistryKeyPath()); + + Component() { + this.cfg = cfg(); + this.id = Id.valueOf(name()); + } + + Component(Config cfg) { + this.cfg = cfg; + this.id = Id.valueOf(name()); + } + + UUID guidOf(Path path) { + return createNameUUID(path, name()); + } + + String idOf(Path path) { + return id.of(path); + } + + boolean isRegistryKeyPath() { + return cfg.withRegistryKeyPath; + } + + boolean isFile() { + return cfg.isFile; + } + + static void startElement(XMLStreamWriter xml, String componentId, + String componentGuid) throws XMLStreamException, IOException { + xml.writeStartElement("Component"); + xml.writeAttribute("Win64", "yes"); + xml.writeAttribute("Id", componentId); + xml.writeAttribute("Guid", componentGuid); + } + + private static final class Config { + Config withRegistryKeyPath() { + withRegistryKeyPath = true; + return this; + } + + Config file() { + isFile = true; + return this; + } + + private boolean isFile; + private boolean withRegistryKeyPath; + } + + private static Config cfg() { + return new Config(); + } + + private final Config cfg; + private final Id id; + }; + + private static void addComponentGroup(XMLStreamWriter xml, String id, + List componentIds) throws XMLStreamException, IOException { + xml.writeStartElement("ComponentGroup"); + xml.writeAttribute("Id", id); + componentIds = componentIds.stream().filter(Objects::nonNull).collect( + Collectors.toList()); + for (var componentId : componentIds) { + xml.writeStartElement("ComponentRef"); + xml.writeAttribute("Id", componentId); + xml.writeEndElement(); + } + xml.writeEndElement(); + } + + private String addComponent(XMLStreamWriter xml, Path path, + Component role, XmlConsumer xmlConsumer) throws XMLStreamException, + IOException { + + final Path directoryRefPath; + if (role.isFile()) { + directoryRefPath = path.getParent(); + } else { + directoryRefPath = path; + } + + xml.writeStartElement("DirectoryRef"); + xml.writeAttribute("Id", Id.Folder.of(directoryRefPath)); + + final String componentId = "c" + role.idOf(path); + Component.startElement(xml, componentId, String.format("{%s}", + role.guidOf(path))); + + boolean isRegistryKeyPath = !systemWide || role.isRegistryKeyPath(); + if (isRegistryKeyPath) { + addRegistryKeyPath(xml, directoryRefPath); + if ((role.isFile() || (role == Component.CreateFolder + && !systemWide)) && !SYSTEM_DIRS.contains(directoryRefPath)) { + xml.writeStartElement("RemoveFolder"); + int counter = Optional.ofNullable(removeFolderItems.get( + directoryRefPath)).orElse(Integer.valueOf(0)).intValue() + 1; + removeFolderItems.put(directoryRefPath, counter); + xml.writeAttribute("Id", String.format("%s_%d", Id.RemoveFolder.of( + directoryRefPath), counter)); + xml.writeAttribute("On", "uninstall"); + xml.writeEndElement(); + } + } + + xml.writeStartElement(role.name()); + if (role != Component.CreateFolder) { + xml.writeAttribute("Id", role.idOf(path)); + } + + if (!isRegistryKeyPath) { + xml.writeAttribute("KeyPath", "yes"); + } + + xmlConsumer.accept(xml); + xml.writeEndElement(); + + xml.writeEndElement(); // + xml.writeEndElement(); // + + return componentId; + } + + private void addFaComponentGroup(XMLStreamWriter xml) + throws XMLStreamException, IOException { + + List componentIds = new ArrayList<>(); + for (var fa : associations) { + componentIds.addAll(addFaComponents(xml, fa)); + } + addComponentGroup(xml, "FileAssociations", componentIds); + } + + private void addShortcutComponentGroup(XMLStreamWriter xml) throws + XMLStreamException, IOException { + List componentIds = new ArrayList<>(); + Set defineShortcutFolders = new HashSet<>(); + for (var launcherPath : launcherPaths) { + for (var folder : shortcutFolders) { + String componentId = addShortcutComponent(xml, launcherPath, + folder); + if (componentId != null) { + defineShortcutFolders.add(folder); + componentIds.add(componentId); + } + } + } + + for (var folder : defineShortcutFolders) { + Path path = folder.getPath(this); + componentIds.addAll(addRootBranch(xml, path)); + } + + addComponentGroup(xml, "Shortcuts", componentIds); + } + + private String addShortcutComponent(XMLStreamWriter xml, Path launcherPath, + ShortcutsFolder folder) throws XMLStreamException, IOException { + Objects.requireNonNull(folder); + + if (!INSTALLDIR.equals(launcherPath.getName(0))) { + throw throwInvalidPathException(launcherPath); + } + + String launcherBasename = IOUtils.replaceSuffix( + launcherPath.getFileName(), "").toString(); + + Path shortcutPath = folder.getPath(this).resolve(launcherBasename); + return addComponent(xml, shortcutPath, Component.Shortcut, unused -> { + final Path icoFile = IOUtils.addSuffix( + installedAppImage.destktopIntegrationDirectory().resolve( + launcherBasename), ".ico"); + + xml.writeAttribute("Name", launcherBasename); + xml.writeAttribute("WorkingDirectory", INSTALLDIR.toString()); + xml.writeAttribute("Advertise", "no"); + xml.writeAttribute("IconIndex", "0"); + xml.writeAttribute("Target", String.format("[#%s]", + Component.File.idOf(launcherPath))); + xml.writeAttribute("Icon", Id.Icon.of(icoFile)); + }); + } + + private List addFaComponents(XMLStreamWriter xml, + FileAssociation fa) throws XMLStreamException, IOException { + List components = new ArrayList<>(); + for (var extension: fa.extensions) { + Path path = INSTALLDIR.resolve(String.format("%s_%s", extension, + fa.launcherPath.getFileName())); + components.add(addComponent(xml, path, Component.ProgId, unused -> { + xml.writeAttribute("Description", fa.description); + + if (fa.iconPath != null) { + xml.writeAttribute("Icon", Id.File.of(getInstalledFaIcoPath( + fa))); + xml.writeAttribute("IconIndex", "0"); + } + + xml.writeStartElement("Extension"); + xml.writeAttribute("Id", extension); + xml.writeAttribute("Advertise", "no"); + + var mimeIt = fa.mimeTypes.iterator(); + if (mimeIt.hasNext()) { + String mime = mimeIt.next(); + xml.writeAttribute("ContentType", mime); + + if (!defaultedMimes.contains(mime)) { + xml.writeStartElement("MIME"); + xml.writeAttribute("ContentType", mime); + xml.writeAttribute("Default", "yes"); + xml.writeEndElement(); + defaultedMimes.add(mime); + } + } + + xml.writeStartElement("Verb"); + xml.writeAttribute("Id", "open"); + xml.writeAttribute("Command", "Open"); + xml.writeAttribute("Argument", "%1"); + xml.writeAttribute("TargetFile", Id.File.of(fa.launcherPath)); + xml.writeEndElement(); // + + xml.writeEndElement(); // + })); + } + + return components; + } + + private List addRootBranch(XMLStreamWriter xml, Path path) + throws XMLStreamException, IOException { + if (!ROOT_DIRS.contains(path.getName(0))) { + throw throwInvalidPathException(path); + } + + Function createDirectoryName = dir -> null; + + boolean sysDir = true; + int levels = 1; + var dirIt = path.iterator(); + xml.writeStartElement("DirectoryRef"); + xml.writeAttribute("Id", dirIt.next().toString()); + + path = path.getName(0); + while (dirIt.hasNext()) { + levels++; + Path name = dirIt.next(); + path = path.resolve(name); + + if (sysDir && !SYSTEM_DIRS.contains(path)) { + sysDir = false; + createDirectoryName = dir -> dir.getFileName().toString(); + } + + final String directoryId; + if (!sysDir && path.equals(installDir)) { + directoryId = INSTALLDIR.toString(); + } else { + directoryId = Id.Folder.of(path); + } + xml.writeStartElement("Directory"); + xml.writeAttribute("Id", directoryId); + + String directoryName = createDirectoryName.apply(path); + if (directoryName != null) { + xml.writeAttribute("Name", directoryName); + } + } + + while (0 != levels--) { + xml.writeEndElement(); + } + + List componentIds = new ArrayList<>(); + while (!SYSTEM_DIRS.contains(path = path.getParent())) { + componentIds.add(addRemoveDirectoryComponent(xml, path)); + } + + return componentIds; + } + + private String addRemoveDirectoryComponent(XMLStreamWriter xml, Path path) + throws XMLStreamException, IOException { + return addComponent(xml, path, Component.RemoveFolder, + unused -> xml.writeAttribute("On", "uninstall")); + } + + private List addDirectoryHierarchy(XMLStreamWriter xml) + throws XMLStreamException, IOException { + + Set allDirs = new HashSet<>(); + Set emptyDirs = new HashSet<>(); + appImage.transform(installedAppImage, new PathGroup.TransformHandler() { + @Override + public void copyFile(Path src, Path dst) throws IOException { + Path dir = dst.getParent(); + createDirectory(dir); + emptyDirs.remove(dir); + } + + @Override + public void createDirectory(final Path dir) throws IOException { + if (!allDirs.contains(dir)) { + emptyDirs.add(dir); + } + + Path it = dir; + while (it != null && allDirs.add(it)) { + it = it.getParent(); + } + + it = dir; + while ((it = it.getParent()) != null && emptyDirs.remove(it)); + } + }); + + List componentIds = new ArrayList<>(); + for (var dir : emptyDirs) { + componentIds.add(addComponent(xml, dir, Component.CreateFolder, + unused -> {})); + } + + if (!systemWide) { + // Per-user install requires component in every + // directory. + for (var dir : allDirs.stream() + .filter(Predicate.not(emptyDirs::contains)) + .filter(Predicate.not(removeFolderItems::containsKey)) + .collect(Collectors.toList())) { + componentIds.add(addRemoveDirectoryComponent(xml, dir)); + } + } + + allDirs.remove(INSTALLDIR); + for (var dir : allDirs) { + xml.writeStartElement("DirectoryRef"); + xml.writeAttribute("Id", Id.Folder.of(dir.getParent())); + xml.writeStartElement("Directory"); + xml.writeAttribute("Id", Id.Folder.of(dir)); + xml.writeAttribute("Name", dir.getFileName().toString()); + xml.writeEndElement(); + xml.writeEndElement(); + } + + componentIds.addAll(addRootBranch(xml, installDir)); + + return componentIds; + } + + private void addFilesComponentGroup(XMLStreamWriter xml) + throws XMLStreamException, IOException { + + List> files = new ArrayList<>(); + appImage.transform(installedAppImage, new PathGroup.TransformHandler() { + @Override + public void copyFile(Path src, Path dst) throws IOException { + files.add(Map.entry(src, dst)); + } + + @Override + public void createDirectory(final Path dir) throws IOException { + } + }); + + List componentIds = new ArrayList<>(); + for (var file : files) { + Path src = file.getKey(); + Path dst = file.getValue(); + + componentIds.add(addComponent(xml, dst, Component.File, unused -> { + xml.writeAttribute("Source", src.normalize().toString()); + Path name = dst.getFileName(); + if (!name.equals(src.getFileName())) { + xml.writeAttribute("Name", name.toString()); + } + })); + } + + componentIds.addAll(addDirectoryHierarchy(xml)); + + componentIds.add(addDirectoryCleaner(xml, INSTALLDIR)); + + addComponentGroup(xml, "Files", componentIds); + } + + private void addIconsFragment(XMLStreamWriter xml) throws + XMLStreamException, IOException { + + PathGroup srcPathGroup = appImage.pathGroup(); + PathGroup dstPathGroup = installedAppImage.pathGroup(); + + // Build list of copy operations for all .ico files in application image + List> icoFiles = new ArrayList<>(); + srcPathGroup.transform(dstPathGroup, new PathGroup.TransformHandler() { + @Override + public void copyFile(Path src, Path dst) throws IOException { + if (src.getFileName().toString().endsWith(".ico")) { + icoFiles.add(Map.entry(src, dst)); + } + } + + @Override + public void createDirectory(Path dst) throws IOException { + } + }); + + xml.writeStartElement("Fragment"); + for (var icoFile : icoFiles) { + xml.writeStartElement("Icon"); + xml.writeAttribute("Id", Id.Icon.of(icoFile.getValue())); + xml.writeAttribute("SourceFile", icoFile.getKey().toString()); + xml.writeEndElement(); + } + xml.writeEndElement(); + } + + private void addRegistryKeyPath(XMLStreamWriter xml, Path path) throws + XMLStreamException, IOException { + addRegistryKeyPath(xml, path, () -> "ProductCode", () -> "[ProductCode]"); + } + + private void addRegistryKeyPath(XMLStreamWriter xml, Path path, + Supplier nameAttr, Supplier valueAttr) throws + XMLStreamException, IOException { + + String regRoot = USER_PROFILE_DIRS.stream().anyMatch(path::startsWith) + || !systemWide ? "HKCU" : "HKLM"; + + xml.writeStartElement("RegistryKey"); + xml.writeAttribute("Root", regRoot); + xml.writeAttribute("Key", registryKeyPath); + if (wixVersion.compareTo("3.6") < 0) { + xml.writeAttribute("Action", "createAndRemoveOnUninstall"); + } + xml.writeStartElement("RegistryValue"); + xml.writeAttribute("Type", "string"); + xml.writeAttribute("KeyPath", "yes"); + xml.writeAttribute("Name", nameAttr.get()); + xml.writeAttribute("Value", valueAttr.get()); + xml.writeEndElement(); // + xml.writeEndElement(); // + } + + private String addDirectoryCleaner(XMLStreamWriter xml, Path path) throws + XMLStreamException, IOException { + if (wixVersion.compareTo("3.6") < 0) { + return null; + } + + // rm -rf + final String baseId = Id.of(path, "rm_rf"); + final String propertyId = baseId.toUpperCase(); + final String componentId = ("c" + baseId); + + xml.writeStartElement("Property"); + xml.writeAttribute("Id", propertyId); + xml.writeStartElement("RegistrySearch"); + xml.writeAttribute("Id", Id.of(path, "regsearch")); + xml.writeAttribute("Root", systemWide ? "HKLM" : "HKCU"); + xml.writeAttribute("Key", registryKeyPath); + xml.writeAttribute("Type", "raw"); + xml.writeAttribute("Name", propertyId); + xml.writeEndElement(); // + xml.writeEndElement(); // + + xml.writeStartElement("DirectoryRef"); + xml.writeAttribute("Id", INSTALLDIR.toString()); + Component.startElement(xml, componentId, "*"); + + addRegistryKeyPath(xml, INSTALLDIR, () -> propertyId, () -> { + // The following code converts a path to value to be saved in registry. + // E.g.: + // INSTALLDIR -> [INSTALLDIR] + // TERGETDIR/ProgramFiles64Folder/foo/bar -> [ProgramFiles64Folder]foo/bar + final Path rootDir = KNOWN_DIRS.stream() + .sorted(Comparator.comparing(Path::getNameCount).reversed()) + .filter(path::startsWith) + .findFirst().get(); + StringBuilder sb = new StringBuilder(); + sb.append(String.format("[%s]", rootDir.getFileName().toString())); + sb.append(rootDir.relativize(path).toString()); + return sb.toString(); + }); + + xml.writeStartElement( + "http://schemas.microsoft.com/wix/UtilExtension", + "RemoveFolderEx"); + xml.writeAttribute("On", "uninstall"); + xml.writeAttribute("Property", propertyId); + xml.writeEndElement(); // + xml.writeEndElement(); // + xml.writeEndElement(); // + + return componentId; + } + + private static IllegalArgumentException throwInvalidPathException(Path v) { + throw new IllegalArgumentException(String.format("Invalid path [%s]", v)); + } + + enum ShortcutsFolder { + ProgramMenu(PROGRAM_MENU_PATH), + Desktop(DESKTOP_PATH); + + private ShortcutsFolder(Path root) { + this.root = root; + } + + Path getPath(WixSourcesBuilder outer) { + if (this == ProgramMenu) { + return root.resolve(outer.programMenuFolderName); + } + return root; + } + + private final Path root; + } + + private DottedVersion wixVersion; + + private boolean systemWide; + + private String registryKeyPath; + + private Path installDir; + + private String programMenuFolderName; + + private List associations; + + private Set shortcutFolders; + + private List launcherPaths; + + private ApplicationLayout appImage; + private ApplicationLayout installedAppImage; + + private Map removeFolderItems; + private Set defaultedMimes; + + private final static Path TARGETDIR = Path.of("TARGETDIR"); + + private final static Path INSTALLDIR = Path.of("INSTALLDIR"); + + private final static Set ROOT_DIRS = Set.of(INSTALLDIR, TARGETDIR); + + private final static Path PROGRAM_MENU_PATH = TARGETDIR.resolve("ProgramMenuFolder"); + + private final static Path DESKTOP_PATH = TARGETDIR.resolve("DesktopFolder"); + + private final static Path PROGRAM_FILES = TARGETDIR.resolve("ProgramFiles64Folder"); + + private final static Path LOCAL_PROGRAM_FILES = TARGETDIR.resolve("LocalAppDataFolder"); + + private final static Set SYSTEM_DIRS = Set.of(TARGETDIR, + PROGRAM_MENU_PATH, DESKTOP_PATH, PROGRAM_FILES, LOCAL_PROGRAM_FILES); + + private final static Set KNOWN_DIRS = Stream.of(Set.of(INSTALLDIR), + SYSTEM_DIRS).flatMap(Set::stream).collect( + Collectors.toUnmodifiableSet()); + + private final static Set USER_PROFILE_DIRS = Set.of(LOCAL_PROGRAM_FILES, + PROGRAM_MENU_PATH, DESKTOP_PATH); + + private static final StandardBundlerParam MENU_HINT = + new WindowsBundlerParam<>( + Arguments.CLIOptions.WIN_MENU_HINT.getId(), + Boolean.class, + params -> false, + // valueOf(null) is false, + // and we actually do want null in some cases + (s, p) -> (s == null || + "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s) + ); + + private static final StandardBundlerParam SHORTCUT_HINT = + new WindowsBundlerParam<>( + Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(), + Boolean.class, + params -> false, + // valueOf(null) is false, + // and we actually do want null in some cases + (s, p) -> (s == null || + "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s) + ); +} --- /dev/null 2019-11-18 21:12:03.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WixTool.java 2019-11-18 21:12:00.261755300 -0500 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.jpackage.internal; + +import java.io.IOException; +import java.nio.file.*; +import java.text.MessageFormat; +import java.util.*; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * WiX tool. + */ +public enum WixTool { + Candle, Light; + + static final class ToolInfo { + ToolInfo(Path path, String version) { + this.path = path; + this.version = new DottedVersion(version); + } + + final Path path; + final DottedVersion version; + } + + static Map toolset() throws ConfigException { + Map toolset = new HashMap<>(); + for (var tool : values()) { + toolset.put(tool, tool.find()); + } + return toolset; + } + + ToolInfo find() throws ConfigException { + final Path toolFileName = IOUtils.addSuffix( + Path.of(name().toLowerCase()), ".exe"); + + String[] version = new String[1]; + ConfigException reason = createToolValidator(toolFileName, version).get(); + if (version[0] != null) { + if (reason == null) { + // Found in PATH. + return new ToolInfo(toolFileName, version[0]); + } + + // Found in PATH, but something went wrong. + throw reason; + } + + for (var dir : findWixInstallDirs()) { + Path path = dir.resolve(toolFileName); + if (path.toFile().exists()) { + reason = createToolValidator(path, version).get(); + if (reason != null) { + throw reason; + } + return new ToolInfo(path, version[0]); + } + } + + throw reason; + } + + private static Supplier createToolValidator(Path toolPath, + String[] versionCtnr) { + return new ToolValidator(toolPath) + .setCommandLine("/?") + .setMinimalVersion(MINIMAL_VERSION) + .setToolNotFoundErrorHandler( + (name, ex) -> new ConfigException( + I18N.getString("error.no-wix-tools"), + I18N.getString("error.no-wix-tools.advice"))) + .setToolOldVersionErrorHandler( + (name, version) -> new ConfigException( + MessageFormat.format(I18N.getString( + "message.wrong-tool-version"), name, + version, MINIMAL_VERSION), + I18N.getString("error.no-wix-tools.advice"))) + .setVersionParser(output -> { + versionCtnr[0] = ""; + String firstLineOfOutput = output.findFirst().orElse(""); + int separatorIdx = firstLineOfOutput.lastIndexOf(' '); + if (separatorIdx == -1) { + return null; + } + versionCtnr[0] = firstLineOfOutput.substring(separatorIdx + 1); + return versionCtnr[0]; + })::validate; + } + + private final static DottedVersion MINIMAL_VERSION = DottedVersion.lazy("3.0"); + + static Path getSystemDir(String envVar, String knownDir) { + return Optional + .ofNullable(getEnvVariableAsPath(envVar)) + .orElseGet(() -> Optional + .ofNullable(getEnvVariableAsPath("SystemDrive")) + .orElseGet(() -> Path.of("C:")).resolve(knownDir)); + } + + private static Path getEnvVariableAsPath(String envVar) { + String path = System.getenv(envVar); + if (path != null) { + try { + return Path.of(path); + } catch (InvalidPathException ex) { + Log.error(MessageFormat.format(I18N.getString( + "error.invalid-envvar"), envVar)); + } + } + return null; + } + + private static List findWixInstallDirs() { + PathMatcher wixInstallDirMatcher = FileSystems.getDefault().getPathMatcher( + "glob:WiX Toolset v*"); + + Path programFiles = getSystemDir("ProgramFiles", "\\Program Files"); + Path programFilesX86 = getSystemDir("ProgramFiles(x86)", + "\\Program Files (x86)"); + + // Returns list of WiX install directories ordered by WiX version number. + // Newer versions go first. + return Stream.of(programFiles, programFilesX86).map(path -> { + List result; + try (var paths = Files.walk(path, 1)) { + result = paths.collect(Collectors.toList()); + } catch (IOException ex) { + Log.verbose(ex); + result = Collections.emptyList(); + } + return result; + }).flatMap(List::stream) + .filter(path -> wixInstallDirMatcher.matches(path.getFileName())) + .sorted(Comparator.comparing(Path::getFileName).reversed()) + .map(path -> path.resolve("bin")) + .collect(Collectors.toList()); + } +} --- /dev/null 2019-11-18 21:12:13.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/MsiInstallerStrings_en.wxl 2019-11-18 21:12:10.742137800 -0500 @@ -0,0 +1,7 @@ + + + The folder [INSTALLDIR] already exist. Would you like to install to that folder anyway? + Main Feature + A higher version of [ProductName] is already installed. Downgrades disabled. Setup will now exit. + A lower version of [ProductName] is already installed. Upgrades disabled. Setup will now exit. + --- /dev/null 2019-11-18 21:12:24.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/MsiInstallerStrings_ja.wxl 2019-11-18 21:12:21.365036100 -0500 @@ -0,0 +1,7 @@ + + + The folder [INSTALLDIR] already exist. Would you like to install to that folder anyway? + Main Feature + A higher version of [ProductName] is already installed. Downgrades disabled. Setup will now exit. + A lower version of [ProductName] is already installed. Upgrades disabled. Setup will now exit. + --- /dev/null 2019-11-18 21:12:35.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/MsiInstallerStrings_zh_CN.wxl 2019-11-18 21:12:31.990596600 -0500 @@ -0,0 +1,7 @@ + + + The folder [INSTALLDIR] already exist. Would you like to install to that folder anyway? + Main Feature + A higher version of [ProductName] is already installed. Downgrades disabled. Setup will now exit. + A lower version of [ProductName] is already installed. Upgrades disabled. Setup will now exit. + --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinLauncher.template 2019-11-18 21:12:46.022222700 -0500 +++ /dev/null 2019-11-18 21:12:47.000000000 -0500 @@ -1,34 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -CompanyName=COMPANY_NAME -FileDescription=FILE_DESCRIPTION -FileVersion=FILE_VERSION -InternalName=INTERNAL_NAME -LegalCopyright=LEGAL_COPYRIGHT -OriginalFilename=ORIGINAL_FILENAME -ProductName=PRODUCT_NAME -ProductVersion=PRODUCT_VERSION --- /dev/null 2019-11-18 21:12:47.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/WinLauncher.template 2019-11-18 21:12:42.534682700 -0500 @@ -0,0 +1,34 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +CompanyName=COMPANY_NAME +FileDescription=FILE_DESCRIPTION +FileVersion=FILE_VERSION +InternalName=INTERNAL_NAME +LegalCopyright=LEGAL_COPYRIGHT +OriginalFilename=ORIGINAL_FILENAME +ProductName=PRODUCT_NAME +ProductVersion=PRODUCT_VERSION --- /dev/null 2019-11-18 21:12:56.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/WinResources.properties 2019-11-18 21:12:53.159377800 -0500 @@ -0,0 +1,67 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +app.bundler.name=Windows Application Image +exe.bundler.name=EXE Installer Package +msi.bundler.name=MSI Installer Package + +param.menu-group.default=Unknown + +resource.executable-properties-template=Template for creating executable properties file +resource.setup-icon=setup dialog icon +resource.post-app-image-script=script to run after application image is populated +resource.post-msi-script=script to run after msi file for exe installer is created +resource.wxl-file-name=MsiInstallerStrings_en.wxl +resource.main-wix-file=Main WiX project file +resource.overrides-wix-file=Overrides WiX project file + +error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe) +error.no-wix-tools.advice=Download WiX 3.0 or later from https://wixtoolset.org and add it to the PATH. +error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}] +error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: https://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx . +error.version-string-major-out-of-range=Major version must be in the range [0, 255] +error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535] +error.version-string-minor-out-of-range=Minor version must be in the range [0, 255] +error.version-string-part-not-number=Failed to convert version component to int +error.version-swap=Failed to update version information for {0} +error.invalid-envvar=Invalid value of {0} environment variable + +message.result-dir=Result application bundle: {0}. +message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. +message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". +message.outputting-to-location=Generating EXE for installer to: {0}. +message.output-location=Installer (.exe) saved to: {0} +message.tool-version=Detected [{0}] version [{1}]. +message.creating-association-with-null-extension=Creating association with null extension. +message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. +message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . +message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. +message.product-code=MSI ProductCode: {0}. +message.upgrade-code=MSI UpgradeCode: {0}. +message.preparing-msi-config=Preparing MSI config: {0}. +message.generating-msi=Generating MSI: {0}. +message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}". + --- /dev/null 2019-11-18 21:13:06.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/WinResources_ja.properties 2019-11-18 21:13:03.922839800 -0500 @@ -0,0 +1,67 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +app.bundler.name=Windows Application Image +exe.bundler.name=EXE Installer Package +msi.bundler.name=MSI Installer Package + +param.menu-group.default=Unknown + +resource.executable-properties-template=Template for creating executable properties file +resource.setup-icon=setup dialog icon +resource.post-app-image-script=script to run after application image is populated +resource.post-msi-script=script to run after msi file for exe installer is created +resource.wxl-file-name=MsiInstallerStrings_en.wxl +resource.main-wix-file=Main WiX project file +resource.overrides-wix-file=Overrides WiX project file + +error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe) +error.no-wix-tools.advice=Download WiX 3.0 or later from https://wixtoolset.org and add it to the PATH. +error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}] +error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: https://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx . +error.version-string-major-out-of-range=Major version must be in the range [0, 255] +error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535] +error.version-string-minor-out-of-range=Minor version must be in the range [0, 255] +error.version-string-part-not-number=Failed to convert version component to int +error.version-swap=Failed to update version information for {0} +error.invalid-envvar=Invalid value of {0} environment variable + +message.result-dir=Result application bundle: {0}. +message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. +message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". +message.outputting-to-location=Generating EXE for installer to: {0}. +message.output-location=Installer (.exe) saved to: {0} +message.tool-version=Detected [{0}] version [{1}]. +message.creating-association-with-null-extension=Creating association with null extension. +message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. +message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . +message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. +message.product-code=MSI ProductCode: {0}. +message.upgrade-code=MSI UpgradeCode: {0}. +message.preparing-msi-config=Preparing MSI config: {0}. +message.generating-msi=Generating MSI: {0}. +message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}". + --- /dev/null 2019-11-18 21:13:17.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/WinResources_zh_CN.properties 2019-11-18 21:13:14.455841000 -0500 @@ -0,0 +1,67 @@ +# +# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +app.bundler.name=Windows Application Image +exe.bundler.name=EXE Installer Package +msi.bundler.name=MSI Installer Package + +param.menu-group.default=Unknown + +resource.executable-properties-template=Template for creating executable properties file +resource.setup-icon=setup dialog icon +resource.post-app-image-script=script to run after application image is populated +resource.post-msi-script=script to run after msi file for exe installer is created +resource.wxl-file-name=MsiInstallerStrings_en.wxl +resource.main-wix-file=Main WiX project file +resource.overrides-wix-file=Overrides WiX project file + +error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe) +error.no-wix-tools.advice=Download WiX 3.0 or later from https://wixtoolset.org and add it to the PATH. +error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}] +error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: https://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx . +error.version-string-major-out-of-range=Major version must be in the range [0, 255] +error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535] +error.version-string-minor-out-of-range=Minor version must be in the range [0, 255] +error.version-string-part-not-number=Failed to convert version component to int +error.version-swap=Failed to update version information for {0} +error.invalid-envvar=Invalid value of {0} environment variable + +message.result-dir=Result application bundle: {0}. +message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. +message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". +message.outputting-to-location=Generating EXE for installer to: {0}. +message.output-location=Installer (.exe) saved to: {0} +message.tool-version=Detected [{0}] version [{1}]. +message.creating-association-with-null-extension=Creating association with null extension. +message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. +message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . +message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. +message.product-code=MSI ProductCode: {0}. +message.upgrade-code=MSI UpgradeCode: {0}. +message.preparing-msi-config=Preparing MSI config: {0}. +message.generating-msi=Generating MSI: {0}. +message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}". + Binary files /dev/null and new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/java48.ico differ --- /dev/null 2019-11-18 21:13:38.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/main.wxs 2019-11-18 21:13:35.270925800 -0500 @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + 1 + + + !(loc.message.install.dir.exist) + + + + + + + + 1 + INSTALLDIR_VALID="0" + INSTALLDIR_VALID="1" + + + + 1 + 1 + + + + + + + + + + + + + + + + + --- /dev/null 2019-11-18 21:13:48.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/overrides.wxi 2019-11-18 21:13:45.734128100 -0500 @@ -0,0 +1,3 @@ + + + \ No newline at end of file --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java 2019-11-18 21:13:59.707261900 -0500 +++ /dev/null 2019-11-18 21:14:01.000000000 -0500 @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -@FunctionalInterface -interface ArgAction { - void execute(); -} --- /dev/null 2019-11-18 21:14:01.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/classes/module-info.java.extra 2019-11-18 21:13:56.295819700 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +provides jdk.incubator.jpackage.internal.Bundler with + jdk.incubator.jpackage.internal.WinAppBundler, + jdk.incubator.jpackage.internal.WinExeBundler, + jdk.incubator.jpackage.internal.WinMsiBundler; + --- old/src/jdk.jpackage/windows/native/jpackageapplauncher/WinLauncher.cpp 2019-11-18 21:14:19.824746600 -0500 +++ /dev/null 2019-11-18 21:14:21.000000000 -0500 @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include -#include -#include -#include -#include - -#define JPACKAGE_LIBRARY TEXT("applauncher.dll") - -typedef bool (*start_launcher)(int argc, TCHAR* argv[]); -typedef void (*stop_launcher)(); - -std::wstring GetTitle() { - std::wstring result; - wchar_t buffer[MAX_PATH]; - GetModuleFileName(NULL, buffer, MAX_PATH - 1); - buffer[MAX_PATH - 1] = '\0'; - result = buffer; - size_t slash = result.find_last_of('\\'); - - if (slash != std::wstring::npos) - result = result.substr(slash + 1, result.size() - slash - 1); - - return result; -} - -#ifdef LAUNCHERC -int main(int argc0, char *argv0[]) { -#else // LAUNCHERC -int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, - LPTSTR lpCmdLine, int nCmdShow) { -#endif // LAUNCHERC - int result = 1; - TCHAR **argv; - int argc; - - // [RT-31061] otherwise UI can be left in back of other windows. - ::AllowSetForegroundWindow(ASFW_ANY); - - ::setlocale(LC_ALL, "en_US.utf8"); - argv = CommandLineToArgvW(GetCommandLine(), &argc); - - HMODULE library = ::LoadLibrary(JPACKAGE_LIBRARY); - - if (library == NULL) { - std::wstring title = GetTitle(); - std::wstring description = std::wstring(JPACKAGE_LIBRARY) - + std::wstring(TEXT(" not found.")); - MessageBox(NULL, description.data(), - title.data(), MB_ICONERROR | MB_OK); - } - else { - start_launcher start = - (start_launcher)GetProcAddress(library, "start_launcher"); - stop_launcher stop = - (stop_launcher)GetProcAddress(library, "stop_launcher"); - - if (start != NULL && stop != NULL) { - if (start(argc, argv) == true) { - result = 0; - stop(); - } - } - - ::FreeLibrary(library); - } - - if (argv != NULL) { - LocalFree(argv); - } - - return result; -} - --- /dev/null 2019-11-18 21:14:21.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/jpackageapplauncher/WinLauncher.cpp 2019-11-18 21:14:16.418946600 -0500 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include +#include + +#define JPACKAGE_LIBRARY TEXT("applauncher.dll") + +typedef bool (*start_launcher)(int argc, TCHAR* argv[]); +typedef void (*stop_launcher)(); + +std::wstring GetTitle() { + std::wstring result; + wchar_t buffer[MAX_PATH]; + GetModuleFileName(NULL, buffer, MAX_PATH - 1); + buffer[MAX_PATH - 1] = '\0'; + result = buffer; + size_t slash = result.find_last_of('\\'); + + if (slash != std::wstring::npos) + result = result.substr(slash + 1, result.size() - slash - 1); + + return result; +} + +#ifdef LAUNCHERC +int main(int argc0, char *argv0[]) { +#else // LAUNCHERC +int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, int nCmdShow) { +#endif // LAUNCHERC + int result = 1; + TCHAR **argv; + int argc; + + // [RT-31061] otherwise UI can be left in back of other windows. + ::AllowSetForegroundWindow(ASFW_ANY); + + ::setlocale(LC_ALL, "en_US.utf8"); + argv = CommandLineToArgvW(GetCommandLine(), &argc); + + HMODULE library = ::LoadLibrary(JPACKAGE_LIBRARY); + + if (library == NULL) { + std::wstring title = GetTitle(); + std::wstring description = std::wstring(JPACKAGE_LIBRARY) + + std::wstring(TEXT(" not found.")); + MessageBox(NULL, description.data(), + title.data(), MB_ICONERROR | MB_OK); + } + else { + start_launcher start = + (start_launcher)GetProcAddress(library, "start_launcher"); + stop_launcher stop = + (stop_launcher)GetProcAddress(library, "stop_launcher"); + + if (start != NULL && stop != NULL) { + if (start(argc, argv) == true) { + result = 0; + stop(); + } + } + + ::FreeLibrary(library); + } + + if (argv != NULL) { + LocalFree(argv); + } + + return result; +} + --- old/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-11-18 21:14:30.173976100 -0500 +++ /dev/null 2019-11-18 21:14:31.000000000 -0500 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include - -extern "C" { - - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved) { - return true; - } -} \ No newline at end of file --- /dev/null 2019-11-18 21:14:31.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-11-18 21:14:26.954283700 -0500 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +extern "C" { + + BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, + LPVOID lpvReserved) { + return true; + } +} + --- old/src/jdk.jpackage/windows/native/libapplauncher/FileAttribute.h 2019-11-18 21:14:50.772536500 -0500 +++ /dev/null 2019-11-18 21:14:52.000000000 -0500 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef FILEATTRIBUTE_H -#define FILEATTRIBUTE_H - -enum FileAttribute { - faArchive = FILE_ATTRIBUTE_ARCHIVE, - faCompressed = FILE_ATTRIBUTE_COMPRESSED, - faDevice = FILE_ATTRIBUTE_DEVICE, - faDirectory = FILE_ATTRIBUTE_DIRECTORY, - faEncrypted = FILE_ATTRIBUTE_ENCRYPTED, - faHidden = FILE_ATTRIBUTE_HIDDEN, - //faIntegrityStream = FILE_ATTRIBUTE_INTEGRITY_STREAM, - faNormal = FILE_ATTRIBUTE_NORMAL, - faNotContentIndexed = FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, - //faNoScrubData = FILE_ATTRIBUTE_NO_SCRUB_DATA, - faOffline = FILE_ATTRIBUTE_OFFLINE, - faSystem = FILE_ATTRIBUTE_SYSTEM, - faSymbolicLink = FILE_ATTRIBUTE_REPARSE_POINT, - faSparceFile = FILE_ATTRIBUTE_SPARSE_FILE, - faReadOnly = FILE_ATTRIBUTE_READONLY, - faTemporary = FILE_ATTRIBUTE_TEMPORARY, - faVirtual = FILE_ATTRIBUTE_VIRTUAL -}; - -#endif // FILEATTRIBUTE_H - --- /dev/null 2019-11-18 21:14:52.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libapplauncher/FileAttribute.h 2019-11-18 21:14:47.194389900 -0500 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef FILEATTRIBUTE_H +#define FILEATTRIBUTE_H + +enum FileAttribute { + faArchive = FILE_ATTRIBUTE_ARCHIVE, + faCompressed = FILE_ATTRIBUTE_COMPRESSED, + faDevice = FILE_ATTRIBUTE_DEVICE, + faDirectory = FILE_ATTRIBUTE_DIRECTORY, + faEncrypted = FILE_ATTRIBUTE_ENCRYPTED, + faHidden = FILE_ATTRIBUTE_HIDDEN, + faNormal = FILE_ATTRIBUTE_NORMAL, + faNotContentIndexed = FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, + faOffline = FILE_ATTRIBUTE_OFFLINE, + faSystem = FILE_ATTRIBUTE_SYSTEM, + faSymbolicLink = FILE_ATTRIBUTE_REPARSE_POINT, + faSparceFile = FILE_ATTRIBUTE_SPARSE_FILE, + faReadOnly = FILE_ATTRIBUTE_READONLY, + faTemporary = FILE_ATTRIBUTE_TEMPORARY, + faVirtual = FILE_ATTRIBUTE_VIRTUAL +}; + +#endif // FILEATTRIBUTE_H + --- old/src/jdk.jpackage/windows/native/libapplauncher/FilePath.cpp 2019-11-18 21:15:11.141766700 -0500 +++ /dev/null 2019-11-18 21:15:12.000000000 -0500 @@ -1,473 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "FilePath.h" - -#include -#include -#include - -bool FilePath::FileExists(const TString FileName) { - bool result = false; - WIN32_FIND_DATA FindFileData; - TString fileName = FixPathForPlatform(FileName); - HANDLE handle = FindFirstFile(fileName.data(), &FindFileData); - - if (handle != INVALID_HANDLE_VALUE) { - if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) { - result = true; - } - else { - result = true; - } - - FindClose(handle); - } - return result; -} - -bool FilePath::DirectoryExists(const TString DirectoryName) { - bool result = false; - WIN32_FIND_DATA FindFileData; - TString directoryName = FixPathForPlatform(DirectoryName); - HANDLE handle = FindFirstFile(directoryName.data(), &FindFileData); - - if (handle != INVALID_HANDLE_VALUE) { - if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) { - result = true; - } - - FindClose(handle); - } - return result; -} - -std::string GetLastErrorAsString() { - // Get the error message, if any. - DWORD errorMessageID = ::GetLastError(); - - if (errorMessageID == 0) { - return "No error message has been recorded"; - } - - LPSTR messageBuffer = NULL; - size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER - | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, - SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); - - std::string message(messageBuffer, size); - - // Free the buffer. - LocalFree(messageBuffer); - - return message; -} - -bool FilePath::DeleteFile(const TString FileName) { - bool result = false; - - if (FileExists(FileName) == true) { - TString lFileName = FixPathForPlatform(FileName); - FileAttributes attributes(lFileName); - - if (attributes.Contains(faReadOnly) == true) { - attributes.Remove(faReadOnly); - } - - result = ::DeleteFile(lFileName.data()) == TRUE; - } - - return result; -} - -bool FilePath::DeleteDirectory(const TString DirectoryName) { - bool result = false; - - if (DirectoryExists(DirectoryName) == true) { - SHFILEOPSTRUCTW fos = {0}; - TString directoryName = FixPathForPlatform(DirectoryName); - DynamicBuffer lDirectoryName(directoryName.size() + 2); - if (lDirectoryName.GetData() == NULL) { - return false; - } - memcpy(lDirectoryName.GetData(), directoryName.data(), (directoryName.size() + 2) * sizeof(TCHAR)); - lDirectoryName[directoryName.size() + 1] = NULL; - // Double null terminate for SHFileOperation. - - // Delete the folder and everything inside. - fos.wFunc = FO_DELETE; - fos.pFrom = lDirectoryName.GetData(); - fos.fFlags = FOF_NO_UI; - result = SHFileOperation(&fos) == 0; - } - - return result; -} - -TString FilePath::IncludeTrailingSeparator(const TString value) { - TString result = value; - - if (value.size() > 0) { - TString::iterator i = result.end(); - i--; - - if (*i != TRAILING_PATHSEPARATOR) { - result += TRAILING_PATHSEPARATOR; - } - } - - return result; -} - -TString FilePath::IncludeTrailingSeparator(const char* value) { - TString lvalue = PlatformString(value).toString(); - return IncludeTrailingSeparator(lvalue); -} - -TString FilePath::IncludeTrailingSeparator(const wchar_t* value) { - TString lvalue = PlatformString(value).toString(); - return IncludeTrailingSeparator(lvalue); -} - -TString FilePath::ExtractFilePath(TString Path) { - TString result; - size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR); - if (slash != TString::npos) - result = Path.substr(0, slash); - return result; -} - -TString FilePath::ExtractFileExt(TString Path) { - TString result; - size_t dot = Path.find_last_of('.'); - - if (dot != TString::npos) { - result = Path.substr(dot, Path.size() - dot); - } - - return result; -} - -TString FilePath::ExtractFileName(TString Path) { - TString result; - - size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR); - if (slash != TString::npos) - result = Path.substr(slash + 1, Path.size() - slash - 1); - - return result; -} - -TString FilePath::ChangeFileExt(TString Path, TString Extension) { - TString result; - size_t dot = Path.find_last_of('.'); - - if (dot != TString::npos) { - result = Path.substr(0, dot) + Extension; - } - - if (result.empty() == true) { - result = Path; - } - - return result; -} - -TString FilePath::FixPathForPlatform(TString Path) { - TString result = Path; - std::replace(result.begin(), result.end(), - BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR); - // The maximum path that does not require long path prefix. On Windows the - // maximum path is 260 minus 1 (NUL) but for directories it is 260 minus - // 12 minus 1 (to allow for the creation of a 8.3 file in the directory). - const int maxPath = 247; - if (result.length() > maxPath && - result.find(_T("\\\\?\\")) == TString::npos && - result.find(_T("\\\\?\\UNC")) == TString::npos) { - const TString prefix(_T("\\\\")); - if (!result.compare(0, prefix.size(), prefix)) { - // UNC path, converting to UNC path in long notation - result = _T("\\\\?\\UNC") + result.substr(1, result.length()); - } else { - // converting to non-UNC path in long notation - result = _T("\\\\?\\") + result; - } - } - return result; -} - -TString FilePath::FixPathSeparatorForPlatform(TString Path) { - TString result = Path; - std::replace(result.begin(), result.end(), - BAD_PATH_SEPARATOR, PATH_SEPARATOR); - return result; -} - -TString FilePath::PathSeparator() { - TString result; - result = PATH_SEPARATOR; - return result; -} - -bool FilePath::CreateDirectory(TString Path, bool ownerOnly) { - bool result = false; - - std::list paths; - TString lpath = Path; - - while (lpath.empty() == false && DirectoryExists(lpath) == false) { - paths.push_front(lpath); - lpath = ExtractFilePath(lpath); - } - - for (std::list::iterator iterator = paths.begin(); - iterator != paths.end(); iterator++) { - lpath = *iterator; - - if (_wmkdir(lpath.data()) == 0) { - result = true; - } else { - result = false; - break; - } - } - - return result; -} - -void FilePath::ChangePermissions(TString FileName, bool ownerOnly) { -} - -#include - -FileAttributes::FileAttributes(const TString FileName, bool FollowLink) { - FFileName = FileName; - FFollowLink = FollowLink; - ReadAttributes(); -} - -bool FileAttributes::WriteAttributes() { - bool result = false; - - DWORD attributes = 0; - - for (std::vector::const_iterator iterator = - FAttributes.begin(); - iterator != FAttributes.end(); iterator++) { - switch (*iterator) { - case faArchive: { - attributes = attributes & FILE_ATTRIBUTE_ARCHIVE; - break; - } - case faCompressed: { - attributes = attributes & FILE_ATTRIBUTE_COMPRESSED; - break; - } - case faDevice: { - attributes = attributes & FILE_ATTRIBUTE_DEVICE; - break; - } - case faDirectory: { - attributes = attributes & FILE_ATTRIBUTE_DIRECTORY; - break; - } - case faEncrypted: { - attributes = attributes & FILE_ATTRIBUTE_ENCRYPTED; - break; - } - case faHidden: { - attributes = attributes & FILE_ATTRIBUTE_HIDDEN; - break; - } - case faNormal: { - attributes = attributes & FILE_ATTRIBUTE_NORMAL; - break; - } - case faNotContentIndexed: { - attributes = attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; - break; - } - case faOffline: { - attributes = attributes & FILE_ATTRIBUTE_OFFLINE; - break; - } - case faSystem: { - attributes = attributes & FILE_ATTRIBUTE_SYSTEM; - break; - } - case faSymbolicLink: { - attributes = attributes & FILE_ATTRIBUTE_REPARSE_POINT; - break; - } - case faSparceFile: { - attributes = attributes & FILE_ATTRIBUTE_SPARSE_FILE; - break; - } - case faReadOnly: { - attributes = attributes & FILE_ATTRIBUTE_READONLY; - break; - } - case faTemporary: { - attributes = attributes & FILE_ATTRIBUTE_TEMPORARY; - break; - } - case faVirtual: { - attributes = attributes & FILE_ATTRIBUTE_VIRTUAL; - break; - } - } - } - - if (::SetFileAttributes(FFileName.data(), attributes) != 0) { - result = true; - } - - return result; -} - -#define S_ISRUSR(m) (((m) & S_IRWXU) == S_IRUSR) -#define S_ISWUSR(m) (((m) & S_IRWXU) == S_IWUSR) -#define S_ISXUSR(m) (((m) & S_IRWXU) == S_IXUSR) - -#define S_ISRGRP(m) (((m) & S_IRWXG) == S_IRGRP) -#define S_ISWGRP(m) (((m) & S_IRWXG) == S_IWGRP) -#define S_ISXGRP(m) (((m) & S_IRWXG) == S_IXGRP) - -#define S_ISROTH(m) (((m) & S_IRWXO) == S_IROTH) -#define S_ISWOTH(m) (((m) & S_IRWXO) == S_IWOTH) -#define S_ISXOTH(m) (((m) & S_IRWXO) == S_IXOTH) - -bool FileAttributes::ReadAttributes() { - bool result = false; - - DWORD attributes = ::GetFileAttributes(FFileName.data()); - - if (attributes != INVALID_FILE_ATTRIBUTES) { - result = true; - - if (attributes | FILE_ATTRIBUTE_ARCHIVE) { - FAttributes.push_back(faArchive); - } - if (attributes | FILE_ATTRIBUTE_COMPRESSED) { - FAttributes.push_back(faCompressed); - } - if (attributes | FILE_ATTRIBUTE_DEVICE) { - FAttributes.push_back(faDevice); - } - if (attributes | FILE_ATTRIBUTE_DIRECTORY) { - FAttributes.push_back(faDirectory); - } - if (attributes | FILE_ATTRIBUTE_ENCRYPTED) { - FAttributes.push_back(faEncrypted); - } - if (attributes | FILE_ATTRIBUTE_HIDDEN) { - FAttributes.push_back(faHidden); - } - // if (attributes | FILE_ATTRIBUTE_INTEGRITY_STREAM) { - // FAttributes.push_back(faIntegrityStream); - // } - if (attributes | FILE_ATTRIBUTE_NORMAL) { - FAttributes.push_back(faNormal); - } - if (attributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) { - FAttributes.push_back(faNotContentIndexed); - } - // if (attributes | FILE_ATTRIBUTE_NO_SCRUB_DATA) { - // FAttributes.push_back(faNoScrubData); - // } - if (attributes | FILE_ATTRIBUTE_SYSTEM) { - FAttributes.push_back(faSystem); - } - if (attributes | FILE_ATTRIBUTE_OFFLINE) { - FAttributes.push_back(faOffline); - } - if (attributes | FILE_ATTRIBUTE_REPARSE_POINT) { - FAttributes.push_back(faSymbolicLink); - } - if (attributes | FILE_ATTRIBUTE_SPARSE_FILE) { - FAttributes.push_back(faSparceFile); - } - if (attributes | FILE_ATTRIBUTE_READONLY ) { - FAttributes.push_back(faReadOnly); - } - if (attributes | FILE_ATTRIBUTE_TEMPORARY) { - FAttributes.push_back(faTemporary); - } - if (attributes | FILE_ATTRIBUTE_VIRTUAL) { - FAttributes.push_back(faVirtual); - } - } - - return result; -} - -bool FileAttributes::Valid(const FileAttribute Value) { - bool result = false; - - switch (Value) { - case faHidden: - case faReadOnly: { - result = true; - break; - } - default: - break; - } - - return result; -} - -void FileAttributes::Append(FileAttribute Value) { - if (Valid(Value) == true) { - FAttributes.push_back(Value); - WriteAttributes(); - } -} - -bool FileAttributes::Contains(FileAttribute Value) { - bool result = false; - - std::vector::const_iterator iterator = - std::find(FAttributes.begin(), FAttributes.end(), Value); - - if (iterator != FAttributes.end()) { - result = true; - } - - return result; -} - -void FileAttributes::Remove(FileAttribute Value) { - if (Valid(Value) == true) { - std::vector::iterator iterator = - std::find(FAttributes.begin(), FAttributes.end(), Value); - - if (iterator != FAttributes.end()) { - FAttributes.erase(iterator); - WriteAttributes(); - } - } -} --- /dev/null 2019-11-18 21:15:12.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libapplauncher/FilePath.cpp 2019-11-18 21:15:07.694478400 -0500 @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "FilePath.h" + +#include +#include +#include + +bool FilePath::FileExists(const TString FileName) { + bool result = false; + WIN32_FIND_DATA FindFileData; + TString fileName = FixPathForPlatform(FileName); + HANDLE handle = FindFirstFile(fileName.data(), &FindFileData); + + if (handle != INVALID_HANDLE_VALUE) { + if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) { + result = true; + } + else { + result = true; + } + + FindClose(handle); + } + return result; +} + +bool FilePath::DirectoryExists(const TString DirectoryName) { + bool result = false; + WIN32_FIND_DATA FindFileData; + TString directoryName = FixPathForPlatform(DirectoryName); + HANDLE handle = FindFirstFile(directoryName.data(), &FindFileData); + + if (handle != INVALID_HANDLE_VALUE) { + if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) { + result = true; + } + + FindClose(handle); + } + return result; +} + +std::string GetLastErrorAsString() { + // Get the error message, if any. + DWORD errorMessageID = ::GetLastError(); + + if (errorMessageID == 0) { + return "No error message has been recorded"; + } + + LPSTR messageBuffer = NULL; + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, + SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + + std::string message(messageBuffer, size); + + // Free the buffer. + LocalFree(messageBuffer); + + return message; +} + +bool FilePath::DeleteFile(const TString FileName) { + bool result = false; + + if (FileExists(FileName) == true) { + TString lFileName = FixPathForPlatform(FileName); + FileAttributes attributes(lFileName); + + if (attributes.Contains(faReadOnly) == true) { + attributes.Remove(faReadOnly); + } + + result = ::DeleteFile(lFileName.data()) == TRUE; + } + + return result; +} + +bool FilePath::DeleteDirectory(const TString DirectoryName) { + bool result = false; + + if (DirectoryExists(DirectoryName) == true) { + SHFILEOPSTRUCTW fos = {0}; + TString directoryName = FixPathForPlatform(DirectoryName); + DynamicBuffer lDirectoryName(directoryName.size() + 2); + if (lDirectoryName.GetData() == NULL) { + return false; + } + memcpy(lDirectoryName.GetData(), directoryName.data(), + (directoryName.size() + 2) * sizeof(TCHAR)); + lDirectoryName[directoryName.size() + 1] = NULL; + // Double null terminate for SHFileOperation. + + // Delete the folder and everything inside. + fos.wFunc = FO_DELETE; + fos.pFrom = lDirectoryName.GetData(); + fos.fFlags = FOF_NO_UI; + result = SHFileOperation(&fos) == 0; + } + + return result; +} + +TString FilePath::IncludeTrailingSeparator(const TString value) { + TString result = value; + + if (value.size() > 0) { + TString::iterator i = result.end(); + i--; + + if (*i != TRAILING_PATHSEPARATOR) { + result += TRAILING_PATHSEPARATOR; + } + } + + return result; +} + +TString FilePath::IncludeTrailingSeparator(const char* value) { + TString lvalue = PlatformString(value).toString(); + return IncludeTrailingSeparator(lvalue); +} + +TString FilePath::IncludeTrailingSeparator(const wchar_t* value) { + TString lvalue = PlatformString(value).toString(); + return IncludeTrailingSeparator(lvalue); +} + +TString FilePath::ExtractFilePath(TString Path) { + TString result; + size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR); + if (slash != TString::npos) + result = Path.substr(0, slash); + return result; +} + +TString FilePath::ExtractFileExt(TString Path) { + TString result; + size_t dot = Path.find_last_of('.'); + + if (dot != TString::npos) { + result = Path.substr(dot, Path.size() - dot); + } + + return result; +} + +TString FilePath::ExtractFileName(TString Path) { + TString result; + + size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR); + if (slash != TString::npos) + result = Path.substr(slash + 1, Path.size() - slash - 1); + + return result; +} + +TString FilePath::ChangeFileExt(TString Path, TString Extension) { + TString result; + size_t dot = Path.find_last_of('.'); + + if (dot != TString::npos) { + result = Path.substr(0, dot) + Extension; + } + + if (result.empty() == true) { + result = Path; + } + + return result; +} + +TString FilePath::FixPathForPlatform(TString Path) { + TString result = Path; + std::replace(result.begin(), result.end(), + BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR); + // The maximum path that does not require long path prefix. On Windows the + // maximum path is 260 minus 1 (NUL) but for directories it is 260 minus + // 12 minus 1 (to allow for the creation of a 8.3 file in the directory). + const int maxPath = 247; + if (result.length() > maxPath && + result.find(_T("\\\\?\\")) == TString::npos && + result.find(_T("\\\\?\\UNC")) == TString::npos) { + const TString prefix(_T("\\\\")); + if (!result.compare(0, prefix.size(), prefix)) { + // UNC path, converting to UNC path in long notation + result = _T("\\\\?\\UNC") + result.substr(1, result.length()); + } else { + // converting to non-UNC path in long notation + result = _T("\\\\?\\") + result; + } + } + return result; +} + +TString FilePath::FixPathSeparatorForPlatform(TString Path) { + TString result = Path; + std::replace(result.begin(), result.end(), + BAD_PATH_SEPARATOR, PATH_SEPARATOR); + return result; +} + +TString FilePath::PathSeparator() { + TString result; + result = PATH_SEPARATOR; + return result; +} + +bool FilePath::CreateDirectory(TString Path, bool ownerOnly) { + bool result = false; + + std::list paths; + TString lpath = Path; + + while (lpath.empty() == false && DirectoryExists(lpath) == false) { + paths.push_front(lpath); + lpath = ExtractFilePath(lpath); + } + + for (std::list::iterator iterator = paths.begin(); + iterator != paths.end(); iterator++) { + lpath = *iterator; + + if (_wmkdir(lpath.data()) == 0) { + result = true; + } else { + result = false; + break; + } + } + + return result; +} + +void FilePath::ChangePermissions(TString FileName, bool ownerOnly) { +} + +#include + +FileAttributes::FileAttributes(const TString FileName, bool FollowLink) { + FFileName = FileName; + FFollowLink = FollowLink; + ReadAttributes(); +} + +bool FileAttributes::WriteAttributes() { + bool result = false; + + DWORD attributes = 0; + + for (std::vector::const_iterator iterator = + FAttributes.begin(); + iterator != FAttributes.end(); iterator++) { + switch (*iterator) { + case faArchive: { + attributes = attributes & FILE_ATTRIBUTE_ARCHIVE; + break; + } + case faCompressed: { + attributes = attributes & FILE_ATTRIBUTE_COMPRESSED; + break; + } + case faDevice: { + attributes = attributes & FILE_ATTRIBUTE_DEVICE; + break; + } + case faDirectory: { + attributes = attributes & FILE_ATTRIBUTE_DIRECTORY; + break; + } + case faEncrypted: { + attributes = attributes & FILE_ATTRIBUTE_ENCRYPTED; + break; + } + case faHidden: { + attributes = attributes & FILE_ATTRIBUTE_HIDDEN; + break; + } + case faNormal: { + attributes = attributes & FILE_ATTRIBUTE_NORMAL; + break; + } + case faNotContentIndexed: { + attributes = attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; + break; + } + case faOffline: { + attributes = attributes & FILE_ATTRIBUTE_OFFLINE; + break; + } + case faSystem: { + attributes = attributes & FILE_ATTRIBUTE_SYSTEM; + break; + } + case faSymbolicLink: { + attributes = attributes & FILE_ATTRIBUTE_REPARSE_POINT; + break; + } + case faSparceFile: { + attributes = attributes & FILE_ATTRIBUTE_SPARSE_FILE; + break; + } + case faReadOnly: { + attributes = attributes & FILE_ATTRIBUTE_READONLY; + break; + } + case faTemporary: { + attributes = attributes & FILE_ATTRIBUTE_TEMPORARY; + break; + } + case faVirtual: { + attributes = attributes & FILE_ATTRIBUTE_VIRTUAL; + break; + } + } + } + + if (::SetFileAttributes(FFileName.data(), attributes) != 0) { + result = true; + } + + return result; +} + +#define S_ISRUSR(m) (((m) & S_IRWXU) == S_IRUSR) +#define S_ISWUSR(m) (((m) & S_IRWXU) == S_IWUSR) +#define S_ISXUSR(m) (((m) & S_IRWXU) == S_IXUSR) + +#define S_ISRGRP(m) (((m) & S_IRWXG) == S_IRGRP) +#define S_ISWGRP(m) (((m) & S_IRWXG) == S_IWGRP) +#define S_ISXGRP(m) (((m) & S_IRWXG) == S_IXGRP) + +#define S_ISROTH(m) (((m) & S_IRWXO) == S_IROTH) +#define S_ISWOTH(m) (((m) & S_IRWXO) == S_IWOTH) +#define S_ISXOTH(m) (((m) & S_IRWXO) == S_IXOTH) + +bool FileAttributes::ReadAttributes() { + bool result = false; + + DWORD attributes = ::GetFileAttributes(FFileName.data()); + + if (attributes != INVALID_FILE_ATTRIBUTES) { + result = true; + + if (attributes | FILE_ATTRIBUTE_ARCHIVE) { + FAttributes.push_back(faArchive); + } + if (attributes | FILE_ATTRIBUTE_COMPRESSED) { + FAttributes.push_back(faCompressed); + } + if (attributes | FILE_ATTRIBUTE_DEVICE) { + FAttributes.push_back(faDevice); + } + if (attributes | FILE_ATTRIBUTE_DIRECTORY) { + FAttributes.push_back(faDirectory); + } + if (attributes | FILE_ATTRIBUTE_ENCRYPTED) { + FAttributes.push_back(faEncrypted); + } + if (attributes | FILE_ATTRIBUTE_HIDDEN) { + FAttributes.push_back(faHidden); + } + if (attributes | FILE_ATTRIBUTE_NORMAL) { + FAttributes.push_back(faNormal); + } + if (attributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) { + FAttributes.push_back(faNotContentIndexed); + } + if (attributes | FILE_ATTRIBUTE_SYSTEM) { + FAttributes.push_back(faSystem); + } + if (attributes | FILE_ATTRIBUTE_OFFLINE) { + FAttributes.push_back(faOffline); + } + if (attributes | FILE_ATTRIBUTE_REPARSE_POINT) { + FAttributes.push_back(faSymbolicLink); + } + if (attributes | FILE_ATTRIBUTE_SPARSE_FILE) { + FAttributes.push_back(faSparceFile); + } + if (attributes | FILE_ATTRIBUTE_READONLY ) { + FAttributes.push_back(faReadOnly); + } + if (attributes | FILE_ATTRIBUTE_TEMPORARY) { + FAttributes.push_back(faTemporary); + } + if (attributes | FILE_ATTRIBUTE_VIRTUAL) { + FAttributes.push_back(faVirtual); + } + } + + return result; +} + +bool FileAttributes::Valid(const FileAttribute Value) { + bool result = false; + + switch (Value) { + case faHidden: + case faReadOnly: { + result = true; + break; + } + default: + break; + } + + return result; +} + +void FileAttributes::Append(FileAttribute Value) { + if (Valid(Value) == true) { + FAttributes.push_back(Value); + WriteAttributes(); + } +} + +bool FileAttributes::Contains(FileAttribute Value) { + bool result = false; + + std::vector::const_iterator iterator = + std::find(FAttributes.begin(), FAttributes.end(), Value); + + if (iterator != FAttributes.end()) { + result = true; + } + + return result; +} + +void FileAttributes::Remove(FileAttribute Value) { + if (Valid(Value) == true) { + std::vector::iterator iterator = + std::find(FAttributes.begin(), FAttributes.end(), Value); + + if (iterator != FAttributes.end()) { + FAttributes.erase(iterator); + WriteAttributes(); + } + } +} --- old/src/jdk.jpackage/windows/native/libapplauncher/PlatformDefs.h 2019-11-18 21:15:31.934672000 -0500 +++ /dev/null 2019-11-18 21:15:33.000000000 -0500 @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef PLATFORM_DEFS_H -#define PLATFORM_DEFS_H - -// Define Windows compatibility requirements XP or later -#define WINVER 0x0600 -#define _WIN32_WINNT 0x0600 - -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -#ifndef WINDOWS -#define WINDOWS -#endif - -typedef std::wstring TString; -#define StringLength wcslen - -#define TRAILING_PATHSEPARATOR '\\' -#define BAD_TRAILING_PATHSEPARATOR '/' -#define PATH_SEPARATOR ';' -#define BAD_PATH_SEPARATOR ':' - -typedef ULONGLONG TPlatformNumber; -typedef DWORD TProcessID; - -typedef void* Module; -typedef void* Procedure; - -#endif // PLATFORM_DEFS_H --- /dev/null 2019-11-18 21:15:33.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libapplauncher/PlatformDefs.h 2019-11-18 21:15:28.588169100 -0500 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef PLATFORM_DEFS_H +#define PLATFORM_DEFS_H + +// Define Windows compatibility requirements XP or later +#define WINVER 0x0600 +#define _WIN32_WINNT 0x0600 + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#ifndef WINDOWS +#define WINDOWS +#endif + +typedef std::wstring TString; +#define StringLength wcslen + +#define TRAILING_PATHSEPARATOR '\\' +#define BAD_TRAILING_PATHSEPARATOR '/' +#define PATH_SEPARATOR ';' +#define BAD_PATH_SEPARATOR ':' + +typedef ULONGLONG TPlatformNumber; +typedef DWORD TProcessID; + +typedef void* Module; +typedef void* Procedure; + +#endif // PLATFORM_DEFS_H --- old/src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.cpp 2019-11-18 21:15:42.557250000 -0500 +++ /dev/null 2019-11-18 21:15:43.000000000 -0500 @@ -1,761 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Platform.h" - -#include "JavaVirtualMachine.h" -#include "WindowsPlatform.h" -#include "Package.h" -#include "Helpers.h" -#include "PlatformString.h" -#include "Macros.h" - -#include -#include -#include -#include -#include -#include - -using namespace std; - -#define WINDOWS_JPACKAGE_TMP_DIR \ - L"\\AppData\\Local\\Java\\JPackage\\tmp" - -class Registry { -private: - HKEY FKey; - HKEY FOpenKey; - bool FOpen; - -public: - - Registry(HKEY Key) { - FOpen = false; - FKey = Key; - } - - ~Registry() { - Close(); - } - - void Close() { - if (FOpen == true) { - RegCloseKey(FOpenKey); - } - } - - bool Open(TString SubKey) { - bool result = false; - Close(); - - if (RegOpenKeyEx(FKey, SubKey.data(), 0, KEY_READ, &FOpenKey) == - ERROR_SUCCESS) { - result = true; - } - - return result; - } - - std::list GetKeys() { - std::list result; - DWORD count; - - if (RegQueryInfoKey(FOpenKey, NULL, NULL, NULL, NULL, NULL, NULL, - &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { - - DWORD length = 255; - DynamicBuffer buffer(length); - if (buffer.GetData() == NULL) { - return result; - } - - for (unsigned int index = 0; index < count; index++) { - buffer.Zero(); - DWORD status = RegEnumValue(FOpenKey, index, buffer.GetData(), - &length, NULL, NULL, NULL, NULL); - - while (status == ERROR_MORE_DATA) { - length = length * 2; - if (!buffer.Resize(length)) { - return result; - } - status = RegEnumValue(FOpenKey, index, buffer.GetData(), - &length, NULL, NULL, NULL, NULL); - } - - if (status == ERROR_SUCCESS) { - TString value = buffer.GetData(); - result.push_back(value); - } - } - } - - return result; - } - - TString ReadString(TString Name) { - TString result; - DWORD length; - DWORD dwRet; - DynamicBuffer buffer(0); - length = 0; - - dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, NULL, - &length); - if (dwRet == ERROR_MORE_DATA || dwRet == 0) { - if (!buffer.Resize(length + 1)) { - return result; - } - dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, - (LPBYTE) buffer.GetData(), &length); - result = buffer.GetData(); - } - - return result; - } -}; - -WindowsPlatform::WindowsPlatform(void) : Platform() { - FMainThread = ::GetCurrentThreadId(); -} - -WindowsPlatform::~WindowsPlatform(void) { -} - -TString WindowsPlatform::GetPackageAppDirectory() { - return FilePath::IncludeTrailingSeparator( - GetPackageRootDirectory()) + _T("app"); -} - -TString WindowsPlatform::GetPackageLauncherDirectory() { - return GetPackageRootDirectory(); -} - -TString WindowsPlatform::GetPackageRuntimeBinDirectory() { - return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime\\bin"); -} - -TCHAR* WindowsPlatform::ConvertStringToFileSystemString(TCHAR* Source, - bool &release) { - // Not Implemented. - return NULL; -} - -TCHAR* WindowsPlatform::ConvertFileSystemStringToString(TCHAR* Source, - bool &release) { - // Not Implemented. - return NULL; -} - -void WindowsPlatform::SetCurrentDirectory(TString Value) { - _wchdir(Value.data()); -} - -TString WindowsPlatform::GetPackageRootDirectory() { - TString filename = GetModuleFileName(); - return FilePath::ExtractFilePath(filename); -} - -TString WindowsPlatform::GetAppDataDirectory() { - TString result; - TCHAR path[MAX_PATH]; - - if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path) == S_OK) { - result = path; - } - - return result; -} - -TString WindowsPlatform::GetAppName() { - TString result = GetModuleFileName(); - result = FilePath::ExtractFileName(result); - result = FilePath::ChangeFileExt(result, _T("")); - return result; -} - -void WindowsPlatform::ShowMessage(TString title, TString description) { - MessageBox(NULL, description.data(), - !title.empty() ? title.data() : description.data(), - MB_ICONERROR | MB_OK); -} - -void WindowsPlatform::ShowMessage(TString description) { - TString appname = GetModuleFileName(); - appname = FilePath::ExtractFileName(appname); - MessageBox(NULL, description.data(), appname.data(), MB_ICONERROR | MB_OK); -} - -MessageResponse WindowsPlatform::ShowResponseMessage(TString title, - TString description) { - MessageResponse result = mrCancel; - - if (::MessageBox(NULL, description.data(), title.data(), MB_OKCANCEL) == - IDOK) { - result = mrOK; - } - - return result; -} - -TString WindowsPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) { - TString result = FilePath::IncludeTrailingSeparator(RuntimePath) + - _T("jre\\bin\\jli.dll"); - - if (FilePath::FileExists(result) == false) { - result = FilePath::IncludeTrailingSeparator(RuntimePath) + - _T("bin\\jli.dll"); - } - - return result; -} - -ISectionalPropertyContainer* WindowsPlatform::GetConfigFile(TString FileName) { - IniFile *result = new IniFile(); - if (result == NULL) { - return NULL; - } - - result->LoadFromFile(FileName); - - return result; -} - -TString WindowsPlatform::GetModuleFileName() { - TString result; - DynamicBuffer buffer(MAX_PATH); - if (buffer.GetData() == NULL) { - return result; - } - - ::GetModuleFileName(NULL, buffer.GetData(), - static_cast (buffer.GetSize())); - - while (ERROR_INSUFFICIENT_BUFFER == GetLastError()) { - if (!buffer.Resize(buffer.GetSize() * 2)) { - return result; - } - ::GetModuleFileName(NULL, buffer.GetData(), - static_cast (buffer.GetSize())); - } - - result = buffer.GetData(); - return result; -} - -Module WindowsPlatform::LoadLibrary(TString FileName) { - return ::LoadLibrary(FileName.data()); -} - -void WindowsPlatform::FreeLibrary(Module AModule) { - ::FreeLibrary((HMODULE) AModule); -} - -Procedure WindowsPlatform::GetProcAddress(Module AModule, - std::string MethodName) { - return ::GetProcAddress((HMODULE) AModule, MethodName.c_str()); -} - -bool WindowsPlatform::IsMainThread() { - bool result = (FMainThread == ::GetCurrentThreadId()); - return result; -} - -TString WindowsPlatform::GetTempDirectory() { - TString result; - PWSTR userDir = 0; - - if (SUCCEEDED(SHGetKnownFolderPath( - FOLDERID_Profile, - 0, - NULL, - &userDir))) { - result = userDir; - result += WINDOWS_JPACKAGE_TMP_DIR; - CoTaskMemFree(userDir); - } - - return result; -} - -static BOOL CALLBACK enumWindows(HWND winHandle, LPARAM lParam) { - DWORD pid = (DWORD) lParam, wPid = 0; - GetWindowThreadProcessId(winHandle, &wPid); - if (pid == wPid) { - SetForegroundWindow(winHandle); - return FALSE; - } - return TRUE; -} - -TPlatformNumber WindowsPlatform::GetMemorySize() { - SYSTEM_INFO si; - GetSystemInfo(&si); - size_t result = (size_t) si.lpMaximumApplicationAddress; - result = result / 1048576; // Convert from bytes to megabytes. - return result; -} - -std::vector FilterList(std::vector &Items, - std::wregex Pattern) { - std::vector result; - - for (std::vector::iterator it = Items.begin(); - it != Items.end(); ++it) { - TString item = *it; - std::wsmatch match; - - if (std::regex_search(item, match, Pattern)) { - result.push_back(item); - } - } - return result; -} - -Process* WindowsPlatform::CreateProcess() { - return new WindowsProcess(); -} - -void WindowsPlatform::InitStreamLocale(wios *stream) { - const std::locale empty_locale = std::locale::empty(); - const std::locale utf8_locale = - std::locale(empty_locale, new std::codecvt_utf8()); - stream->imbue(utf8_locale); -} - -void WindowsPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) { - if (pJavaLibrary == NULL) { - return; - } - - if (FilePath::FileExists(_T("msvcr100.dll")) == true) { - pJavaLibrary->AddDependency(_T("msvcr100.dll")); - } - - TString runtimeBin = GetPackageRuntimeBinDirectory(); - SetDllDirectory(runtimeBin.c_str()); -} - -void Platform::CopyString(char *Destination, - size_t NumberOfElements, const char *Source) { - strcpy_s(Destination, NumberOfElements, Source); - - if (NumberOfElements > 0) { - Destination[NumberOfElements - 1] = '\0'; - } -} - -void Platform::CopyString(wchar_t *Destination, - size_t NumberOfElements, const wchar_t *Source) { - wcscpy_s(Destination, NumberOfElements, Source); - - if (NumberOfElements > 0) { - Destination[NumberOfElements - 1] = '\0'; - } -} - -// Owner must free the return value. -MultibyteString Platform::WideStringToMultibyteString( - const wchar_t* value) { - MultibyteString result; - size_t count = 0; - - if (value == NULL) { - return result; - } - - count = WideCharToMultiByte(CP_UTF8, 0, value, -1, NULL, 0, NULL, NULL); - - if (count > 0) { - result.data = new char[count + 1]; - result.length = WideCharToMultiByte(CP_UTF8, 0, value, -1, - result.data, (int)count, NULL, NULL); - } - - return result; -} - -// Owner must free the return value. -WideString Platform::MultibyteStringToWideString(const char* value) { - WideString result; - size_t count = 0; - - if (value == NULL) { - return result; - } - - mbstowcs_s(&count, NULL, 0, value, _TRUNCATE); - - if (count > 0) { - result.data = new wchar_t[count + 1]; - mbstowcs_s(&result.length, result.data, count, value, count); - } - - return result; -} - -FileHandle::FileHandle(std::wstring FileName) { - FHandle = ::CreateFile(FileName.data(), GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); -} - -FileHandle::~FileHandle() { - if (IsValid() == true) { - ::CloseHandle(FHandle); - } -} - -bool FileHandle::IsValid() { - return FHandle != INVALID_HANDLE_VALUE; -} - -HANDLE FileHandle::GetHandle() { - return FHandle; -} - -FileMappingHandle::FileMappingHandle(HANDLE FileHandle) { - FHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); -} - -bool FileMappingHandle::IsValid() { - return FHandle != NULL; -} - -FileMappingHandle::~FileMappingHandle() { - if (IsValid() == true) { - ::CloseHandle(FHandle); - } -} - -HANDLE FileMappingHandle::GetHandle() { - return FHandle; -} - -FileData::FileData(HANDLE Handle) { - FBaseAddress = ::MapViewOfFile(Handle, FILE_MAP_READ, 0, 0, 0); -} - -FileData::~FileData() { - if (IsValid() == true) { - ::UnmapViewOfFile(FBaseAddress); - } -} - -bool FileData::IsValid() { - return FBaseAddress != NULL; -} - -LPVOID FileData::GetBaseAddress() { - return FBaseAddress; -} - -WindowsLibrary::WindowsLibrary(std::wstring FileName) { - FFileName = FileName; -} - -std::vector WindowsLibrary::GetImports() { - std::vector result; - FileHandle library(FFileName); - - if (library.IsValid() == true) { - FileMappingHandle mapping(library.GetHandle()); - - if (mapping.IsValid() == true) { - FileData fileData(mapping.GetHandle()); - - if (fileData.IsValid() == true) { - PIMAGE_DOS_HEADER dosHeader = - (PIMAGE_DOS_HEADER) fileData.GetBaseAddress(); - PIMAGE_FILE_HEADER pImgFileHdr = - (PIMAGE_FILE_HEADER) fileData.GetBaseAddress(); - if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) { - result = DumpPEFile(dosHeader); - } - } - } - } - - return result; -} - -// Given an RVA, look up the section header that encloses it and return a -// pointer to its IMAGE_SECTION_HEADER - -PIMAGE_SECTION_HEADER WindowsLibrary::GetEnclosingSectionHeader(DWORD rva, - PIMAGE_NT_HEADERS pNTHeader) { - PIMAGE_SECTION_HEADER result = 0; - PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader); - - for (unsigned index = 0; index < pNTHeader->FileHeader.NumberOfSections; - index++, section++) { - // Is the RVA is within this section? - if ((rva >= section->VirtualAddress) && - (rva < (section->VirtualAddress + section->Misc.VirtualSize))) { - result = section; - } - } - - return result; -} - -LPVOID WindowsLibrary::GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader, - DWORD imageBase) { - LPVOID result = 0; - PIMAGE_SECTION_HEADER pSectionHdr = GetEnclosingSectionHeader(rva, - pNTHeader); - - if (pSectionHdr != NULL) { - INT delta = (INT) ( - pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData); - DWORD_PTR dwp = (DWORD_PTR) (imageBase + rva - delta); - result = reinterpret_cast (dwp); // VS2017 - FIXME - } - - return result; -} - -std::vector WindowsLibrary::GetImportsSection(DWORD base, - PIMAGE_NT_HEADERS pNTHeader) { - std::vector result; - - // Look up where the imports section is located. Normally in - // the .idata section, - // but not necessarily so. Therefore, grab the RVA from the data dir. - DWORD importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[ - IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; - - if (importsStartRVA != NULL) { - // Get the IMAGE_SECTION_HEADER that contains the imports. This is - // usually the .idata section, but doesn't have to be. - PIMAGE_SECTION_HEADER pSection = - GetEnclosingSectionHeader(importsStartRVA, pNTHeader); - - if (pSection != NULL) { - PIMAGE_IMPORT_DESCRIPTOR importDesc = - (PIMAGE_IMPORT_DESCRIPTOR) GetPtrFromRVA( - importsStartRVA, pNTHeader, base); - - if (importDesc != NULL) { - while (true) { - // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR - if ((importDesc->TimeDateStamp == 0) && - (importDesc->Name == 0)) { - break; - } - - std::string filename = (char*) GetPtrFromRVA( - importDesc->Name, pNTHeader, base); - result.push_back(PlatformString(filename)); - importDesc++; // advance to next IMAGE_IMPORT_DESCRIPTOR - } - } - } - } - - return result; -} - -std::vector WindowsLibrary::DumpPEFile(PIMAGE_DOS_HEADER dosHeader) { - std::vector result; - // all of this is VS2017 - FIXME - DWORD_PTR dwDosHeaders = reinterpret_cast (dosHeader); - DWORD_PTR dwPIHeaders = dwDosHeaders + (DWORD) (dosHeader->e_lfanew); - - PIMAGE_NT_HEADERS pNTHeader = - reinterpret_cast (dwPIHeaders); - - // Verify that the e_lfanew field gave us a reasonable - // pointer and the PE signature. - // TODO: To really fix JDK-8131321 this condition needs to be changed. - // There is a matching change - // in JavaVirtualMachine.cpp that also needs to be changed. - if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) { - DWORD base = (DWORD) (dwDosHeaders); - result = GetImportsSection(base, pNTHeader); - } - - return result; -} - -#include - -WindowsJob::WindowsJob() { - FHandle = NULL; -} - -WindowsJob::~WindowsJob() { - if (FHandle != NULL) { - CloseHandle(FHandle); - } -} - -HANDLE WindowsJob::GetHandle() { - if (FHandle == NULL) { - FHandle = CreateJobObject(NULL, NULL); // GLOBAL - - if (FHandle == NULL) { - ::MessageBox(0, _T("Could not create job object"), - _T("TEST"), MB_OK); - } else { - JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0}; - - // Configure all child processes associated with - // the job to terminate when the - jeli.BasicLimitInformation.LimitFlags = - JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; - if (0 == SetInformationJobObject(FHandle, - JobObjectExtendedLimitInformation, &jeli, sizeof (jeli))) { - ::MessageBox(0, _T("Could not SetInformationJobObject"), - _T("TEST"), MB_OK); - } - } - } - - return FHandle; -} - -// Initialize static member of WindowsProcess -WindowsJob WindowsProcess::FJob; - -WindowsProcess::WindowsProcess() : Process() { - FRunning = false; -} - -WindowsProcess::~WindowsProcess() { - Terminate(); -} - -void WindowsProcess::Cleanup() { - CloseHandle(FProcessInfo.hProcess); - CloseHandle(FProcessInfo.hThread); -} - -bool WindowsProcess::IsRunning() { - bool result = false; - - HANDLE handle = ::CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); - if (handle == INVALID_HANDLE_VALUE) { - return false; - } - - PROCESSENTRY32 process = {0}; - process.dwSize = sizeof (process); - - if (::Process32First(handle, &process)) { - do { - if (process.th32ProcessID == FProcessInfo.dwProcessId) { - result = true; - break; - } - } while (::Process32Next(handle, &process)); - } - - CloseHandle(handle); - - return result; -} - -bool WindowsProcess::Terminate() { - bool result = false; - - if (IsRunning() == true && FRunning == true) { - FRunning = false; - } - - return result; -} - -bool WindowsProcess::Execute(const TString Application, - const std::vector Arguments, bool AWait) { - bool result = false; - - if (FRunning == false) { - FRunning = true; - - STARTUPINFO startupInfo; - ZeroMemory(&startupInfo, sizeof (startupInfo)); - startupInfo.cb = sizeof (startupInfo); - ZeroMemory(&FProcessInfo, sizeof (FProcessInfo)); - - TString command = Application; - - for (std::vector::const_iterator iterator = Arguments.begin(); - iterator != Arguments.end(); iterator++) { - command += TString(_T(" ")) + *iterator; - } - - if (::CreateProcess(Application.data(), (wchar_t*)command.data(), NULL, - NULL, FALSE, 0, NULL, NULL, &startupInfo, &FProcessInfo) == FALSE) { - TString message = PlatformString::Format( - _T("Error: Unable to create process %s"), - Application.data()); - throw Exception(message); - } else { - if (FJob.GetHandle() != NULL) { - if (::AssignProcessToJobObject(FJob.GetHandle(), - FProcessInfo.hProcess) == 0) { - // Failed to assign process to job. It doesn't prevent - // anything from continuing so continue. - } - } - - // Wait until child process exits. - if (AWait == true) { - Wait(); - // Close process and thread handles. - Cleanup(); - } - } - } - - return result; -} - -bool WindowsProcess::Wait() { - bool result = false; - - WaitForSingleObject(FProcessInfo.hProcess, INFINITE); - return result; -} - -TProcessID WindowsProcess::GetProcessID() { - return FProcessInfo.dwProcessId; -} - -bool WindowsProcess::ReadOutput() { - bool result = false; - // TODO implement - return result; -} - -void WindowsProcess::SetInput(TString Value) { - // TODO implement -} - -std::list WindowsProcess::GetOutput() { - ReadOutput(); - return Process::GetOutput(); -} --- /dev/null 2019-11-18 21:15:44.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libapplauncher/WindowsPlatform.cpp 2019-11-18 21:15:39.203493800 -0500 @@ -0,0 +1,759 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Platform.h" + +#include "JavaVirtualMachine.h" +#include "WindowsPlatform.h" +#include "Package.h" +#include "Helpers.h" +#include "PlatformString.h" +#include "Macros.h" + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#define WINDOWS_JPACKAGE_TMP_DIR \ + L"\\AppData\\Local\\Java\\JPackage\\tmp" + +class Registry { +private: + HKEY FKey; + HKEY FOpenKey; + bool FOpen; + +public: + + Registry(HKEY Key) { + FOpen = false; + FKey = Key; + } + + ~Registry() { + Close(); + } + + void Close() { + if (FOpen == true) { + RegCloseKey(FOpenKey); + } + } + + bool Open(TString SubKey) { + bool result = false; + Close(); + + if (RegOpenKeyEx(FKey, SubKey.data(), 0, KEY_READ, &FOpenKey) == + ERROR_SUCCESS) { + result = true; + } + + return result; + } + + std::list GetKeys() { + std::list result; + DWORD count; + + if (RegQueryInfoKey(FOpenKey, NULL, NULL, NULL, NULL, NULL, NULL, + &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { + + DWORD length = 255; + DynamicBuffer buffer(length); + if (buffer.GetData() == NULL) { + return result; + } + + for (unsigned int index = 0; index < count; index++) { + buffer.Zero(); + DWORD status = RegEnumValue(FOpenKey, index, buffer.GetData(), + &length, NULL, NULL, NULL, NULL); + + while (status == ERROR_MORE_DATA) { + length = length * 2; + if (!buffer.Resize(length)) { + return result; + } + status = RegEnumValue(FOpenKey, index, buffer.GetData(), + &length, NULL, NULL, NULL, NULL); + } + + if (status == ERROR_SUCCESS) { + TString value = buffer.GetData(); + result.push_back(value); + } + } + } + + return result; + } + + TString ReadString(TString Name) { + TString result; + DWORD length; + DWORD dwRet; + DynamicBuffer buffer(0); + length = 0; + + dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, NULL, + &length); + if (dwRet == ERROR_MORE_DATA || dwRet == 0) { + if (!buffer.Resize(length + 1)) { + return result; + } + dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, + (LPBYTE) buffer.GetData(), &length); + result = buffer.GetData(); + } + + return result; + } +}; + +WindowsPlatform::WindowsPlatform(void) : Platform() { + FMainThread = ::GetCurrentThreadId(); +} + +WindowsPlatform::~WindowsPlatform(void) { +} + +TString WindowsPlatform::GetPackageAppDirectory() { + return FilePath::IncludeTrailingSeparator( + GetPackageRootDirectory()) + _T("app"); +} + +TString WindowsPlatform::GetPackageLauncherDirectory() { + return GetPackageRootDirectory(); +} + +TString WindowsPlatform::GetPackageRuntimeBinDirectory() { + return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime\\bin"); +} + +TCHAR* WindowsPlatform::ConvertStringToFileSystemString(TCHAR* Source, + bool &release) { + // Not Implemented. + return NULL; +} + +TCHAR* WindowsPlatform::ConvertFileSystemStringToString(TCHAR* Source, + bool &release) { + // Not Implemented. + return NULL; +} + +TString WindowsPlatform::GetPackageRootDirectory() { + TString result; + TString filename = GetModuleFileName(); + return FilePath::ExtractFilePath(filename); +} + +TString WindowsPlatform::GetAppDataDirectory() { + TString result; + TCHAR path[MAX_PATH]; + + if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path) == S_OK) { + result = path; + } + + return result; +} + +TString WindowsPlatform::GetAppName() { + TString result = GetModuleFileName(); + result = FilePath::ExtractFileName(result); + result = FilePath::ChangeFileExt(result, _T("")); + return result; +} + +void WindowsPlatform::ShowMessage(TString title, TString description) { + MessageBox(NULL, description.data(), + !title.empty() ? title.data() : description.data(), + MB_ICONERROR | MB_OK); +} + +void WindowsPlatform::ShowMessage(TString description) { + TString appname = GetModuleFileName(); + appname = FilePath::ExtractFileName(appname); + MessageBox(NULL, description.data(), appname.data(), MB_ICONERROR | MB_OK); +} + +MessageResponse WindowsPlatform::ShowResponseMessage(TString title, + TString description) { + MessageResponse result = mrCancel; + + if (::MessageBox(NULL, description.data(), title.data(), MB_OKCANCEL) == + IDOK) { + result = mrOK; + } + + return result; +} + +TString WindowsPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) { + TString result = FilePath::IncludeTrailingSeparator(RuntimePath) + + _T("jre\\bin\\jli.dll"); + + if (FilePath::FileExists(result) == false) { + result = FilePath::IncludeTrailingSeparator(RuntimePath) + + _T("bin\\jli.dll"); + } + + return result; +} + +ISectionalPropertyContainer* WindowsPlatform::GetConfigFile(TString FileName) { + IniFile *result = new IniFile(); + if (result == NULL) { + return NULL; + } + + result->LoadFromFile(FileName); + + return result; +} + +TString WindowsPlatform::GetModuleFileName() { + TString result; + DynamicBuffer buffer(MAX_PATH); + if (buffer.GetData() == NULL) { + return result; + } + + ::GetModuleFileName(NULL, buffer.GetData(), + static_cast (buffer.GetSize())); + + while (ERROR_INSUFFICIENT_BUFFER == GetLastError()) { + if (!buffer.Resize(buffer.GetSize() * 2)) { + return result; + } + ::GetModuleFileName(NULL, buffer.GetData(), + static_cast (buffer.GetSize())); + } + + result = buffer.GetData(); + return result; +} + +Module WindowsPlatform::LoadLibrary(TString FileName) { + return ::LoadLibrary(FileName.data()); +} + +void WindowsPlatform::FreeLibrary(Module AModule) { + ::FreeLibrary((HMODULE) AModule); +} + +Procedure WindowsPlatform::GetProcAddress(Module AModule, + std::string MethodName) { + return ::GetProcAddress((HMODULE) AModule, MethodName.c_str()); +} + +bool WindowsPlatform::IsMainThread() { + bool result = (FMainThread == ::GetCurrentThreadId()); + return result; +} + +TString WindowsPlatform::GetTempDirectory() { + TString result; + PWSTR userDir = 0; + + if (SUCCEEDED(SHGetKnownFolderPath( + FOLDERID_Profile, + 0, + NULL, + &userDir))) { + result = userDir; + result += WINDOWS_JPACKAGE_TMP_DIR; + CoTaskMemFree(userDir); + } + + return result; +} + +static BOOL CALLBACK enumWindows(HWND winHandle, LPARAM lParam) { + DWORD pid = (DWORD) lParam, wPid = 0; + GetWindowThreadProcessId(winHandle, &wPid); + if (pid == wPid) { + SetForegroundWindow(winHandle); + return FALSE; + } + return TRUE; +} + +TPlatformNumber WindowsPlatform::GetMemorySize() { + SYSTEM_INFO si; + GetSystemInfo(&si); + size_t result = (size_t) si.lpMaximumApplicationAddress; + result = result / 1048576; // Convert from bytes to megabytes. + return result; +} + +std::vector FilterList(std::vector &Items, + std::wregex Pattern) { + std::vector result; + + for (std::vector::iterator it = Items.begin(); + it != Items.end(); ++it) { + TString item = *it; + std::wsmatch match; + + if (std::regex_search(item, match, Pattern)) { + result.push_back(item); + } + } + return result; +} + +Process* WindowsPlatform::CreateProcess() { + return new WindowsProcess(); +} + +void WindowsPlatform::InitStreamLocale(wios *stream) { + const std::locale empty_locale = std::locale::empty(); + const std::locale utf8_locale = + std::locale(empty_locale, new std::codecvt_utf8()); + stream->imbue(utf8_locale); +} + +void WindowsPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) { + if (pJavaLibrary == NULL) { + return; + } + + if (FilePath::FileExists(_T("msvcr100.dll")) == true) { + pJavaLibrary->AddDependency(_T("msvcr100.dll")); + } + + TString runtimeBin = GetPackageRuntimeBinDirectory(); + SetDllDirectory(runtimeBin.c_str()); +} + +void Platform::CopyString(char *Destination, + size_t NumberOfElements, const char *Source) { + strcpy_s(Destination, NumberOfElements, Source); + + if (NumberOfElements > 0) { + Destination[NumberOfElements - 1] = '\0'; + } +} + +void Platform::CopyString(wchar_t *Destination, + size_t NumberOfElements, const wchar_t *Source) { + wcscpy_s(Destination, NumberOfElements, Source); + + if (NumberOfElements > 0) { + Destination[NumberOfElements - 1] = '\0'; + } +} + +// Owner must free the return value. +MultibyteString Platform::WideStringToMultibyteString( + const wchar_t* value) { + MultibyteString result; + size_t count = 0; + + if (value == NULL) { + return result; + } + + count = WideCharToMultiByte(CP_UTF8, 0, value, -1, NULL, 0, NULL, NULL); + + if (count > 0) { + result.data = new char[count + 1]; + result.length = WideCharToMultiByte(CP_UTF8, 0, value, -1, + result.data, (int)count, NULL, NULL); + } + + return result; +} + +// Owner must free the return value. +WideString Platform::MultibyteStringToWideString(const char* value) { + WideString result; + size_t count = 0; + + if (value == NULL) { + return result; + } + + mbstowcs_s(&count, NULL, 0, value, _TRUNCATE); + + if (count > 0) { + result.data = new wchar_t[count + 1]; + mbstowcs_s(&result.length, result.data, count, value, count); + } + + return result; +} + +FileHandle::FileHandle(std::wstring FileName) { + FHandle = ::CreateFile(FileName.data(), GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); +} + +FileHandle::~FileHandle() { + if (IsValid() == true) { + ::CloseHandle(FHandle); + } +} + +bool FileHandle::IsValid() { + return FHandle != INVALID_HANDLE_VALUE; +} + +HANDLE FileHandle::GetHandle() { + return FHandle; +} + +FileMappingHandle::FileMappingHandle(HANDLE FileHandle) { + FHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); +} + +bool FileMappingHandle::IsValid() { + return FHandle != NULL; +} + +FileMappingHandle::~FileMappingHandle() { + if (IsValid() == true) { + ::CloseHandle(FHandle); + } +} + +HANDLE FileMappingHandle::GetHandle() { + return FHandle; +} + +FileData::FileData(HANDLE Handle) { + FBaseAddress = ::MapViewOfFile(Handle, FILE_MAP_READ, 0, 0, 0); +} + +FileData::~FileData() { + if (IsValid() == true) { + ::UnmapViewOfFile(FBaseAddress); + } +} + +bool FileData::IsValid() { + return FBaseAddress != NULL; +} + +LPVOID FileData::GetBaseAddress() { + return FBaseAddress; +} + +WindowsLibrary::WindowsLibrary(std::wstring FileName) { + FFileName = FileName; +} + +std::vector WindowsLibrary::GetImports() { + std::vector result; + FileHandle library(FFileName); + + if (library.IsValid() == true) { + FileMappingHandle mapping(library.GetHandle()); + + if (mapping.IsValid() == true) { + FileData fileData(mapping.GetHandle()); + + if (fileData.IsValid() == true) { + PIMAGE_DOS_HEADER dosHeader = + (PIMAGE_DOS_HEADER) fileData.GetBaseAddress(); + PIMAGE_FILE_HEADER pImgFileHdr = + (PIMAGE_FILE_HEADER) fileData.GetBaseAddress(); + if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) { + result = DumpPEFile(dosHeader); + } + } + } + } + + return result; +} + +// Given an RVA, look up the section header that encloses it and return a +// pointer to its IMAGE_SECTION_HEADER + +PIMAGE_SECTION_HEADER WindowsLibrary::GetEnclosingSectionHeader(DWORD rva, + PIMAGE_NT_HEADERS pNTHeader) { + PIMAGE_SECTION_HEADER result = 0; + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader); + + for (unsigned index = 0; index < pNTHeader->FileHeader.NumberOfSections; + index++, section++) { + // Is the RVA is within this section? + if ((rva >= section->VirtualAddress) && + (rva < (section->VirtualAddress + section->Misc.VirtualSize))) { + result = section; + } + } + + return result; +} + +LPVOID WindowsLibrary::GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader, + DWORD imageBase) { + LPVOID result = 0; + PIMAGE_SECTION_HEADER pSectionHdr = GetEnclosingSectionHeader(rva, + pNTHeader); + + if (pSectionHdr != NULL) { + INT delta = (INT) ( + pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData); + DWORD_PTR dwp = (DWORD_PTR) (imageBase + rva - delta); + result = reinterpret_cast (dwp); // VS2017 - FIXME + } + + return result; +} + +std::vector WindowsLibrary::GetImportsSection(DWORD base, + PIMAGE_NT_HEADERS pNTHeader) { + std::vector result; + + // Look up where the imports section is located. Normally in + // the .idata section, + // but not necessarily so. Therefore, grab the RVA from the data dir. + DWORD importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[ + IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + + if (importsStartRVA != NULL) { + // Get the IMAGE_SECTION_HEADER that contains the imports. This is + // usually the .idata section, but doesn't have to be. + PIMAGE_SECTION_HEADER pSection = + GetEnclosingSectionHeader(importsStartRVA, pNTHeader); + + if (pSection != NULL) { + PIMAGE_IMPORT_DESCRIPTOR importDesc = + (PIMAGE_IMPORT_DESCRIPTOR) GetPtrFromRVA( + importsStartRVA, pNTHeader, base); + + if (importDesc != NULL) { + while (true) { + // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR + if ((importDesc->TimeDateStamp == 0) && + (importDesc->Name == 0)) { + break; + } + + std::string filename = (char*) GetPtrFromRVA( + importDesc->Name, pNTHeader, base); + result.push_back(PlatformString(filename)); + importDesc++; // advance to next IMAGE_IMPORT_DESCRIPTOR + } + } + } + } + + return result; +} + +std::vector WindowsLibrary::DumpPEFile(PIMAGE_DOS_HEADER dosHeader) { + std::vector result; + // all of this is VS2017 - FIXME + DWORD_PTR dwDosHeaders = reinterpret_cast (dosHeader); + DWORD_PTR dwPIHeaders = dwDosHeaders + (DWORD) (dosHeader->e_lfanew); + + PIMAGE_NT_HEADERS pNTHeader = + reinterpret_cast (dwPIHeaders); + + // Verify that the e_lfanew field gave us a reasonable + // pointer and the PE signature. + // TODO: To really fix JDK-8131321 this condition needs to be changed. + // There is a matching change + // in JavaVirtualMachine.cpp that also needs to be changed. + if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) { + DWORD base = (DWORD) (dwDosHeaders); + result = GetImportsSection(base, pNTHeader); + } + + return result; +} + +#include + +WindowsJob::WindowsJob() { + FHandle = NULL; +} + +WindowsJob::~WindowsJob() { + if (FHandle != NULL) { + CloseHandle(FHandle); + } +} + +HANDLE WindowsJob::GetHandle() { + if (FHandle == NULL) { + FHandle = CreateJobObject(NULL, NULL); // GLOBAL + + if (FHandle == NULL) { + ::MessageBox(0, _T("Could not create job object"), + _T("TEST"), MB_OK); + } else { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0}; + + // Configure all child processes associated with + // the job to terminate when the + jeli.BasicLimitInformation.LimitFlags = + JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + if (0 == SetInformationJobObject(FHandle, + JobObjectExtendedLimitInformation, &jeli, sizeof (jeli))) { + ::MessageBox(0, _T("Could not SetInformationJobObject"), + _T("TEST"), MB_OK); + } + } + } + + return FHandle; +} + +// Initialize static member of WindowsProcess +WindowsJob WindowsProcess::FJob; + +WindowsProcess::WindowsProcess() : Process() { + FRunning = false; +} + +WindowsProcess::~WindowsProcess() { + Terminate(); +} + +void WindowsProcess::Cleanup() { + CloseHandle(FProcessInfo.hProcess); + CloseHandle(FProcessInfo.hThread); +} + +bool WindowsProcess::IsRunning() { + bool result = false; + + HANDLE handle = ::CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); + if (handle == INVALID_HANDLE_VALUE) { + return false; + } + + PROCESSENTRY32 process = {0}; + process.dwSize = sizeof (process); + + if (::Process32First(handle, &process)) { + do { + if (process.th32ProcessID == FProcessInfo.dwProcessId) { + result = true; + break; + } + } while (::Process32Next(handle, &process)); + } + + CloseHandle(handle); + + return result; +} + +bool WindowsProcess::Terminate() { + bool result = false; + + if (IsRunning() == true && FRunning == true) { + FRunning = false; + } + + return result; +} + +bool WindowsProcess::Execute(const TString Application, + const std::vector Arguments, bool AWait) { + bool result = false; + + if (FRunning == false) { + FRunning = true; + + STARTUPINFO startupInfo; + ZeroMemory(&startupInfo, sizeof (startupInfo)); + startupInfo.cb = sizeof (startupInfo); + ZeroMemory(&FProcessInfo, sizeof (FProcessInfo)); + + TString command = Application; + + for (std::vector::const_iterator iterator = Arguments.begin(); + iterator != Arguments.end(); iterator++) { + command += TString(_T(" ")) + *iterator; + } + + if (::CreateProcess(Application.data(), (wchar_t*)command.data(), NULL, + NULL, FALSE, 0, NULL, NULL, &startupInfo, &FProcessInfo) + == FALSE) { + TString message = PlatformString::Format( + _T("Error: Unable to create process %s"), + Application.data()); + throw Exception(message); + } else { + if (FJob.GetHandle() != NULL) { + if (::AssignProcessToJobObject(FJob.GetHandle(), + FProcessInfo.hProcess) == 0) { + // Failed to assign process to job. It doesn't prevent + // anything from continuing so continue. + } + } + + // Wait until child process exits. + if (AWait == true) { + Wait(); + // Close process and thread handles. + Cleanup(); + } + } + } + + return result; +} + +bool WindowsProcess::Wait() { + bool result = false; + + WaitForSingleObject(FProcessInfo.hProcess, INFINITE); + return result; +} + +TProcessID WindowsProcess::GetProcessID() { + return FProcessInfo.dwProcessId; +} + +bool WindowsProcess::ReadOutput() { + bool result = false; + // TODO implement + return result; +} + +void WindowsProcess::SetInput(TString Value) { + // TODO implement +} + +std::list WindowsProcess::GetOutput() { + ReadOutput(); + return Process::GetOutput(); +} --- old/src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.h 2019-11-18 21:16:02.611166100 -0500 +++ /dev/null 2019-11-18 21:16:04.000000000 -0500 @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef WINDOWSPLATFORM_H -#define WINDOWSPLATFORM_H - -#include -#include "Platform.h" - -class WindowsPlatform : virtual public Platform { -private: - DWORD FMainThread; - -public: - WindowsPlatform(void); - virtual ~WindowsPlatform(void); - - virtual TCHAR* ConvertStringToFileSystemString(TCHAR* Source, - bool &release); - virtual TCHAR* ConvertFileSystemStringToString(TCHAR* Source, - bool &release); - - virtual void ShowMessage(TString title, TString description); - virtual void ShowMessage(TString description); - virtual MessageResponse ShowResponseMessage(TString title, - TString description); - - virtual void SetCurrentDirectory(TString Value); - virtual TString GetPackageRootDirectory(); - virtual TString GetAppDataDirectory(); - virtual TString GetAppName(); - virtual TString GetBundledJavaLibraryFileName(TString RuntimePath); - TString GetPackageAppDirectory(); - TString GetPackageLauncherDirectory(); - TString GetPackageRuntimeBinDirectory(); - - virtual ISectionalPropertyContainer* GetConfigFile(TString FileName); - - virtual TString GetModuleFileName(); - virtual Module LoadLibrary(TString FileName); - virtual void FreeLibrary(Module AModule); - virtual Procedure GetProcAddress(Module AModule, std::string MethodName); - - virtual Process* CreateProcess(); - - virtual bool IsMainThread(); - virtual TPlatformNumber GetMemorySize(); - - virtual TString GetTempDirectory(); - void InitStreamLocale(wios *stream); - void addPlatformDependencies(JavaLibrary *pJavaLibrary); -}; - -class FileHandle { -private: - HANDLE FHandle; - -public: - FileHandle(std::wstring FileName); - ~FileHandle(); - - bool IsValid(); - HANDLE GetHandle(); -}; - - -class FileMappingHandle { -private: - HANDLE FHandle; - -public: - FileMappingHandle(HANDLE FileHandle); - ~FileMappingHandle(); - - bool IsValid(); - HANDLE GetHandle(); -}; - - -class FileData { -private: - LPVOID FBaseAddress; - -public: - FileData(HANDLE Handle); - ~FileData(); - - bool IsValid(); - LPVOID GetBaseAddress(); -}; - - -class WindowsLibrary { -private: - TString FFileName; - - // Given an RVA, look up the section header that encloses it and return a - // pointer to its IMAGE_SECTION_HEADER - static PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva, - PIMAGE_NT_HEADERS pNTHeader); - static LPVOID GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader, - DWORD imageBase); - static std::vector GetImportsSection(DWORD base, - PIMAGE_NT_HEADERS pNTHeader); - static std::vector DumpPEFile(PIMAGE_DOS_HEADER dosHeader); - -public: - WindowsLibrary(const TString FileName); - - std::vector GetImports(); -}; - - -class WindowsJob { -private: - HANDLE FHandle; - -public: - WindowsJob(); - ~WindowsJob(); - - HANDLE GetHandle(); -}; - - -class WindowsProcess : public Process { -private: - bool FRunning; - - PROCESS_INFORMATION FProcessInfo; - static WindowsJob FJob; - - void Cleanup(); - bool ReadOutput(); - -public: - WindowsProcess(); - virtual ~WindowsProcess(); - - virtual bool IsRunning(); - virtual bool Terminate(); - virtual bool Execute(const TString Application, - const std::vector Arguments, bool AWait = false); - virtual bool Wait(); - virtual TProcessID GetProcessID(); - virtual void SetInput(TString Value); - virtual std::list GetOutput(); -}; - -#endif // WINDOWSPLATFORM_H --- /dev/null 2019-11-18 21:16:04.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libapplauncher/WindowsPlatform.h 2019-11-18 21:15:59.383453800 -0500 @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef WINDOWSPLATFORM_H +#define WINDOWSPLATFORM_H + +#include +#include "Platform.h" + +class WindowsPlatform : virtual public Platform { +private: + DWORD FMainThread; + +public: + WindowsPlatform(void); + virtual ~WindowsPlatform(void); + + virtual TCHAR* ConvertStringToFileSystemString(TCHAR* Source, + bool &release); + virtual TCHAR* ConvertFileSystemStringToString(TCHAR* Source, + bool &release); + + virtual void ShowMessage(TString title, TString description); + virtual void ShowMessage(TString description); + virtual MessageResponse ShowResponseMessage(TString title, + TString description); + + virtual TString GetPackageRootDirectory(); + virtual TString GetAppDataDirectory(); + virtual TString GetAppName(); + virtual TString GetBundledJavaLibraryFileName(TString RuntimePath); + TString GetPackageAppDirectory(); + TString GetPackageLauncherDirectory(); + TString GetPackageRuntimeBinDirectory(); + + virtual ISectionalPropertyContainer* GetConfigFile(TString FileName); + + virtual TString GetModuleFileName(); + virtual Module LoadLibrary(TString FileName); + virtual void FreeLibrary(Module AModule); + virtual Procedure GetProcAddress(Module AModule, std::string MethodName); + + virtual Process* CreateProcess(); + + virtual bool IsMainThread(); + virtual TPlatformNumber GetMemorySize(); + + virtual TString GetTempDirectory(); + void InitStreamLocale(wios *stream); + void addPlatformDependencies(JavaLibrary *pJavaLibrary); +}; + +class FileHandle { +private: + HANDLE FHandle; + +public: + FileHandle(std::wstring FileName); + ~FileHandle(); + + bool IsValid(); + HANDLE GetHandle(); +}; + + +class FileMappingHandle { +private: + HANDLE FHandle; + +public: + FileMappingHandle(HANDLE FileHandle); + ~FileMappingHandle(); + + bool IsValid(); + HANDLE GetHandle(); +}; + + +class FileData { +private: + LPVOID FBaseAddress; + +public: + FileData(HANDLE Handle); + ~FileData(); + + bool IsValid(); + LPVOID GetBaseAddress(); +}; + + +class WindowsLibrary { +private: + TString FFileName; + + // Given an RVA, look up the section header that encloses it and return a + // pointer to its IMAGE_SECTION_HEADER + static PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva, + PIMAGE_NT_HEADERS pNTHeader); + static LPVOID GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader, + DWORD imageBase); + static std::vector GetImportsSection(DWORD base, + PIMAGE_NT_HEADERS pNTHeader); + static std::vector DumpPEFile(PIMAGE_DOS_HEADER dosHeader); + +public: + WindowsLibrary(const TString FileName); + + std::vector GetImports(); +}; + + +class WindowsJob { +private: + HANDLE FHandle; + +public: + WindowsJob(); + ~WindowsJob(); + + HANDLE GetHandle(); +}; + + +class WindowsProcess : public Process { +private: + bool FRunning; + + PROCESS_INFORMATION FProcessInfo; + static WindowsJob FJob; + + void Cleanup(); + bool ReadOutput(); + +public: + WindowsProcess(); + virtual ~WindowsProcess(); + + virtual bool IsRunning(); + virtual bool Terminate(); + virtual bool Execute(const TString Application, + const std::vector Arguments, bool AWait = false); + virtual bool Wait(); + virtual TProcessID GetProcessID(); + virtual void SetInput(TString Value); + virtual std::list GetOutput(); +}; + +#endif // WINDOWSPLATFORM_H --- old/src/jdk.jpackage/windows/native/libjpackage/ByteBuffer.cpp 2019-11-18 21:16:22.894006300 -0500 +++ /dev/null 2019-11-18 21:16:24.000000000 -0500 @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "ByteBuffer.h" - -#include - -ByteBuffer::ByteBuffer() { - buffer.reserve(1024); -} - -ByteBuffer::~ByteBuffer() { -} - -LPBYTE ByteBuffer::getPtr() { - return &buffer[0]; -} - -size_t ByteBuffer::getPos() { - return buffer.size(); -} - -void ByteBuffer::AppendString(wstring str) { - size_t len = (str.size() + 1) * sizeof (WCHAR); - AppendBytes((BYTE*) str.c_str(), len); -} - -void ByteBuffer::AppendWORD(WORD word) { - AppendBytes((BYTE*) & word, sizeof (WORD)); -} - -void ByteBuffer::Align(size_t bytesNumber) { - size_t pos = getPos(); - if (pos % bytesNumber) { - DWORD dwNull = 0; - size_t len = bytesNumber - pos % bytesNumber; - AppendBytes((BYTE*) & dwNull, len); - } -} - -void ByteBuffer::AppendBytes(BYTE* ptr, size_t len) { - buffer.insert(buffer.end(), ptr, ptr + len); -} - -void ByteBuffer::ReplaceWORD(size_t offset, WORD word) { - ReplaceBytes(offset, (BYTE*) & word, sizeof (WORD)); -} - -void ByteBuffer::ReplaceBytes(size_t offset, BYTE* ptr, size_t len) { - for (size_t i = 0; i < len; i++) { - buffer[offset + i] = *(ptr + i); - } -} --- /dev/null 2019-11-18 21:16:24.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/ByteBuffer.cpp 2019-11-18 21:16:19.580917400 -0500 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ByteBuffer.h" + +#include + +ByteBuffer::ByteBuffer() { + buffer.reserve(1024); +} + +ByteBuffer::~ByteBuffer() { +} + +LPBYTE ByteBuffer::getPtr() { + return &buffer[0]; +} + +size_t ByteBuffer::getPos() { + return buffer.size(); +} + +void ByteBuffer::AppendString(wstring str) { + size_t len = (str.size() + 1) * sizeof (WCHAR); + AppendBytes((BYTE*) str.c_str(), len); +} + +void ByteBuffer::AppendWORD(WORD word) { + AppendBytes((BYTE*) & word, sizeof (WORD)); +} + +void ByteBuffer::Align(size_t bytesNumber) { + size_t pos = getPos(); + if (pos % bytesNumber) { + DWORD dwNull = 0; + size_t len = bytesNumber - pos % bytesNumber; + AppendBytes((BYTE*) & dwNull, len); + } +} + +void ByteBuffer::AppendBytes(BYTE* ptr, size_t len) { + buffer.insert(buffer.end(), ptr, ptr + len); +} + +void ByteBuffer::ReplaceWORD(size_t offset, WORD word) { + ReplaceBytes(offset, (BYTE*) & word, sizeof (WORD)); +} + +void ByteBuffer::ReplaceBytes(size_t offset, BYTE* ptr, size_t len) { + for (size_t i = 0; i < len; i++) { + buffer[offset + i] = *(ptr + i); + } +} --- old/src/jdk.jpackage/windows/native/libjpackage/ByteBuffer.h 2019-11-18 21:16:33.393679800 -0500 +++ /dev/null 2019-11-18 21:16:34.000000000 -0500 @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef BYTEBUFFER_H -#define BYTEBUFFER_H - -#include -#include -#include - -using namespace std; - -class ByteBuffer { -public: - ByteBuffer(); - ~ByteBuffer(); - - LPBYTE getPtr(); - size_t getPos(); - - void AppendString(wstring str); - void AppendWORD(WORD word); - void AppendBytes(BYTE* ptr, size_t len); - - void ReplaceWORD(size_t offset, WORD word); - void ReplaceBytes(size_t offset, BYTE* ptr, size_t len); - - void Align(size_t bytesNumber); - -private: - vector buffer; -}; - -#endif // BYTEBUFFER_H \ No newline at end of file --- /dev/null 2019-11-18 21:16:34.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/ByteBuffer.h 2019-11-18 21:16:29.878824900 -0500 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef BYTEBUFFER_H +#define BYTEBUFFER_H + +#include +#include +#include + +using namespace std; + +class ByteBuffer { +public: + ByteBuffer(); + ~ByteBuffer(); + + LPBYTE getPtr(); + size_t getPos(); + + void AppendString(wstring str); + void AppendWORD(WORD word); + void AppendBytes(BYTE* ptr, size_t len); + + void ReplaceWORD(size_t offset, WORD word); + void ReplaceBytes(size_t offset, BYTE* ptr, size_t len); + + void Align(size_t bytesNumber); + +private: + vector buffer; +}; + +#endif // BYTEBUFFER_H + --- /dev/null 2019-11-18 21:16:53.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/ErrorHandling.cpp 2019-11-18 21:16:50.368625500 -0500 @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +#include "ErrorHandling.h" +#include "Log.h" + + +namespace { + +tstring getFilename(const SourceCodePos& pos) { + const std::string buf(pos.file); + const std::string::size_type idx = buf.find_last_of("\\/"); + if (idx == std::string::npos) { + return tstrings::fromUtf8(buf); + } + return tstrings::fromUtf8(buf.substr(idx + 1)); +} + +void reportError(const SourceCodePos& pos, const tstring& msg) { + Logger::defaultLogger().log(Logger::LOG_ERROR, getFilename(pos).c_str(), + pos.lno, tstrings::fromUtf8(pos.func).c_str(), msg); +} + +} // namespace + +void reportError(const SourceCodePos& pos, const std::exception& e) { + reportError(pos, (tstrings::any() << "Exception with message \'" + << e.what() << "\' caught").tstr()); +} + + +void reportUnknownError(const SourceCodePos& pos) { + reportError(pos, _T("Unknown exception caught")); +} + + +std::string makeMessage(const std::exception& e, const SourceCodePos& pos) { + std::ostringstream printer; + printer << getFilename(pos) << "(" << pos.lno << ") at " + << pos.func << "(): " + << e.what(); + return printer.str(); +} + + +namespace { + +bool isNotSpace(int chr) { + return isspace(chr) == 0; +} + + +enum TrimMode { + TrimLeading = 0x10, + TrimTrailing = 0x20, + TrimBoth = TrimLeading | TrimTrailing +}; + +// Returns position of the last printed character in the given string. +// Returns std::string::npos if nothing was printed. +size_t printWithoutWhitespaces(std::ostream& out, const std::string& str, + TrimMode mode) { + std::string::const_reverse_iterator it = str.rbegin(); + std::string::const_reverse_iterator end = str.rend(); + + if (mode & TrimLeading) { + // skip leading whitespace + std::string::const_iterator entry = std::find_if(str.begin(), + str.end(), isNotSpace); + end = std::string::const_reverse_iterator(entry); + } + + if (mode & TrimTrailing) { + // skip trailing whitespace + it = std::find_if(it, end, isNotSpace); + } + + if (it == end) { + return std::string::npos; + } + + const size_t pos = str.rend() - end; + const size_t len = end - it; + out.write(str.c_str() + pos, len); + return pos + len - 1; +} + +} // namespace + +std::string joinErrorMessages(const std::string& a, const std::string& b) { + const std::string endPhraseChars(";.,:!?"); + const std::string space(" "); + const std::string dotAndSpace(". "); + + std::ostringstream printer; + printer.exceptions(std::ios::failbit | std::ios::badbit); + + size_t idx = printWithoutWhitespaces(printer, a, TrimTrailing); + size_t extra = 0; + if (idx < a.size() && endPhraseChars.find(a[idx]) == std::string::npos) { + printer << dotAndSpace; + extra = dotAndSpace.size(); + } else if (idx != std::string::npos) { + printer << space; + extra = space.size(); + } + + idx = printWithoutWhitespaces(printer, b, TrimBoth); + + const std::string str = printer.str(); + + if (std::string::npos == idx && extra) { + // Nothing printed from the 'b' message. Backout delimiter string. + return str.substr(0, str.size() - extra); + } + return str; +} --- /dev/null 2019-11-18 21:17:04.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/ErrorHandling.h 2019-11-18 21:17:00.925345800 -0500 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ErrorHandling_h +#define ErrorHandling_h + + +#include + +#include "SourceCodePos.h" +#include "tstrings.h" + + +// +// Exception handling helpers. Allow transparent exception logging. +// Use as follows: +// +// void foo () { +// JP_TRY; +// +// if (!do_something()) { +// JP_THROW("do_something() failed"); +// } +// +// JP_CATCH_ALL; +// } +// + + +// Logs std::exception caught at 'pos'. +void reportError(const SourceCodePos& pos, const std::exception& e); +// Logs unknown exception caught at 'pos'. +// Assumed to be called from catch (...) {} +void reportUnknownError(const SourceCodePos& pos); + +std::string makeMessage(const std::exception& e, const SourceCodePos& pos); + +std::string joinErrorMessages(const std::string& a, const std::string& b); + + +template +class JpError: public Base { +public: + JpError(const Base& e, const SourceCodePos& pos): + Base(e), msg(::makeMessage(e, pos)) { + } + + ~JpError() throw() { + } + + // override Base::what() + const char* what() const throw() { + return msg.c_str(); + } +private: + // Assert Base is derived from std::exception + enum { isDerivedFromStdException = + sizeof(static_cast((Base*)0)) }; + + std::string msg; +}; + +template +inline JpError makeException(const T& obj, const SourceCodePos& p) { + return JpError(obj, p); +} + +inline JpError makeException( + const std::string& msg, const SourceCodePos& p) { + return JpError(std::runtime_error(msg), p); +} + +inline JpError makeException( + const tstrings::any& msg, const SourceCodePos& p) { + return makeException(msg.str(), p); +} + +inline JpError makeException( + std::string::const_pointer msg, const SourceCodePos& p) { + return makeException(std::string(msg), p); +} + + +#define JP_REPORT_ERROR(e) reportError(JP_SOURCE_CODE_POS, e) +#define JP_REPORT_UNKNOWN_ERROR reportUnknownError(JP_SOURCE_CODE_POS) + +// Redefine locally in cpp file(s) if need more handling than just reporting +#define JP_HANDLE_ERROR(e) JP_REPORT_ERROR(e) +#define JP_HANDLE_UNKNOWN_ERROR JP_REPORT_UNKNOWN_ERROR + + +#define JP_TRY \ + try \ + { \ + do {} while(0) + +#define JP_DEFAULT_CATCH_EXCEPTIONS \ + JP_CATCH_STD_EXCEPTION \ + JP_CATCH_UNKNOWN_EXCEPTION + +#define JP_CATCH_EXCEPTIONS \ + JP_DEFAULT_CATCH_EXCEPTIONS + +#define JP_CATCH_ALL \ + } \ + JP_CATCH_EXCEPTIONS \ + do {} while(0) + +#define JP_CATCH_STD_EXCEPTION \ + catch (const std::exception& e) \ + { \ + JP_HANDLE_ERROR(e); \ + } + +#define JP_CATCH_UNKNOWN_EXCEPTION \ + catch (...) \ + { \ + JP_HANDLE_UNKNOWN_ERROR; \ + } + + +#define JP_THROW(e) throw makeException((e), JP_SOURCE_CODE_POS) + +#define JP_NO_THROW(expr) \ + JP_TRY; \ + expr; \ + JP_CATCH_ALL + +#endif // #ifndef ErrorHandling_h --- /dev/null 2019-11-18 21:17:14.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/FileUtils.cpp 2019-11-18 21:17:11.447075300 -0500 @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include + +#include "FileUtils.h" +#include "WinErrorHandling.h" +#include "Log.h" + + +// Needed by FileUtils::isDirectoryNotEmpty +#pragma comment(lib, "shlwapi") + + +namespace FileUtils { + +namespace { + + +tstring reservedFilenameChars() { + tstring buf; + for (char charCode = 0; charCode < 32; ++charCode) { + buf.append(1, charCode); + } + buf += _T("<>:\"|?*/\\"); + return buf; +} + +} // namespace + +bool isDirSeparator(const tstring::value_type c) { + return (c == '/' || c == '\\'); +} + +bool isFileExists(const tstring &filePath) { + return GetFileAttributes(filePath.c_str()) != INVALID_FILE_ATTRIBUTES; +} + +namespace { +bool isDirectoryAttrs(const DWORD attrs) { + return attrs != INVALID_FILE_ATTRIBUTES + && (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0; +} +} // namespace + +bool isDirectory(const tstring &filePath) { + return isDirectoryAttrs(GetFileAttributes(filePath.c_str())); +} + +bool isDirectoryNotEmpty(const tstring &dirPath) { + if (!isDirectory(dirPath)) { + return false; + } + return FALSE == PathIsDirectoryEmpty(dirPath.c_str()); +} + +tstring dirname(const tstring &path) { + tstring::size_type pos = path.find_last_of(_T("\\/")); + if (pos != tstring::npos) { + pos = path.find_last_not_of(_T("\\/"), pos); // skip trailing slashes + } + return pos == tstring::npos ? tstring() : path.substr(0, pos + 1); +} + +tstring basename(const tstring &path) { + const tstring::size_type pos = path.find_last_of(_T("\\/")); + if (pos == tstring::npos) { + return path; + } + return path.substr(pos + 1); +} + +tstring suffix(const tstring &path) { + const tstring::size_type pos = path.rfind('.'); + if (pos == tstring::npos) { + return tstring(); + } + const tstring::size_type dirSepPos = path.find_first_of(_T("\\/"), + pos + 1); + if (dirSepPos != tstring::npos) { + return tstring(); + } + // test for '/..' and '..' cases + if (pos != 0 && path[pos - 1] == '.' + && (pos == 1 || isDirSeparator(path[pos - 2]))) { + return tstring(); + } + return path.substr(pos); +} + +tstring combinePath(const tstring& parent, const tstring& child) { + if (parent.empty()) { + return child; + } + if (child.empty()) { + return parent; + } + + tstring parentWOSlash = removeTrailingSlash(parent); + // also handle the case when child contains starting slash + bool childHasSlash = isDirSeparator(child.front()); + tstring childWOSlash = childHasSlash ? child.substr(1) : child; + + return parentWOSlash + _T("\\") + childWOSlash; +} + +tstring removeTrailingSlash(const tstring& path) { + if (path.empty()) { + return path; + } + tstring::const_reverse_iterator it = path.rbegin(); + tstring::const_reverse_iterator end = path.rend(); + + while (it != end && isDirSeparator(*it)) { + ++it; + } + return path.substr(0, end - it); +} + +tstring normalizePath(tstring v) { + std::replace(v.begin(), v.end(), '/', '\\'); + return tstrings::toLower(v); +} + +namespace { + +bool createNewFile(const tstring& path) { + HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + // if the file exists => h == INVALID_HANDLE_VALUE & GetLastError + // returns ERROR_FILE_EXISTS + if (h != INVALID_HANDLE_VALUE) { + CloseHandle(h); + LOG_TRACE(tstrings::any() << "Created [" << path << "] file"); + return true; + } + return false; +} + +} // namespace + +tstring createTempFile(const tstring &prefix, const tstring &suffix, + const tstring &path) { + const tstring invalidChars = reservedFilenameChars(); + + if (prefix.find_first_of(invalidChars) != tstring::npos) { + JP_THROW(tstrings::any() << "Illegal characters in prefix=" << prefix); + } + + if (suffix.find_first_of(invalidChars) != tstring::npos) { + JP_THROW(tstrings::any() << "Illegal characters in suffix=" << suffix); + } + + int rnd = (int)GetTickCount(); + + // do no more than 100 attempts + for (int i=0; i<100; i++) { + const tstring filePath = mkpath() << path << (prefix + + (tstrings::any() << (rnd + i)).tstr() + suffix); + if (createNewFile(filePath)) { + return filePath; + } + } + + // 100 attempts failed + JP_THROW(tstrings::any() << "createTempFile(" << prefix << ", " + << suffix << ", " + << path << ") failed"); +} + +tstring createTempDirectory(const tstring &prefix, const tstring &suffix, + const tstring &basedir) { + const tstring filePath = createTempFile(prefix, suffix, basedir); + // delete the file and create directory with the same name + deleteFile(filePath); + createDirectory(filePath); + return filePath; +} + +tstring createUniqueFile(const tstring &prototype) { + if (createNewFile(prototype)) { + return prototype; + } + + return createTempFile(replaceSuffix(basename(prototype)), + suffix(prototype), dirname(prototype)); +} + +namespace { + +void createDir(const tstring path, LPSECURITY_ATTRIBUTES saAttr, + tstring_array* createdDirs=0) { + if (CreateDirectory(path.c_str(), saAttr)) { + LOG_TRACE(tstrings::any() << "Created [" << path << "] directory"); + if (createdDirs) { + createdDirs->push_back(removeTrailingSlash(path)); + } + } else { + const DWORD createDirectoryErr = GetLastError(); + // if saAttr is specified, fail even if the directory exists + if (saAttr != NULL || !isDirectory(path)) { + JP_THROW(SysError(tstrings::any() << "CreateDirectory(" + << path << ") failed", CreateDirectory, createDirectoryErr)); + } + } +} + +} + +void createDirectory(const tstring &path, tstring_array* createdDirs) { + const tstring dirPath = removeTrailingSlash(path) + _T("\\"); + + tstring::size_type pos = dirPath.find_first_of(_T("\\/")); + while (pos != tstring::npos) { + const tstring subdirPath = dirPath.substr(0, pos + 1); + createDir(subdirPath, NULL, createdDirs); + pos = dirPath.find_first_of(_T("\\/"), pos + 1); + } +} + + +void copyFile(const tstring& fromPath, const tstring& toPath, + bool failIfExists) { + createDirectory(dirname(toPath)); + if (!CopyFile(fromPath.c_str(), toPath.c_str(), + (failIfExists ? TRUE : FALSE))) { + JP_THROW(SysError(tstrings::any() + << "CopyFile(" << fromPath << ", " << toPath << ", " + << failIfExists << ") failed", CopyFile)); + } + LOG_TRACE(tstrings::any() << "Copied [" << fromPath << "] file to [" + << toPath << "]"); +} + + +namespace { + +void moveFileImpl(const tstring& fromPath, const tstring& toPath, + DWORD flags) { + const bool isDir = isDirectory(fromPath); + if (!MoveFileEx(fromPath.c_str(), toPath.empty() ? NULL : toPath.c_str(), + flags)) { + JP_THROW(SysError(tstrings::any() << "MoveFileEx(" << fromPath + << ", " << toPath << ", " << flags << ") failed", MoveFileEx)); + } + + const bool onReboot = 0 != (flags & MOVEFILE_DELAY_UNTIL_REBOOT); + + const LPCTSTR label = isDir ? _T("folder") : _T("file"); + + tstrings::any msg; + if (!toPath.empty()) { + if (onReboot) { + msg << "Move"; + } else { + msg << "Moved"; + } + msg << " '" << fromPath << "' " << label << " to '" << toPath << "'"; + } else { + if (onReboot) { + msg << "Delete"; + } else { + msg << "Deleted"; + } + msg << " '" << fromPath << "' " << label; + } + if (onReboot) { + msg << " on reboot"; + } + LOG_TRACE(msg); +} + +} // namespace + + +void moveFile(const tstring& fromPath, const tstring& toPath, + bool failIfExists) { + createDirectory(dirname(toPath)); + + DWORD flags = MOVEFILE_COPY_ALLOWED; + if (!failIfExists) { + flags |= MOVEFILE_REPLACE_EXISTING; + } + + moveFileImpl(fromPath, toPath, flags); +} + +void deleteFile(const tstring &path) +{ + if (!deleteFile(path, std::nothrow)) { + JP_THROW(SysError(tstrings::any() + << "DeleteFile(" << path << ") failed", DeleteFile)); + } +} + +namespace { + +bool notFound(const DWORD status=GetLastError()) { + return status == ERROR_FILE_NOT_FOUND || status == ERROR_PATH_NOT_FOUND; +} + +bool deleteFileImpl(const std::nothrow_t &, const tstring &path) { + const bool deleted = (DeleteFile(path.c_str()) != 0); + if (deleted) { + LOG_TRACE(tstrings::any() << "Deleted [" << path << "] file"); + return true; + } + return notFound(); +} + +} // namespace + +bool deleteFile(const tstring &path, const std::nothrow_t &) throw() +{ + bool deleted = deleteFileImpl(std::nothrow, path); + const DWORD status = GetLastError(); + if (!deleted && status == ERROR_ACCESS_DENIED) { + DWORD attrs = GetFileAttributes(path.c_str()); + SetLastError(status); + if (attrs == INVALID_FILE_ATTRIBUTES) { + return false; + } + if (attrs & FILE_ATTRIBUTE_READONLY) { + // DeleteFile() failed because file is R/O. + // Remove R/O attribute and retry DeleteFile(). + attrs &= ~FILE_ATTRIBUTE_READONLY; + if (SetFileAttributes(path.c_str(), attrs)) { + LOG_TRACE(tstrings::any() << "Discarded R/O attribute from [" + << path << "] file"); + deleted = deleteFileImpl(std::nothrow, path); + } else { + LOG_WARNING(SysError(tstrings::any() + << "Failed to discard R/O attribute from [" + << path << "] file. File will not be deleted", + SetFileAttributes).what()); + SetLastError(status); + } + } + } + + return deleted || notFound(); +} + +void deleteDirectory(const tstring &path) +{ + if (!deleteDirectory(path, std::nothrow)) { + JP_THROW(SysError(tstrings::any() + << "RemoveDirectory(" << path << ") failed", RemoveDirectory)); + } +} + +bool deleteDirectory(const tstring &path, const std::nothrow_t &) throw() +{ + const bool deleted = (RemoveDirectory(path.c_str()) != 0); + if (deleted) { + LOG_TRACE(tstrings::any() << "Deleted [" << path << "] directory"); + } + return deleted || notFound(); +} + +namespace { + +class DeleteFilesCallback: public DirectoryCallback { +public: + explicit DeleteFilesCallback(bool ff): failfast(ff), failed(false) { + } + + virtual bool onFile(const tstring& path) { + if (failfast) { + deleteFile(path); + } else { + updateStatus(deleteFile(path, std::nothrow)); + } + return true; + } + + bool good() const { + return !failed; + } + +protected: + void updateStatus(bool success) { + if (!success) { + failed = true; + } + } + + const bool failfast; +private: + bool failed; +}; + +class DeleteAllCallback: public DeleteFilesCallback { +public: + explicit DeleteAllCallback(bool failfast): DeleteFilesCallback(failfast) { + } + + virtual bool onDirectory(const tstring& path) { + if (failfast) { + deleteDirectoryRecursive(path); + } else { + updateStatus(deleteDirectoryRecursive(path, std::nothrow)); + } + return true; + } +}; + + +class BatchDeleter { + const tstring dirPath; + bool recursive; +public: + explicit BatchDeleter(const tstring& path): dirPath(path) { + deleteSubdirs(false); + } + + BatchDeleter& deleteSubdirs(bool v) { + recursive = v; + return *this; + } + + void execute() const { + if (!isFileExists(dirPath)) { + return; + } + iterateDirectory(true /* fail fast */); + if (recursive) { + deleteDirectory(dirPath); + } + } + + bool execute(const std::nothrow_t&) const { + if (!isFileExists(dirPath)) { + return true; + } + + if (!isDirectory(dirPath)) { + return false; + } + + JP_TRY; + if (!iterateDirectory(false /* ignore errors */)) { + return false; + } + if (recursive) { + return deleteDirectory(dirPath, std::nothrow); + } + return true; + JP_CATCH_ALL; + + return false; + } + +private: + bool iterateDirectory(bool failfast) const { + std::unique_ptr callback; + if (recursive) { + callback = std::unique_ptr( + new DeleteAllCallback(failfast)); + } else { + callback = std::unique_ptr( + new DeleteFilesCallback(failfast)); + } + + FileUtils::iterateDirectory(dirPath, *callback); + return callback->good(); + } +}; + +} // namespace + +void deleteFilesInDirectory(const tstring &dirPath) { + BatchDeleter(dirPath).execute(); +} + +bool deleteFilesInDirectory(const tstring &dirPath, + const std::nothrow_t &) throw() { + return BatchDeleter(dirPath).execute(std::nothrow); +} + +void deleteDirectoryRecursive(const tstring &dirPath) { + BatchDeleter(dirPath).deleteSubdirs(true).execute(); +} + +bool deleteDirectoryRecursive(const tstring &dirPath, + const std::nothrow_t &) throw() { + return BatchDeleter(dirPath).deleteSubdirs(true).execute(std::nothrow); +} + +namespace { + +struct FindFileDeleter { + typedef HANDLE pointer; + + void operator()(HANDLE h) { + if (h && h != INVALID_HANDLE_VALUE) { + FindClose(h); + } + } +}; + +typedef std::unique_ptr UniqueFindFileHandle; + +}; // namesace +void iterateDirectory(const tstring &dirPath, DirectoryCallback& callback) +{ + const tstring searchString = combinePath(dirPath, _T("*")); + WIN32_FIND_DATA findData; + UniqueFindFileHandle h(FindFirstFile(searchString.c_str(), &findData)); + if (h.get() == INVALID_HANDLE_VALUE) { + // GetLastError() == ERROR_FILE_NOT_FOUND is OK + // - no files in the directory + // ERROR_PATH_NOT_FOUND is returned + // if the parent directory does not exist + if (GetLastError() != ERROR_FILE_NOT_FOUND) { + JP_THROW(SysError(tstrings::any() << "FindFirstFile(" + << dirPath << ") failed", FindFirstFile)); + } + return; + } + + do { + const tstring fname(findData.cFileName); + const tstring filePath = combinePath(dirPath, fname); + if (!isDirectoryAttrs(findData.dwFileAttributes)) { + if (!callback.onFile(filePath)) { + return; + } + } else if (fname != _T(".") && fname != _T("..")) { + if (!callback.onDirectory(filePath)) { + return; + } + } + } while (FindNextFile(h.get(), &findData)); + + // expect GetLastError() == ERROR_NO_MORE_FILES + if (GetLastError() != ERROR_NO_MORE_FILES) { + JP_THROW(SysError(tstrings::any() << "FindNextFile(" + << dirPath << ") failed", FindNextFile)); + } +} + + +tstring replaceSuffix(const tstring& path, const tstring& newSuffix) { + return (path.substr(0, path.size() - suffix(path).size()) + newSuffix); +} + + +DirectoryIterator& DirectoryIterator::findItems(tstring_array& v) { + if (!isDirectory(root)) { + return *this; + } + + iterateDirectory(root, *this); + v.insert(v.end(), items.begin(), items.end()); + items = tstring_array(); + return *this; +} + +bool DirectoryIterator::onFile(const tstring& path) { + if (theWithFiles) { + items.push_back(path); + } + return true; +} + +bool DirectoryIterator::onDirectory(const tstring& path) { + if (theWithFolders) { + items.push_back(path); + } + if (theRecurse) { + DirectoryIterator(path).recurse(theRecurse) + .withFiles(theWithFiles) + .withFolders(theWithFolders) + .findItems(items); + } + return true; +} + + +namespace { + +struct DeleterFunctor { + // Order of items in the following enum is important! + // It controls order in which items of particular type will be deleted. + // See Deleter::execute(). + enum { + File, + FilesInDirectory, + RecursiveDirectory, + EmptyDirectory + }; + + void operator () (const Deleter::Path& path) const { + switch (path.second) { +#define DELETE_SOME(o, f)\ + case o:\ + f(path.first, std::nothrow);\ + break + + DELETE_SOME(File, deleteFile); + DELETE_SOME(EmptyDirectory, deleteDirectory); + DELETE_SOME(FilesInDirectory, deleteFilesInDirectory); + DELETE_SOME(RecursiveDirectory, deleteDirectoryRecursive); + +#undef DELETE_SOME + default: + break; + } + } +}; + +} // namespace + +void Deleter::execute() { + Paths tmp; + tmp.swap(paths); + + // Reorder items to delete. + std::stable_sort(tmp.begin(), tmp.end(), [] (const Paths::value_type& a, + const Paths::value_type& b) { + return a.second < b.second; + }); + + std::for_each(tmp.begin(), tmp.end(), DeleterFunctor()); +} + +Deleter& Deleter::appendFile(const tstring& path) { + paths.push_back(std::make_pair(path, DeleterFunctor::File)); + return *this; +} + +Deleter& Deleter::appendEmptyDirectory(const Directory& dir) { + tstring path = normalizePath(removeTrailingSlash(dir)); + const tstring parent = normalizePath(removeTrailingSlash(dir.parent)); + while(parent != path) { + appendEmptyDirectory(path); + path = dirname(path); + } + + return *this; +} + +Deleter& Deleter::appendEmptyDirectory(const tstring& path) { + paths.push_back(std::make_pair(path, DeleterFunctor::EmptyDirectory)); + return *this; +} + +Deleter& Deleter::appendAllFilesInDirectory(const tstring& path) { + paths.push_back(std::make_pair(path, DeleterFunctor::FilesInDirectory)); + return *this; +} + +Deleter& Deleter::appendRecursiveDirectory(const tstring& path) { + paths.push_back(std::make_pair(path, DeleterFunctor::RecursiveDirectory)); + return *this; +} + + +FileWriter::FileWriter(const tstring& path): dstPath(path) { + tmpFile = FileUtils::createTempFile(_T("jds"), _T(".tmp"), + FileUtils::dirname(path)); + + cleaner.appendFile(tmpFile); + + // we want to get exception on error + tmp.exceptions(std::ifstream::failbit | std::ifstream::badbit); + tmp.open(tmpFile, std::ios::binary | std::ios::trunc); +} + +FileWriter& FileWriter::write(const void* buf, size_t bytes) { + tmp.write(static_cast(buf), bytes); + return *this; +} + +void FileWriter::finalize() { + tmp.close(); + + FileUtils::moveFile(tmpFile, dstPath, false); + + // cancel file deletion + cleaner.cancel(); +} + +} // namespace FileUtils --- /dev/null 2019-11-18 21:17:25.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/FileUtils.h 2019-11-18 21:17:22.525608100 -0500 @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef FILEUTILS_H +#define FILEUTILS_H + + +#include +#include "SysInfo.h" + + +namespace FileUtils { + + // Returns 'true' if the given character is a path separator. + bool isDirSeparator(const tstring::value_type c); + + // checks if the file or directory exists + bool isFileExists(const tstring &filePath); + + // checks is the specified file is a directory + // returns false if the path does not exist + bool isDirectory(const tstring &filePath); + + // checks if the specified directory is not empty + // returns true if the path is an existing directory and + // it contains at least one file other than "." or "..". + bool isDirectoryNotEmpty(const tstring &dirPath); + + // returns directory part of the path. + // returns empty string if the path contains only filename. + // if the path ends with slash/backslash, + // returns removeTrailingSlashes(path). + tstring dirname(const tstring &path); + + // returns basename part of the path + // if the path ends with slash/backslash, returns empty string. + tstring basename(const tstring &path); + + /** + * Translates forward slashes to back slashes and returns lower case version + * of the given string. + */ + tstring normalizePath(tstring v); + + // Returns suffix of the path. If the given path has a suffix the first + // character of the return value is '.'. + // Otherwise return value if empty string. + tstring suffix(const tstring &path); + + // combines two strings into a path + tstring combinePath(const tstring& parent, const tstring& child); + + // removes trailing slashes and backslashes in the path if any + tstring removeTrailingSlash(const tstring& path); + + // Creates a file with unique name in the specified base directory, + // throws an exception if operation fails + // path is constructed as . + // The function fails and throws exception if 'path' doesn't exist. + tstring createTempFile(const tstring &prefix = _T(""), + const tstring &suffix = _T(".tmp"), + const tstring &path=SysInfo::getTempDir()); + + // Creates a directory with unique name in the specified base directory, + // throws an exception if operation fails + // path is constructed as + // The function fails and throws exception if 'path' doesn't exist. + tstring createTempDirectory(const tstring &prefix = _T(""), + const tstring &suffix = _T(".tmp"), + const tstring &basedir=SysInfo::getTempDir()); + + // If the file referenced with "prototype" parameter DOES NOT exist, + // the return value is the given path. No new files created. + // Otherwise the function creates another file in the same directory as + // the given file with the same suffix and with the basename from the + // basename of the given file with some random chars appended to ensure + // created file is unique. + tstring createUniqueFile(const tstring &prototype); + + // Creates directory and subdirectories if don't exist. + // Currently supports only "standard" path like "c:\bla-bla" + // If 'createdDirs' parameter is not NULL, the given array is appended with + // all subdirectories created by this function call. + void createDirectory(const tstring &path, tstring_array* createdDirs=0); + + // copies file from fromPath to toPath. + // Creates output directory if doesn't exist. + void copyFile(const tstring& fromPath, const tstring& toPath, + bool failIfExists); + + // moves file from fromPath to toPath. + // Creates output directory if doesn't exist. + void moveFile(const tstring& fromPath, const tstring& toPath, + bool failIfExists); + + // Throws exception if fails to delete specified 'path'. + // Exits normally if 'path' doesn't exist or it has been deleted. + // Attempts to strip R/O attribute if delete fails and retry delete. + void deleteFile(const tstring &path); + // Returns 'false' if fails to delete specified 'path'. + // Returns 'true' if 'path' doesn't exist or it has been deleted. + // Attempts to strip R/O attribute if delete fails and retry delete. + bool deleteFile(const tstring &path, const std::nothrow_t &) throw(); + + // Like deleteFile(), but applies to directories. + void deleteDirectory(const tstring &path); + bool deleteDirectory(const tstring &path, const std::nothrow_t &) throw(); + + // Deletes all files (not subdirectories) from the specified directory. + // Exits normally if all files in 'dirPath' have been deleted or if + // 'dirPath' doesn't exist. + // Throws exception if 'dirPath' references existing file system object + // which is not a directory or when the first failure of file delete + // occurs. + void deleteFilesInDirectory(const tstring &dirPath); + // Deletes all files (not subdirectories) from the specified directory. + // Returns 'true' normally if all files in 'dirPath' have been deleted or + // if 'dirPath' doesn't exist. + // Returns 'false' if 'dirPath' references existing file system object + // which is not a directory or if failed to delete one ore more files in + // 'dirPath' directory. + // Doesn't abort iteration over files if the given directory after the + // first failure to delete a file. + bool deleteFilesInDirectory(const tstring &dirPath, + const std::nothrow_t &) throw(); + // Like deleteFilesInDirectory, but deletes subdirectories as well + void deleteDirectoryRecursive(const tstring &dirPath); + bool deleteDirectoryRecursive(const tstring &dirPath, + const std::nothrow_t &) throw(); + + class DirectoryCallback { + public: + virtual ~DirectoryCallback() {}; + + virtual bool onFile(const tstring& path) { + return true; + } + virtual bool onDirectory(const tstring& path) { + return true; + } + }; + + // Calls the given callback for every file and subdirectory of + // the given directory. + void iterateDirectory(const tstring &dirPath, DirectoryCallback& callback); + + /** + * Replace file suffix, example replaceSuffix("file/path.txt", ".csv") + * @param path file path to replace suffix + * @param suffix new suffix for path + * @return return file path with new suffix + */ + tstring replaceSuffix(const tstring& path, const tstring& suffix=tstring()); + + class DirectoryIterator: DirectoryCallback { + public: + DirectoryIterator(const tstring& root=tstring()): root(root) { + recurse().withFiles().withFolders(); + } + + DirectoryIterator& recurse(bool v=true) { + theRecurse = v; + return *this; + } + + DirectoryIterator& withFiles(bool v=true) { + theWithFiles = v; + return *this; + } + + DirectoryIterator& withFolders(bool v=true) { + theWithFolders = v; + return *this; + } + + tstring_array findItems() { + tstring_array reply; + findItems(reply); + return reply; + } + + DirectoryIterator& findItems(tstring_array& v); + + private: + virtual bool onFile(const tstring& path); + virtual bool onDirectory(const tstring& path); + + private: + bool theRecurse; + bool theWithFiles; + bool theWithFolders; + tstring root; + tstring_array items; + }; + + // Returns array of all the files/sub-folders from the given directory, + // empty array if basedir is not a directory. The returned + // array is ordered from top down (i.e. dirs are listed first followed + // by subfolders and files). + // Order of subfolders and files is undefined + // but usually they are sorted by names. + inline tstring_array listAllContents(const tstring& basedir) { + return DirectoryIterator(basedir).findItems(); + } + + // Helper to construct path from multiple components. + // + // Sample usage: + // Construct "c:\Program Files\Java" string from three components + // + // tstring path = FileUtils::mkpath() << _T("c:") + // << _T("Program Files") + // << _T("Java"); + // + class mkpath { + public: + operator const tstring& () const { + return path; + } + + mkpath& operator << (const tstring& p) { + path = combinePath(path, p); + return *this; + } + + // mimic std::string + const tstring::value_type* c_str() const { + return path.c_str(); + } + private: + tstring path; + }; + + struct Directory { + Directory() { + } + + Directory(const tstring &parent, + const tstring &subdir) : parent(parent), subdir(subdir) { + } + + operator tstring () const { + return getPath(); + } + + tstring getPath() const { + return combinePath(parent, subdir); + } + + bool empty() const { + return (parent.empty() && subdir.empty()); + } + + tstring parent; + tstring subdir; + }; + + // Deletes list of files and directories in batch mode. + // Registered files and directories are deleted when destructor is called. + // Order or delete operations is following: + // - delete items registered with appendFile() calls; + // - delete items registered with appendAllFilesInDirectory() calls; + // - delete items registered with appendRecursiveDirectory() calls; + // - delete items registered with appendEmptyDirectory() calls. + class Deleter { + public: + Deleter() { + } + + ~Deleter() { + execute(); + } + + typedef std::pair Path; + typedef std::vector Paths; + + /** + * Appends all records from the given deleter Deleter into this Deleter + * instance. On success array with records in the passed in Deleter + * instance is emptied. + */ + Deleter& appendFrom(Deleter& other) { + Paths tmp(paths); + tmp.insert(tmp.end(), other.paths.begin(), other.paths.end()); + Paths empty; + other.paths.swap(empty); + paths.swap(tmp); + return *this; + } + + // Schedule file for deletion. + Deleter& appendFile(const tstring& path); + + // Schedule files for deletion. + template + Deleter& appendFiles(It b, It e) { + for (It it = b; it != e; ++it) { + appendFile(*it); + } + return *this; + } + + // Schedule files for deletion in the given directory. + template + Deleter& appendFiles(const tstring& dirname, It b, It e) { + for (It it = b; it != e; ++it) { + appendFile(FileUtils::mkpath() << dirname << *it); + } + return *this; + } + + // Schedule empty directory for deletion with empty roots + // (up to Directory.parent). + Deleter& appendEmptyDirectory(const Directory& dir); + + // Schedule empty directory for deletion without roots. + // This is a particular case of + // appendEmptyDirectory(const Directory& dir) + // with Directory(dirname(path), basename(path)). + Deleter& appendEmptyDirectory(const tstring& path); + + // Schedule all file from the given directory for deletion. + Deleter& appendAllFilesInDirectory(const tstring& path); + + // Schedule directory for recursive deletion. + Deleter& appendRecursiveDirectory(const tstring& path); + + void cancel() { + paths.clear(); + } + + // Deletes scheduled files and directories. After this function + // is called internal list of scheduled items is emptied. + void execute(); + + private: + Paths paths; + }; + + + /** + * Helper to write chunks of data into binary file. + * Creates temporary file in the same folder with destination file. + * All subsequent requests to save data chunks are redirected to temporary + * file. finalize() method closes temporary file stream and renames + * temporary file. + * If finalize() method is not called, temporary file is deleted in + * ~FileWriter(), destination file is not touched. + */ + class FileWriter { + public: + explicit FileWriter(const tstring& path); + + FileWriter& write(const void* buf, size_t bytes); + + template + FileWriter& write(const Ctnr& buf) { + return write(buf.data(), + buf.size() * sizeof(typename Ctnr::value_type)); + } + + void finalize(); + + private: + // Not accessible by design! + FileWriter& write(const std::wstring& str); + + private: + tstring tmpFile; + Deleter cleaner; + std::ofstream tmp; + tstring dstPath; + }; +} // FileUtils + +#endif // FILEUTILS_H --- old/src/jdk.jpackage/windows/native/libjpackage/IconSwap.cpp 2019-11-18 21:17:36.243655600 -0500 +++ /dev/null 2019-11-18 21:17:37.000000000 -0500 @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include -#include -#include -#include -#include - -using namespace std; - -// http://msdn.microsoft.com/en-us/library/ms997538.aspx - -typedef struct _ICONDIRENTRY { - BYTE bWidth; - BYTE bHeight; - BYTE bColorCount; - BYTE bReserved; - WORD wPlanes; - WORD wBitCount; - DWORD dwBytesInRes; - DWORD dwImageOffset; -} ICONDIRENTRY, * LPICONDIRENTRY; - -typedef struct _ICONDIR { - WORD idReserved; - WORD idType; - WORD idCount; - ICONDIRENTRY idEntries[1]; -} ICONDIR, * LPICONDIR; - -// #pragmas are used here to insure that the structure's -// packing in memory matches the packing of the EXE or DLL. -#pragma pack(push) -#pragma pack(2) - -typedef struct _GRPICONDIRENTRY { - BYTE bWidth; - BYTE bHeight; - BYTE bColorCount; - BYTE bReserved; - WORD wPlanes; - WORD wBitCount; - DWORD dwBytesInRes; - WORD nID; -} GRPICONDIRENTRY, * LPGRPICONDIRENTRY; -#pragma pack(pop) - -#pragma pack(push) -#pragma pack(2) - -typedef struct _GRPICONDIR { - WORD idReserved; - WORD idType; - WORD idCount; - GRPICONDIRENTRY idEntries[1]; -} GRPICONDIR, * LPGRPICONDIR; -#pragma pack(pop) - -void PrintError() { - LPVOID message = NULL; - DWORD error = GetLastError(); - - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) & message, 0, NULL) != 0) { - printf("%S", (LPTSTR) message); - LocalFree(message); - } -} - -// Note: We do not check here that iconTarget is valid icon. -// Java code will already do this for us. - -bool ChangeIcon(wstring iconTarget, wstring launcher) { - WORD language = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT); - - HANDLE icon = CreateFile(iconTarget.c_str(), GENERIC_READ, 0, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (icon == INVALID_HANDLE_VALUE) { - PrintError(); - return false; - } - - // Reading .ICO file - WORD idReserved, idType, idCount; - - DWORD dwBytesRead; - ReadFile(icon, &idReserved, sizeof (WORD), &dwBytesRead, NULL); - ReadFile(icon, &idType, sizeof (WORD), &dwBytesRead, NULL); - ReadFile(icon, &idCount, sizeof (WORD), &dwBytesRead, NULL); - - LPICONDIR lpid = (LPICONDIR) malloc( - sizeof (ICONDIR) + (sizeof (ICONDIRENTRY) * (idCount - 1))); - if (lpid == NULL) { - CloseHandle(icon); - printf("Error: Failed to allocate memory\n"); - return false; - } - - lpid->idReserved = idReserved; - lpid->idType = idType; - lpid->idCount = idCount; - - ReadFile(icon, &lpid->idEntries[0], sizeof (ICONDIRENTRY) * lpid->idCount, - &dwBytesRead, NULL); - - LPGRPICONDIR lpgid = (LPGRPICONDIR) malloc( - sizeof (GRPICONDIR) + (sizeof (GRPICONDIRENTRY) * (idCount - 1))); - if (lpid == NULL) { - CloseHandle(icon); - free(lpid); - printf("Error: Failed to allocate memory\n"); - return false; - } - - lpgid->idReserved = idReserved; - lpgid->idType = idType; - lpgid->idCount = idCount; - - for (int i = 0; i < lpgid->idCount; i++) { - lpgid->idEntries[i].bWidth = lpid->idEntries[i].bWidth; - lpgid->idEntries[i].bHeight = lpid->idEntries[i].bHeight; - lpgid->idEntries[i].bColorCount = lpid->idEntries[i].bColorCount; - lpgid->idEntries[i].bReserved = lpid->idEntries[i].bReserved; - lpgid->idEntries[i].wPlanes = lpid->idEntries[i].wPlanes; - lpgid->idEntries[i].wBitCount = lpid->idEntries[i].wBitCount; - lpgid->idEntries[i].dwBytesInRes = lpid->idEntries[i].dwBytesInRes; - lpgid->idEntries[i].nID = i + 1; - } - - // Store images in .EXE - HANDLE update = BeginUpdateResource(launcher.c_str(), FALSE); - if (update == NULL) { - free(lpid); - free(lpgid); - CloseHandle(icon); - PrintError(); - return false; - } - - for (int i = 0; i < lpid->idCount; i++) { - LPBYTE lpBuffer = (LPBYTE) malloc(lpid->idEntries[i].dwBytesInRes); - SetFilePointer(icon, lpid->idEntries[i].dwImageOffset, - NULL, FILE_BEGIN); - ReadFile(icon, lpBuffer, lpid->idEntries[i].dwBytesInRes, - &dwBytesRead, NULL); - if (!UpdateResource(update, RT_ICON, - MAKEINTRESOURCE(lpgid->idEntries[i].nID), - language, &lpBuffer[0], lpid->idEntries[i].dwBytesInRes)) { - free(lpBuffer); - free(lpid); - free(lpgid); - CloseHandle(icon); - PrintError(); - return false; - } - free(lpBuffer); - } - - free(lpid); - CloseHandle(icon); - - if (!UpdateResource(update, RT_GROUP_ICON, - MAKEINTRESOURCE(1), language, &lpgid[0], - (sizeof (WORD) * 3) + (sizeof (GRPICONDIRENTRY) * lpgid->idCount))) { - free(lpgid); - PrintError(); - return false; - } - - free(lpgid); - - if (EndUpdateResource(update, FALSE) == FALSE) { - PrintError(); - return false; - } - - return true; -} --- /dev/null 2019-11-18 21:17:37.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/IconSwap.cpp 2019-11-18 21:17:32.947699200 -0500 @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include +#include + +using namespace std; + +// http://msdn.microsoft.com/en-us/library/ms997538.aspx + +typedef struct _ICONDIRENTRY { + BYTE bWidth; + BYTE bHeight; + BYTE bColorCount; + BYTE bReserved; + WORD wPlanes; + WORD wBitCount; + DWORD dwBytesInRes; + DWORD dwImageOffset; +} ICONDIRENTRY, * LPICONDIRENTRY; + +typedef struct _ICONDIR { + WORD idReserved; + WORD idType; + WORD idCount; + ICONDIRENTRY idEntries[1]; +} ICONDIR, * LPICONDIR; + +// #pragmas are used here to insure that the structure's +// packing in memory matches the packing of the EXE or DLL. +#pragma pack(push) +#pragma pack(2) + +typedef struct _GRPICONDIRENTRY { + BYTE bWidth; + BYTE bHeight; + BYTE bColorCount; + BYTE bReserved; + WORD wPlanes; + WORD wBitCount; + DWORD dwBytesInRes; + WORD nID; +} GRPICONDIRENTRY, * LPGRPICONDIRENTRY; +#pragma pack(pop) + +#pragma pack(push) +#pragma pack(2) + +typedef struct _GRPICONDIR { + WORD idReserved; + WORD idType; + WORD idCount; + GRPICONDIRENTRY idEntries[1]; +} GRPICONDIR, * LPGRPICONDIR; +#pragma pack(pop) + +void PrintError() { + LPVOID message = NULL; + DWORD error = GetLastError(); + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) & message, 0, NULL) != 0) { + printf("%S", (LPTSTR) message); + LocalFree(message); + } +} + +// Note: We do not check here that iconTarget is valid icon. +// Java code will already do this for us. + +bool ChangeIcon(wstring iconTarget, wstring launcher) { + WORD language = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT); + + HANDLE icon = CreateFile(iconTarget.c_str(), GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (icon == INVALID_HANDLE_VALUE) { + PrintError(); + return false; + } + + // Reading .ICO file + WORD idReserved, idType, idCount; + + DWORD dwBytesRead; + ReadFile(icon, &idReserved, sizeof (WORD), &dwBytesRead, NULL); + ReadFile(icon, &idType, sizeof (WORD), &dwBytesRead, NULL); + ReadFile(icon, &idCount, sizeof (WORD), &dwBytesRead, NULL); + + LPICONDIR lpid = (LPICONDIR) malloc( + sizeof (ICONDIR) + (sizeof (ICONDIRENTRY) * (idCount - 1))); + if (lpid == NULL) { + CloseHandle(icon); + printf("Error: Failed to allocate memory\n"); + return false; + } + + lpid->idReserved = idReserved; + lpid->idType = idType; + lpid->idCount = idCount; + + ReadFile(icon, &lpid->idEntries[0], sizeof (ICONDIRENTRY) * lpid->idCount, + &dwBytesRead, NULL); + + LPGRPICONDIR lpgid = (LPGRPICONDIR) malloc( + sizeof (GRPICONDIR) + (sizeof (GRPICONDIRENTRY) * (idCount - 1))); + if (lpid == NULL) { + CloseHandle(icon); + free(lpid); + printf("Error: Failed to allocate memory\n"); + return false; + } + + lpgid->idReserved = idReserved; + lpgid->idType = idType; + lpgid->idCount = idCount; + + for (int i = 0; i < lpgid->idCount; i++) { + lpgid->idEntries[i].bWidth = lpid->idEntries[i].bWidth; + lpgid->idEntries[i].bHeight = lpid->idEntries[i].bHeight; + lpgid->idEntries[i].bColorCount = lpid->idEntries[i].bColorCount; + lpgid->idEntries[i].bReserved = lpid->idEntries[i].bReserved; + lpgid->idEntries[i].wPlanes = lpid->idEntries[i].wPlanes; + lpgid->idEntries[i].wBitCount = lpid->idEntries[i].wBitCount; + lpgid->idEntries[i].dwBytesInRes = lpid->idEntries[i].dwBytesInRes; + lpgid->idEntries[i].nID = i + 1; + } + + // Store images in .EXE + HANDLE update = BeginUpdateResource(launcher.c_str(), FALSE); + if (update == NULL) { + free(lpid); + free(lpgid); + CloseHandle(icon); + PrintError(); + return false; + } + + for (int i = 0; i < lpid->idCount; i++) { + LPBYTE lpBuffer = (LPBYTE) malloc(lpid->idEntries[i].dwBytesInRes); + SetFilePointer(icon, lpid->idEntries[i].dwImageOffset, + NULL, FILE_BEGIN); + ReadFile(icon, lpBuffer, lpid->idEntries[i].dwBytesInRes, + &dwBytesRead, NULL); + if (!UpdateResource(update, RT_ICON, + MAKEINTRESOURCE(lpgid->idEntries[i].nID), + language, &lpBuffer[0], lpid->idEntries[i].dwBytesInRes)) { + free(lpBuffer); + free(lpid); + free(lpgid); + CloseHandle(icon); + PrintError(); + return false; + } + free(lpBuffer); + } + + free(lpid); + CloseHandle(icon); + + if (!UpdateResource(update, RT_GROUP_ICON, MAKEINTRESOURCE(1), + language, &lpgid[0], (sizeof (WORD) * 3) + + (sizeof (GRPICONDIRENTRY) * lpgid->idCount))) { + free(lpgid); + PrintError(); + return false; + } + + free(lpgid); + + if (EndUpdateResource(update, FALSE) == FALSE) { + PrintError(); + return false; + } + + return true; +} --- old/src/jdk.jpackage/windows/native/libjpackage/IconSwap.h 2019-11-18 21:17:56.999001600 -0500 +++ /dev/null 2019-11-18 21:17:58.000000000 -0500 @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef ICONSWAP_H -#define ICONSWAP_H - -#include - -using namespace std; - -bool ChangeIcon(wstring iconTarget, wstring launcher); - -#endif // ICONSWAP_H \ No newline at end of file --- /dev/null 2019-11-18 21:17:58.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/IconSwap.h 2019-11-18 21:17:53.572025400 -0500 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ICONSWAP_H +#define ICONSWAP_H + +#include + +using namespace std; + +bool ChangeIcon(wstring iconTarget, wstring launcher); + +#endif // ICONSWAP_H + --- /dev/null 2019-11-18 21:18:17.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/Log.cpp 2019-11-18 21:18:14.039982300 -0500 @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Log.h" +#include "SysInfo.h" +#include "FileUtils.h" + + +namespace { + // + // IMPORTANT: Static objects with non-trivial constructors are NOT allowed + // in logger module. Allocate buffers only and do lazy initialization of + // globals in Logger::getDefault(). + // + // Logging subsystem is used almost in every module, and logging API can be + // called from constructors of static objects in various modules. As + // ordering of static objects initialization between modules is undefined, + // this means some module may call logging api before logging static + // variables are initialized if any. This will result in AV. To avoid such + // use cases keep logging module free from static variables that require + // initialization with functions called by CRT. + // + + // by default log everything + const Logger::LogLevel defaultLogLevel = Logger::LOG_TRACE; + + char defaultLogAppenderMemory[sizeof(StderrLogAppender)] = {}; + + char defaultLoggerMemory[sizeof(Logger)] = {}; + + NopLogAppender nopLogApender; + + LPCTSTR getLogLevelStr(Logger::LogLevel level) { + switch (level) { + case Logger::LOG_TRACE: + return _T("TRACE"); + case Logger::LOG_INFO: + return _T("INFO"); + case Logger::LOG_WARNING: + return _T("WARNING"); + case Logger::LOG_ERROR: + return _T("ERROR"); + } + return _T("UNKNOWN"); + } + + tstring retrieveModuleName() { + try { + return FileUtils::basename(SysInfo::getCurrentModulePath()); + } catch (const std::exception&) { + return _T("Unknown"); + } + } + + TCHAR moduleName[MAX_PATH] = { 'U', 'n', 'k', 'o', 'w', 'n', TCHAR(0) }; + + const LPCTSTR format = _T("[%04u/%02u/%02u %02u:%02u:%02u.%03u, %s (PID: %u, TID: %u), %s:%u (%s)]\n\t%s: %s\n"); + + enum State { NotInitialized, Initializing, Initialized }; + State state = NotInitialized; +} + + +LogEvent::LogEvent() { + memset(this, 0, sizeof(*this)); + moduleName = tstring(); + logLevel = tstring(); + fileName = tstring(); + funcName = tstring(); + message = tstring(); +} + + +StderrLogAppender::StderrLogAppender() { +} + + +/*static*/ +Logger& Logger::defaultLogger() { + Logger* reply = reinterpret_cast(defaultLoggerMemory); + + if (!reply->appender) { + // Memory leak by design. Not an issue at all as this is global + // object. OS will do resources clean up anyways when application + // terminates and the default log appender should live as long as + // application lives. + reply->appender = new (defaultLogAppenderMemory) StderrLogAppender(); + } + + if (Initializing == state) { + // Recursive call to Logger::defaultLogger. + moduleName[0] = TCHAR(0); + } else if (NotInitialized == state) { + state = Initializing; + + tstring mname = retrieveModuleName(); + mname.resize(_countof(moduleName) - 1); + std::memcpy(moduleName, mname.c_str(), mname.size()); + moduleName[mname.size()] = TCHAR(0); + + // if JPACKAGE_DEBUG environment variable is NOT set to "true" disable + // logging. + if (SysInfo::getEnvVariable(std::nothrow, + L"JPACKAGE_DEBUG") != L"true") { + reply->appender = &nopLogApender; + } + + state = Initialized; + } + + return *reply; +} + +Logger::Logger(LogAppender& appender, LogLevel logLevel) + : level(logLevel), appender(&appender) { +} + +void Logger::setLogLevel(LogLevel logLevel) { + level = logLevel; +} + +Logger::~Logger() { +} + + +bool Logger::isLoggable(LogLevel logLevel) const { + return logLevel >= level; +} + +void Logger::log(LogLevel logLevel, LPCTSTR fileName, int lineNum, + LPCTSTR funcName, const tstring& message) const { + LogEvent logEvent; + + // [YYYY/MM/DD HH:MM:SS.ms, (PID: processID, TID: threadID), + // fileName:lineNum (funcName)] LEVEL: message + GetLocalTime(&logEvent.ts); + + logEvent.pid = GetCurrentProcessId(); + logEvent.tid = GetCurrentThreadId(); + logEvent.moduleName = moduleName; + logEvent.fileName = FileUtils::basename(fileName); + logEvent.funcName = funcName; + logEvent.logLevel = getLogLevelStr(logLevel); + logEvent.lineNum = lineNum; + logEvent.message = message; + + appender->append(logEvent); +} + + +void StderrLogAppender::append(const LogEvent& v) +{ + const tstring out = tstrings::unsafe_format(format, + unsigned(v.ts.wYear), unsigned(v.ts.wMonth), unsigned(v.ts.wDay), + unsigned(v.ts.wHour), unsigned(v.ts.wMinute), unsigned(v.ts.wSecond), + unsigned(v.ts.wMilliseconds), + v.moduleName.c_str(), v.pid, v.tid, + v.fileName.c_str(), v.lineNum, v.funcName.c_str(), + v.logLevel.c_str(), + v.message.c_str()); + + std::cerr << tstrings::toUtf8(out); +} + + +// Logger::ScopeTracer +Logger::ScopeTracer::ScopeTracer(Logger &logger, LogLevel logLevel, + LPCTSTR fileName, int lineNum, LPCTSTR funcName, + const tstring& scopeName) : log(logger), level(logLevel), + file(fileName), line(lineNum), + func(funcName), scope(scopeName), needLog(logger.isLoggable(logLevel)) { + if (needLog) { + log.log(level, file.c_str(), line, func.c_str(), + tstrings::any() << "Entering " << scope); + } +} + +Logger::ScopeTracer::~ScopeTracer() { + if (needLog) { + // we don't know what line is end of scope at, so specify line 0 + // and add note about line when the scope begins + log.log(level, file.c_str(), 0, func.c_str(), + tstrings::any() << "Exiting " << scope << " (entered at " + << FileUtils::basename(file) << ":" << line << ")"); + } +} --- /dev/null 2019-11-18 21:18:27.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/Log.h 2019-11-18 21:18:24.548730800 -0500 @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef __LOG_H_INCLUDED_ +#define __LOG_H_INCLUDED_ + +#include +#include "tstrings.h" + + +/* Default logger (Logger::defaultLogger()) writes log messages to + * the default log file. + * Common scenario: + * - main() function configures default logger: + * FileLogAppender appender(_T("my_log_filename.log")); + * Logger::defaultLogger().setAppender(appender); + * Logger::defaultLogger().setLogLevel(LOG_INFO); + * If the default file name and log level are not set, + * _T("jusched.log")/LOG_TRACE are used. + * + * Logger fileName specifies only file name, + * full path for the log file depends on the platform + * (usually value of the TMP env. var) + */ + +struct LogEvent { + SYSTEMTIME ts; + long tid; + long pid; + tstring moduleName; + tstring logLevel; + tstring fileName; + int lineNum; + tstring funcName; + tstring message; + + LogEvent(); +}; + + +class LogAppender { +public: + virtual ~LogAppender() { + } + virtual void append(const LogEvent& v) = 0; +}; + + +class NopLogAppender: public LogAppender { +public: + virtual void append(const LogEvent& v) {}; +}; + + +class TeeLogAppender: public LogAppender { +public: + TeeLogAppender(LogAppender* first, LogAppender* second): + first(first), second(second) { + } + virtual ~TeeLogAppender() { + } + virtual void append(const LogEvent& v) { + if (first) { + first->append(v); + } + if (second) { + second->append(v); + } + } +private: + LogAppender* first; + LogAppender* second; +}; + + +/** + * Writes log events to stderr. + */ +class StderrLogAppender: public LogAppender { +public: + explicit StderrLogAppender(); + + virtual void append(const LogEvent& v); +}; + + +class Logger { +public: + enum LogLevel { + LOG_TRACE, + LOG_INFO, + LOG_WARNING, + LOG_ERROR + }; + + static Logger& defaultLogger(); + + explicit Logger(LogAppender& appender, LogLevel logLevel = LOG_TRACE); + ~Logger(); + + LogAppender& setAppender(LogAppender& v) { + LogAppender& oldAppender = *appender; + appender = &v; + return oldAppender; + } + + LogAppender& getAppender() const { + return *appender; + } + + void setLogLevel(LogLevel logLevel); + + bool isLoggable(LogLevel logLevel) const ; + void log(LogLevel logLevel, LPCTSTR fileName, int lineNum, + LPCTSTR funcName, const tstring& message) const; + void log(LogLevel logLevel, LPCTSTR fileName, int lineNum, + LPCTSTR funcName, const tstrings::any& message) const { + return log(logLevel, fileName, lineNum, funcName, message.tstr()); + } + void log(LogLevel logLevel, LPCTSTR fileName, int lineNum, + LPCTSTR funcName, tstring::const_pointer message) const { + return log(logLevel, fileName, lineNum, funcName, tstring(message)); + } + + // internal class for scope tracing + class ScopeTracer { + public: + ScopeTracer(Logger &logger, LogLevel logLevel, LPCTSTR fileName, + int lineNum, LPCTSTR funcName, const tstring& scopeName); + ~ScopeTracer(); + + private: + const Logger &log; + const LogLevel level; + const bool needLog; + const tstring file; + const int line; + const tstring func; + const tstring scope; + }; + +private: + LogLevel level; + LogAppender* appender; +}; + + +// base logging macro +#define LOGGER_LOG(logger, logLevel, message) \ + do { \ + if (logger.isLoggable(logLevel)) { \ + logger.log(logLevel, _T(__FILE__), __LINE__, _T(__FUNCTION__), message); \ + } \ + } while(false) + + +// custom logger macros +#define LOGGER_TRACE(logger, message) LOGGER_LOG(logger, Logger::LOG_TRACE, message) +#define LOGGER_INFO(logger, message) LOGGER_LOG(logger, Logger::LOG_INFO, message) +#define LOGGER_WARNING(logger, message) LOGGER_LOG(logger, Logger::LOG_WARNING, message) +#define LOGGER_ERROR(logger, message) LOGGER_LOG(logger, Logger::LOG_ERROR, message) +// scope tracing macros +#define LOGGER_TRACE_SCOPE(logger, scopeName) \ + Logger::ScopeTracer tracer__COUNTER__(logger, Logger::LOG_TRACE, _T(__FILE__), __LINE__, _T(__FUNCTION__), scopeName) +#define LOGGER_TRACE_FUNCTION(logger) LOGGER_TRACE_SCOPE(logger, _T(__FUNCTION__)) + + +// default logger macros +#define LOG_TRACE(message) LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_TRACE, message) +#define LOG_INFO(message) LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_INFO, message) +#define LOG_WARNING(message) LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_WARNING, message) +#define LOG_ERROR(message) LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_ERROR, message) +// scope tracing macros +// logs (_T("Entering ") + scopeName) at the beging, (_T("Exiting ") + scopeName) at the end of scope +#define LOG_TRACE_SCOPE(scopeName) LOGGER_TRACE_SCOPE(Logger::defaultLogger(), scopeName) +// logs (_T("Entering ") + functionName) at the beging, (_T("Exiting ") + __FUNCTION__) at the end of scope +#define LOG_TRACE_FUNCTION() LOGGER_TRACE_FUNCTION(Logger::defaultLogger()) + + +#endif // __LOG_H_INCLUDED_ --- /dev/null 2019-11-18 21:18:38.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/ResourceEditor.cpp 2019-11-18 21:18:35.086813200 -0500 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include "ResourceEditor.h" +#include "WinErrorHandling.h" +#include "Log.h" + + +ResourceEditor::FileLock::FileLock(const std::wstring& binaryPath) { + h = BeginUpdateResource(binaryPath.c_str(), FALSE); + if (NULL == h) { + JP_THROW(SysError(tstrings::any() << "BeginUpdateResource(" + << binaryPath << ") failed", BeginUpdateResource)); + } + + discard(false); +} + + +ResourceEditor::FileLock::~FileLock() { + if (!EndUpdateResource(h, theDiscard)) { + JP_NO_THROW(JP_THROW(SysError(tstrings::any() + << "EndUpdateResource(" << h << ") failed.", EndUpdateResource))); + } +} + + +ResourceEditor::ResourceEditor() { + language(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)).type(unsigned(0)).id(unsigned(0)); +} + + +ResourceEditor& ResourceEditor::type(unsigned v) { + return type(MAKEINTRESOURCE(v)); +} + + +ResourceEditor& ResourceEditor::type(LPCWSTR v) { + if (IS_INTRESOURCE(v)) { + std::wostringstream printer; + printer << L"#" << reinterpret_cast(v); + theType = printer.str(); + theTypePtr = MAKEINTRESOURCE(static_cast(reinterpret_cast(v))); + } else { + theType = v; + theTypePtr = theType.c_str(); + } + return *this; +} + + +ResourceEditor& ResourceEditor::id(unsigned v) { + return id(MAKEINTRESOURCE(v)); +} + + +ResourceEditor& ResourceEditor::id(LPCWSTR v) { + if (IS_INTRESOURCE(v)) { + std::wostringstream printer; + printer << L"#" << reinterpret_cast(v); + theId = printer.str(); + } else { + theId = v; + theIdPtr = theId.c_str(); + } + return *this; +} + + +ResourceEditor& ResourceEditor::apply(const FileLock& dstBinary, + std::istream& srcStream, std::streamsize size) { + + typedef std::vector ByteArray; + ByteArray buf; + if (size <= 0) { + // Read the entire stream. + buf = ByteArray((std::istreambuf_iterator(srcStream)), + std::istreambuf_iterator()); + } else { + buf.resize(size_t(size)); + srcStream.read(reinterpret_cast(buf.data()), size); + } + + auto reply = UpdateResource(dstBinary.get(), theTypePtr, theIdPtr, lang, + buf.data(), static_cast(buf.size())); + if (reply == FALSE) { + JP_THROW(SysError("UpdateResource() failed", UpdateResource)); + } + + return *this; +} + + +ResourceEditor& ResourceEditor::apply(const FileLock& dstBinary, + const std::wstring& srcFile) { + std::ifstream input(srcFile, std::ios_base::binary); + input.exceptions(std::ios::failbit | std::ios::badbit); + return apply(dstBinary, input); +} --- /dev/null 2019-11-18 21:18:48.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/ResourceEditor.h 2019-11-18 21:18:45.160296300 -0500 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef RESOURCEEDITOR_H +#define RESOURCEEDITOR_H + +#include +#include +#include + + +class ResourceEditor { +public: + class FileLock { + public: + FileLock(const std::wstring& binaryPath); + ~FileLock(); + + HANDLE get() const { + return h; + } + + void discard(bool v = true) { + theDiscard = v; + } + + private: + FileLock(const FileLock&); + FileLock& operator=(const FileLock&); + private: + HANDLE h; + bool theDiscard; + }; + +public: + ResourceEditor(); + + /** + * Set the language identifier of the resource to be updated. + */ + ResourceEditor& language(unsigned v) { + lang = v; + return *this; + } + + /** + * Set the resource type to be updated. + */ + ResourceEditor& type(unsigned v); + + /** + * Set the resource type to be updated. + */ + ResourceEditor& type(LPCWSTR v); + + /** + * Set resource ID. + */ + ResourceEditor& id(unsigned v); + + /** + * Set resource ID. + */ + ResourceEditor& id(LPCWSTR v); + + /** + * Relaces resource configured in the given binary with the given data stream. + */ + ResourceEditor& apply(const FileLock& dstBinary, std::istream& srcStream, std::streamsize size=0); + + /** + * Relaces resource configured in the given binary with contents of + * the given binary file. + */ + ResourceEditor& apply(const FileLock& dstBinary, const std::wstring& srcFile); + +private: + unsigned lang; + std::wstring theId; + LPCWSTR theIdPtr; + std::wstring theType; + LPCWSTR theTypePtr; +}; + +#endif // #ifndef RESOURCEEDITOR_H --- old/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-11-18 21:18:59.042837400 -0500 +++ /dev/null 2019-11-18 21:19:00.000000000 -0500 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include - -extern "C" { - - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved) { - return true; - } -} \ No newline at end of file --- /dev/null 2019-11-18 21:19:00.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/SourceCodePos.h 2019-11-18 21:18:55.721646200 -0500 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +#ifndef SourceCodePos_h +#define SourceCodePos_h + + +// +// Position in source code. +// + +struct SourceCodePos +{ + SourceCodePos(const char* fl, const char* fnc, int l): + file(fl), func(fnc), lno(l) + { + } + + const char* file; + const char* func; + int lno; +}; + + +// Initializes SourceCodePos instance with the +// information from the point of calling. +#define JP_SOURCE_CODE_POS SourceCodePos(__FILE__, __FUNCTION__, __LINE__) + + +#endif // #ifndef SourceCodePos_h --- /dev/null 2019-11-18 21:19:19.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/SysInfo.h 2019-11-18 21:19:16.332838600 -0500 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +#ifndef SYSINFO_H +#define SYSINFO_H + +#include "tstrings.h" + + +// +// This namespace provides information about environment in which +// the current application runs. +// It is for general purpose use. +// Functions in this namespaces are just queries about the environment. +// Functions that change the existing environment like file or directory +// creation should not be added to this namespace. +// +namespace SysInfo { + /** + * Returns temp dir (for the current user). + */ + tstring getTempDir(); + + /** + * Returns absolute path to the process executable. + */ + tstring getProcessModulePath(); + + /** + * Returns absolute path to the current executable module. + */ + tstring getCurrentModulePath(); + + enum CommandArgProgramNameMode { + IncludeProgramName, + ExcludeProgramName + }; + /** + * Retrieves the command-line arguments for the current process. + * With IncludeProgramName option returns result similar to argv/argc. + * With ExcludeProgramName option program name + * (the 1st element of command line) + * is excluded. + */ + tstring_array getCommandArgs( + CommandArgProgramNameMode progNameMode = ExcludeProgramName); + + /** + * Returns value of environment variable with the given name. + * Throws exception if variable is not set or any other error occurred + * reading the value. + */ + tstring getEnvVariable(const tstring& name); + + /** + * Returns value of environment variable with the given name. + * Returns value of 'defValue' parameter if variable is not set or any + * other error occurred reading the value. + */ + tstring getEnvVariable(const std::nothrow_t&, const tstring& name, + const tstring& defValue=tstring()); + + /** + * Returns 'true' if environment variable with the given name is set. + */ + bool isEnvVariableSet(const tstring& name); +} + +#endif // SYSINFO_H --- old/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-11-18 21:19:30.262850000 -0500 +++ /dev/null 2019-11-18 21:19:31.000000000 -0500 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include - -extern "C" { - - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved) { - return true; - } -} \ No newline at end of file --- /dev/null 2019-11-18 21:19:31.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/UniqueHandle.h 2019-11-18 21:19:26.854044700 -0500 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef UNIQUEHANDLE_H +#define UNIQUEHANDLE_H + +#include +#include + + +struct WndHandleDeleter { + typedef HANDLE pointer; + + void operator()(HANDLE h) { + ::CloseHandle(h); + } +}; + +typedef std::unique_ptr UniqueHandle; + +#endif // #ifndef UNIQUEHANDLE_H --- old/src/jdk.jpackage/windows/native/libjpackage/Utils.cpp 2019-11-18 21:19:50.451786500 -0500 +++ /dev/null 2019-11-18 21:19:51.000000000 -0500 @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "Windows.h" -#include "Utils.h" - -#define BUFFER_SIZE 4096 - -wstring GetStringFromJString(JNIEnv *pEnv, jstring jstr) { - const jchar *pJChars = pEnv->GetStringChars(jstr, NULL); - if (pJChars == NULL) { - return wstring(L""); - } - - wstring wstr(pJChars); - - pEnv->ReleaseStringChars(jstr, pJChars); - - return wstr; -} - -jstring GetJStringFromString(JNIEnv *pEnv, const jchar *unicodeChars, jsize len) { - return pEnv->NewString(unicodeChars, len); -} - -wstring GetLongPath(wstring path) { - wstring result(L""); - - size_t len = path.length(); - if (len > 1) { - if (path.at(len - 1) == '\\') { - path.erase(len - 1); - } - } - - TCHAR *pBuffer = new TCHAR[BUFFER_SIZE]; - if (pBuffer != NULL) { - DWORD dwResult = GetLongPathName(path.c_str(), pBuffer, BUFFER_SIZE); - if (dwResult > 0 && dwResult < BUFFER_SIZE) { - result = wstring(pBuffer); - } else { - delete [] pBuffer; - pBuffer = new TCHAR[dwResult]; - if (pBuffer != NULL) { - DWORD dwResult2 = GetLongPathName(path.c_str(), pBuffer, dwResult); - if (dwResult2 == (dwResult - 1)) { - result = wstring(pBuffer); - } - } - } - - if (pBuffer != NULL) { - delete [] pBuffer; - } - } - - return result; -} --- /dev/null 2019-11-18 21:19:52.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/Utils.cpp 2019-11-18 21:19:47.091763900 -0500 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Windows.h" +#include "Utils.h" + +#define BUFFER_SIZE 4096 + +wstring GetStringFromJString(JNIEnv *pEnv, jstring jstr) { + const jchar *pJChars = pEnv->GetStringChars(jstr, NULL); + if (pJChars == NULL) { + return wstring(L""); + } + + wstring wstr(pJChars); + + pEnv->ReleaseStringChars(jstr, pJChars); + + return wstr; +} + +jstring GetJStringFromString(JNIEnv *pEnv, + const jchar *unicodeChars, jsize len) { + return pEnv->NewString(unicodeChars, len); +} + +wstring GetLongPath(wstring path) { + wstring result(L""); + + size_t len = path.length(); + if (len > 1) { + if (path.at(len - 1) == '\\') { + path.erase(len - 1); + } + } + + TCHAR *pBuffer = new TCHAR[BUFFER_SIZE]; + if (pBuffer != NULL) { + DWORD dwResult = GetLongPathName(path.c_str(), pBuffer, BUFFER_SIZE); + if (dwResult > 0 && dwResult < BUFFER_SIZE) { + result = wstring(pBuffer); + } else { + delete [] pBuffer; + pBuffer = new TCHAR[dwResult]; + if (pBuffer != NULL) { + DWORD dwResult2 = + GetLongPathName(path.c_str(), pBuffer, dwResult); + if (dwResult2 == (dwResult - 1)) { + result = wstring(pBuffer); + } + } + } + + if (pBuffer != NULL) { + delete [] pBuffer; + } + } + + return result; +} --- old/src/jdk.jpackage/windows/native/libjpackage/Utils.h 2019-11-18 21:20:10.903088500 -0500 +++ /dev/null 2019-11-18 21:20:12.000000000 -0500 @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef UTILS_H -#define UTILS_H - -#include -#include "jni.h" - -using namespace std; - -wstring GetStringFromJString(JNIEnv *pEnv, jstring jstr); -jstring GetJStringFromString(JNIEnv *pEnv, const jchar *unicodeChars, jsize len); - -wstring GetLongPath(wstring path); - -#endif // UTILS_H --- /dev/null 2019-11-18 21:20:12.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/Utils.h 2019-11-18 21:20:07.664195600 -0500 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef UTILS_H +#define UTILS_H + +#include +#include "jni.h" + +using namespace std; + +wstring GetStringFromJString(JNIEnv *pEnv, jstring jstr); +jstring GetJStringFromString(JNIEnv *pEnv, const jchar *unicodeChars, + jsize len); + +wstring GetLongPath(wstring path); + +#endif // UTILS_H --- old/src/jdk.jpackage/windows/native/libjpackage/VersionInfoSwap.cpp 2019-11-18 21:20:31.087556300 -0500 +++ /dev/null 2019-11-18 21:20:32.000000000 -0500 @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include "VersionInfoSwap.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -using namespace std; - -/* - * [Property file] contains key/value pairs - * The swap tool uses these pairs to create new version resource - * - * See MSDN docs for VS_VERSIONINFO structure that - * depicts organization of data in this version resource - * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx - * - * The swap tool makes changes in [Executable file] - * The tool assumes that the executable file has no version resource - * and it adds new resource in the executable file. - * If the executable file has an existing version resource, then - * the existing version resource will be replaced with new one. - */ - -VersionInfoSwap::VersionInfoSwap(wstring executableProperties, wstring launcher) { - m_executableProperties = executableProperties; - m_launcher = launcher; -} - -bool VersionInfoSwap::PatchExecutable() { - bool b = LoadFromPropertyFile(); - if (!b) { - return false; - } - - ByteBuffer buf; - b = CreateNewResource(&buf); - if (!b) { - return false; - } - - b = this->UpdateResource(buf.getPtr(), static_cast (buf.getPos())); - if (!b) { - return false; - } - - return true; -} - -bool VersionInfoSwap::LoadFromPropertyFile() { - wifstream stream(m_executableProperties.c_str()); - - const locale empty_locale = locale::empty(); - const locale utf8_locale = - locale(empty_locale, new codecvt_utf8()); - stream.imbue(utf8_locale); - - if (stream.is_open() == true) { - int lineNumber = 1; - while (stream.eof() == false) { - wstring line; - getline(stream, line); - - // # at the first character will comment out the line. - if (line.empty() == false && line[0] != '#') { - wstring::size_type pos = line.find('='); - if (pos != wstring::npos) { - wstring name = line.substr(0, pos); - wstring value = line.substr(pos + 1); - m_props[name] = value; - } - } - lineNumber++; - } - return true; - } - - return false; -} - -/* - * Creates new version resource - * - * MSND docs for VS_VERSION_INFO structure - * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx - */ -bool VersionInfoSwap::CreateNewResource(ByteBuffer *buf) { - size_t versionInfoStart = buf->getPos(); - buf->AppendWORD(0); - buf->AppendWORD(sizeof VS_FIXEDFILEINFO); - buf->AppendWORD(0); - buf->AppendString(TEXT("VS_VERSION_INFO")); - buf->Align(4); - - VS_FIXEDFILEINFO fxi; - if (!FillFixedFileInfo(&fxi)) { - return false; - } - buf->AppendBytes((BYTE*) & fxi, sizeof (VS_FIXEDFILEINFO)); - buf->Align(4); - - // String File Info - size_t stringFileInfoStart = buf->getPos(); - buf->AppendWORD(0); - buf->AppendWORD(0); - buf->AppendWORD(1); - buf->AppendString(TEXT("StringFileInfo")); - buf->Align(4); - - // String Table - size_t stringTableStart = buf->getPos(); - buf->AppendWORD(0); - buf->AppendWORD(0); - buf->AppendWORD(1); - - // "040904B0" = LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP - buf->AppendString(TEXT("040904B0")); - buf->Align(4); - - // Strings - vector keys; - for (map::const_iterator it = - m_props.begin(); it != m_props.end(); ++it) { - keys.push_back(it->first); - } - - for (size_t index = 0; index < keys.size(); index++) { - wstring name = keys[index]; - wstring value = m_props[name]; - - size_t stringStart = buf->getPos(); - buf->AppendWORD(0); - buf->AppendWORD(static_cast (value.length())); - buf->AppendWORD(1); - buf->AppendString(name); - buf->Align(4); - buf->AppendString(value); - buf->ReplaceWORD(stringStart, - static_cast (buf->getPos() - stringStart)); - buf->Align(4); - } - - buf->ReplaceWORD(stringTableStart, - static_cast (buf->getPos() - stringTableStart)); - buf->ReplaceWORD(stringFileInfoStart, - static_cast (buf->getPos() - stringFileInfoStart)); - - // VarFileInfo - size_t varFileInfoStart = buf->getPos(); - buf->AppendWORD(1); - buf->AppendWORD(0); - buf->AppendWORD(1); - buf->AppendString(TEXT("VarFileInfo")); - buf->Align(4); - - buf->AppendWORD(0x24); - buf->AppendWORD(0x04); - buf->AppendWORD(0x00); - buf->AppendString(TEXT("Translation")); - buf->Align(4); - // "000004B0" = LANG_NEUTRAL/SUBLANG_ENGLISH_US, Unicode CP - buf->AppendWORD(0x0000); - buf->AppendWORD(0x04B0); - - buf->ReplaceWORD(varFileInfoStart, - static_cast (buf->getPos() - varFileInfoStart)); - buf->ReplaceWORD(versionInfoStart, - static_cast (buf->getPos() - versionInfoStart)); - - return true; -} - -bool VersionInfoSwap::FillFixedFileInfo(VS_FIXEDFILEINFO *fxi) { - wstring fileVersion; - wstring productVersion; - int ret; - - fileVersion = m_props[TEXT("FileVersion")]; - productVersion = m_props[TEXT("ProductVersion")]; - - unsigned fv_1 = 0, fv_2 = 0, fv_3 = 0, fv_4 = 0; - unsigned pv_1 = 0, pv_2 = 0, pv_3 = 0, pv_4 = 0; - - ret = _stscanf_s(fileVersion.c_str(), - TEXT("%d.%d.%d.%d"), &fv_1, &fv_2, &fv_3, &fv_4); - if (ret <= 0 || ret > 4) { - return false; - } - - ret = _stscanf_s(productVersion.c_str(), - TEXT("%d.%d.%d.%d"), &pv_1, &pv_2, &pv_3, &pv_4); - if (ret <= 0 || ret > 4) { - return false; - } - - fxi->dwSignature = 0xFEEF04BD; - fxi->dwStrucVersion = 0x00010000; - - fxi->dwFileVersionMS = MAKELONG(fv_2, fv_1); - fxi->dwFileVersionLS = MAKELONG(fv_4, fv_3); - fxi->dwProductVersionMS = MAKELONG(pv_2, pv_1); - fxi->dwProductVersionLS = MAKELONG(pv_4, pv_3); - - fxi->dwFileFlagsMask = 0; - fxi->dwFileFlags = 0; - fxi->dwFileOS = VOS_NT_WINDOWS32; - - wstring exeExt = - m_launcher.substr(m_launcher.find_last_of(TEXT("."))); - if (exeExt == TEXT(".exe")) { - fxi->dwFileType = VFT_APP; - } else if (exeExt == TEXT(".dll")) { - fxi->dwFileType = VFT_DLL; - } else { - fxi->dwFileType = VFT_UNKNOWN; - } - fxi->dwFileSubtype = 0; - - fxi->dwFileDateLS = 0; - fxi->dwFileDateMS = 0; - - return true; -} - -/* - * Adds new resource in the executable - */ -bool VersionInfoSwap::UpdateResource(LPVOID lpResLock, DWORD size) { - - HANDLE hUpdateRes; - BOOL r; - - hUpdateRes = ::BeginUpdateResource(m_launcher.c_str(), FALSE); - if (hUpdateRes == NULL) { - return false; - } - - r = ::UpdateResource(hUpdateRes, - RT_VERSION, - MAKEINTRESOURCE(VS_VERSION_INFO), - MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), - lpResLock, - size); - - if (!r) { - return false; - } - - if (!::EndUpdateResource(hUpdateRes, FALSE)) { - return false; - } - - return true; -} --- /dev/null 2019-11-18 21:20:32.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/VersionInfoSwap.cpp 2019-11-18 21:20:27.700440100 -0500 @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "VersionInfoSwap.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; + +/* + * [Property file] contains key/value pairs + * The swap tool uses these pairs to create new version resource + * + * See MSDN docs for VS_VERSIONINFO structure that + * depicts organization of data in this version resource + * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx + * + * The swap tool makes changes in [Executable file] + * The tool assumes that the executable file has no version resource + * and it adds new resource in the executable file. + * If the executable file has an existing version resource, then + * the existing version resource will be replaced with new one. + */ + +VersionInfoSwap::VersionInfoSwap(wstring executableProperties, + wstring launcher) { + m_executableProperties = executableProperties; + m_launcher = launcher; +} + +bool VersionInfoSwap::PatchExecutable() { + bool b = LoadFromPropertyFile(); + if (!b) { + return false; + } + + ByteBuffer buf; + b = CreateNewResource(&buf); + if (!b) { + return false; + } + + b = this->UpdateResource(buf.getPtr(), static_cast (buf.getPos())); + if (!b) { + return false; + } + + return true; +} + +bool VersionInfoSwap::LoadFromPropertyFile() { + wifstream stream(m_executableProperties.c_str()); + + const locale empty_locale = locale::empty(); + const locale utf8_locale = + locale(empty_locale, new codecvt_utf8()); + stream.imbue(utf8_locale); + + if (stream.is_open() == true) { + int lineNumber = 1; + while (stream.eof() == false) { + wstring line; + getline(stream, line); + + // # at the first character will comment out the line. + if (line.empty() == false && line[0] != '#') { + wstring::size_type pos = line.find('='); + if (pos != wstring::npos) { + wstring name = line.substr(0, pos); + wstring value = line.substr(pos + 1); + m_props[name] = value; + } + } + lineNumber++; + } + return true; + } + + return false; +} + +/* + * Creates new version resource + * + * MSND docs for VS_VERSION_INFO structure + * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx + */ +bool VersionInfoSwap::CreateNewResource(ByteBuffer *buf) { + size_t versionInfoStart = buf->getPos(); + buf->AppendWORD(0); + buf->AppendWORD(sizeof VS_FIXEDFILEINFO); + buf->AppendWORD(0); + buf->AppendString(TEXT("VS_VERSION_INFO")); + buf->Align(4); + + VS_FIXEDFILEINFO fxi; + if (!FillFixedFileInfo(&fxi)) { + return false; + } + buf->AppendBytes((BYTE*) & fxi, sizeof (VS_FIXEDFILEINFO)); + buf->Align(4); + + // String File Info + size_t stringFileInfoStart = buf->getPos(); + buf->AppendWORD(0); + buf->AppendWORD(0); + buf->AppendWORD(1); + buf->AppendString(TEXT("StringFileInfo")); + buf->Align(4); + + // String Table + size_t stringTableStart = buf->getPos(); + buf->AppendWORD(0); + buf->AppendWORD(0); + buf->AppendWORD(1); + + // "040904B0" = LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP + buf->AppendString(TEXT("040904B0")); + buf->Align(4); + + // Strings + vector keys; + for (map::const_iterator it = + m_props.begin(); it != m_props.end(); ++it) { + keys.push_back(it->first); + } + + for (size_t index = 0; index < keys.size(); index++) { + wstring name = keys[index]; + wstring value = m_props[name]; + + size_t stringStart = buf->getPos(); + buf->AppendWORD(0); + buf->AppendWORD(static_cast (value.length())); + buf->AppendWORD(1); + buf->AppendString(name); + buf->Align(4); + buf->AppendString(value); + buf->ReplaceWORD(stringStart, + static_cast (buf->getPos() - stringStart)); + buf->Align(4); + } + + buf->ReplaceWORD(stringTableStart, + static_cast (buf->getPos() - stringTableStart)); + buf->ReplaceWORD(stringFileInfoStart, + static_cast (buf->getPos() - stringFileInfoStart)); + + // VarFileInfo + size_t varFileInfoStart = buf->getPos(); + buf->AppendWORD(1); + buf->AppendWORD(0); + buf->AppendWORD(1); + buf->AppendString(TEXT("VarFileInfo")); + buf->Align(4); + + buf->AppendWORD(0x24); + buf->AppendWORD(0x04); + buf->AppendWORD(0x00); + buf->AppendString(TEXT("Translation")); + buf->Align(4); + // "000004B0" = LANG_NEUTRAL/SUBLANG_ENGLISH_US, Unicode CP + buf->AppendWORD(0x0000); + buf->AppendWORD(0x04B0); + + buf->ReplaceWORD(varFileInfoStart, + static_cast (buf->getPos() - varFileInfoStart)); + buf->ReplaceWORD(versionInfoStart, + static_cast (buf->getPos() - versionInfoStart)); + + return true; +} + +bool VersionInfoSwap::FillFixedFileInfo(VS_FIXEDFILEINFO *fxi) { + wstring fileVersion; + wstring productVersion; + int ret; + + fileVersion = m_props[TEXT("FileVersion")]; + productVersion = m_props[TEXT("ProductVersion")]; + + unsigned fv_1 = 0, fv_2 = 0, fv_3 = 0, fv_4 = 0; + unsigned pv_1 = 0, pv_2 = 0, pv_3 = 0, pv_4 = 0; + + ret = _stscanf_s(fileVersion.c_str(), + TEXT("%d.%d.%d.%d"), &fv_1, &fv_2, &fv_3, &fv_4); + if (ret <= 0 || ret > 4) { + return false; + } + + ret = _stscanf_s(productVersion.c_str(), + TEXT("%d.%d.%d.%d"), &pv_1, &pv_2, &pv_3, &pv_4); + if (ret <= 0 || ret > 4) { + return false; + } + + fxi->dwSignature = 0xFEEF04BD; + fxi->dwStrucVersion = 0x00010000; + + fxi->dwFileVersionMS = MAKELONG(fv_2, fv_1); + fxi->dwFileVersionLS = MAKELONG(fv_4, fv_3); + fxi->dwProductVersionMS = MAKELONG(pv_2, pv_1); + fxi->dwProductVersionLS = MAKELONG(pv_4, pv_3); + + fxi->dwFileFlagsMask = 0; + fxi->dwFileFlags = 0; + fxi->dwFileOS = VOS_NT_WINDOWS32; + + wstring exeExt = + m_launcher.substr(m_launcher.find_last_of(TEXT("."))); + if (exeExt == TEXT(".exe")) { + fxi->dwFileType = VFT_APP; + } else if (exeExt == TEXT(".dll")) { + fxi->dwFileType = VFT_DLL; + } else { + fxi->dwFileType = VFT_UNKNOWN; + } + fxi->dwFileSubtype = 0; + + fxi->dwFileDateLS = 0; + fxi->dwFileDateMS = 0; + + return true; +} + +/* + * Adds new resource in the executable + */ +bool VersionInfoSwap::UpdateResource(LPVOID lpResLock, DWORD size) { + + HANDLE hUpdateRes; + BOOL r; + + hUpdateRes = ::BeginUpdateResource(m_launcher.c_str(), FALSE); + if (hUpdateRes == NULL) { + return false; + } + + r = ::UpdateResource(hUpdateRes, + RT_VERSION, + MAKEINTRESOURCE(VS_VERSION_INFO), + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), + lpResLock, + size); + + if (!r) { + return false; + } + + if (!::EndUpdateResource(hUpdateRes, FALSE)) { + return false; + } + + return true; +} --- old/src/jdk.jpackage/windows/native/libjpackage/VersionInfoSwap.h 2019-11-18 21:20:51.303719000 -0500 +++ /dev/null 2019-11-18 21:20:52.000000000 -0500 @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef VERSIONINFOSWAP_H -#define VERSIONINFOSWAP_H - -#include "ByteBuffer.h" -#include - -using namespace std; - -class VersionInfoSwap { -public: - VersionInfoSwap(wstring executableProperties, wstring launcher); - - bool PatchExecutable(); - -private: - wstring m_executableProperties; - wstring m_launcher; - - map m_props; - - bool LoadFromPropertyFile(); - bool CreateNewResource(ByteBuffer *buf); - bool UpdateResource(LPVOID lpResLock, DWORD size); - bool FillFixedFileInfo(VS_FIXEDFILEINFO *fxi); -}; - -#endif // VERSIONINFOSWAP_H \ No newline at end of file --- /dev/null 2019-11-18 21:20:52.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/VersionInfoSwap.h 2019-11-18 21:20:48.083026400 -0500 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef VERSIONINFOSWAP_H +#define VERSIONINFOSWAP_H + +#include "ByteBuffer.h" +#include + +using namespace std; + +class VersionInfoSwap { +public: + VersionInfoSwap(wstring executableProperties, wstring launcher); + + bool PatchExecutable(); + +private: + wstring m_executableProperties; + wstring m_launcher; + + map m_props; + + bool LoadFromPropertyFile(); + bool CreateNewResource(ByteBuffer *buf); + bool UpdateResource(LPVOID lpResLock, DWORD size); + bool FillFixedFileInfo(VS_FIXEDFILEINFO *fxi); +}; + +#endif // VERSIONINFOSWAP_H + --- /dev/null 2019-11-18 21:21:11.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/WinErrorHandling.cpp 2019-11-18 21:21:07.882568100 -0500 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "WinErrorHandling.h" +#include "Log.h" +#include "SysInfo.h" +#include "FileUtils.h" + + +namespace { + +std::string makeMessage(const std::string& msg, const char* label, + const void* c, DWORD errorCode) { + std::ostringstream err; + err << (label ? label : "Some error") << " [" << errorCode << "]"; + + HMODULE hmodule = NULL; + if (c) { + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS + | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(c), &hmodule); + + if (!hmodule) { + LOG_WARNING(tstrings::any() << "GetModuleHandleEx() failed for " + << c << " address."); + } + } + if (hmodule || !c) { + err << "(" << SysError::getSysErrorMessage(errorCode, hmodule) << ")"; + } + + return joinErrorMessages(msg, err.str()); +} + + +std::wstring getSystemMessageDescription(DWORD messageId, HMODULE moduleHandle) { + LPWSTR pMsg = NULL; + std::wstring descr; + + // we always retrieve UNICODE description from system, + // convert it to utf8 if UNICODE is not defined + + while (true) { + DWORD res = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS + | (moduleHandle != NULL ? FORMAT_MESSAGE_FROM_HMODULE : 0), + moduleHandle, messageId, 0, (LPWSTR)&pMsg, 0, NULL); + if (res > 0) { + // replace all non-printed chars with space + for (DWORD i=0; i0; i--) { + if (pMsg[i] > L' ' && pMsg[i] != L'.') { + break; + } + pMsg[i] = 0; + } + + descr = pMsg; + + LocalFree(pMsg); + } else { + // if we fail to get description for specific moduleHandle, + // try to get "common" description. + if (moduleHandle != NULL) { + moduleHandle = NULL; + continue; + } + descr = L"No description available"; + } + break; + } + + return descr; +} + +} // namespace + + +SysError::SysError(const tstrings::any& msg, const void* caller, DWORD ec, + const char* label): + +std::runtime_error(makeMessage(msg.str(), label, caller, ec)) { +} + +std::wstring SysError::getSysErrorMessage(DWORD errCode, HMODULE moduleHandle) { + tstrings::any msg; + msg << "system error " << errCode + << " (" << getSystemMessageDescription(errCode, moduleHandle) << ")"; + return msg.tstr(); +} + +std::wstring SysError::getComErrorMessage(HRESULT hr) { + HRESULT hrOrig = hr; + // for FACILITY_WIN32 facility we need to reset hiword + if(HRESULT_FACILITY(hr) == FACILITY_WIN32) { + hr = HRESULT_CODE(hr); + } + return tstrings::format(_T("COM error 0x%08X (%s)"), hrOrig, + getSystemMessageDescription(hr, NULL)); +} --- old/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-11-18 21:21:22.131939200 -0500 +++ /dev/null 2019-11-18 21:21:23.000000000 -0500 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include - -extern "C" { - - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved) { - return true; - } -} \ No newline at end of file --- /dev/null 2019-11-18 21:21:23.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/WinErrorHandling.h 2019-11-18 21:21:18.647791400 -0500 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +#ifndef WinErrorHandling_h +#define WinErrorHandling_h + + +#include "ErrorHandling.h" + + +class SysError : public std::runtime_error { +public: + SysError(const tstrings::any& msg, const void* caller, + DWORD errorCode=GetLastError(), const char* label="System error"); + + // returns string "system error (error_description)" + // in UNICODE is not defined, the string returned is utf8-encoded + static std::wstring getSysErrorMessage(DWORD errCode = GetLastError(), + HMODULE moduleHandle = NULL); + + // returns string "COM error 0x
(error_description)" + // in UNICODE is not defined, the string returned is utf8-encoded + static std::wstring getComErrorMessage(HRESULT hr); +}; + +#endif // #ifndef WinErrorHandling_h --- /dev/null 2019-11-18 21:21:42.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/WinSysInfo.cpp 2019-11-18 21:21:39.078057100 -0500 @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include + +#include "WinSysInfo.h" +#include "FileUtils.h" +#include "WinErrorHandling.h" + +#pragma comment(lib, "Shell32") + +namespace SysInfo { + +tstring getTempDir() { + std::vector buffer(MAX_PATH); + DWORD res = GetTempPath(static_cast(buffer.size()), buffer.data()); + if (res > buffer.size()) { + buffer.resize(res); + GetTempPath(static_cast(buffer.size()), buffer.data()); + } + return FileUtils::removeTrailingSlash(buffer.data()); +} + +namespace { + +template +tstring getSystemDirImpl(Func func, const std::string& label) { + std::vector buffer(MAX_PATH); + for (int i=0; i<2; i++) { + DWORD res = func(buffer.data(), static_cast(buffer.size())); + if (!res) { + JP_THROW(SysError(label + " failed", func)); + } + if (res < buffer.size()) { + return FileUtils::removeTrailingSlash(buffer.data()); + } + buffer.resize(res + 1); + } + JP_THROW("Unexpected reply from" + label); +} + +} // namespace + +tstring getSystem32Dir() { + return getSystemDirImpl(GetSystemDirectory, "GetSystemDirectory"); +} + +tstring getWIPath() { + return FileUtils::mkpath() << getSystem32Dir() << _T("msiexec.exe"); +} + +namespace { + +tstring getModulePath(HMODULE h) +{ + std::vector buf(MAX_PATH); + DWORD len = 0; + while (true) { + len = GetModuleFileName(h, buf.data(), (DWORD)buf.size()); + if (len < buf.size()) { + break; + } + // buffer is too small, increase it + buf.resize(buf.size() * 2); + } + + if (len == 0) { + // error occured + JP_THROW(SysError("GetModuleFileName failed", GetModuleFileName)); + } + return tstring(buf.begin(), buf.begin() + len); +} + +} // namespace + +tstring getProcessModulePath() { + return getModulePath(NULL); +} + +HMODULE getCurrentModuleHandle() +{ + // get module handle for the address of this function + LPCWSTR address = reinterpret_cast(getCurrentModuleHandle); + HMODULE hmodule = NULL; + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS + | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, address, &hmodule)) + { + JP_THROW(SysError(tstrings::any() << "GetModuleHandleExW failed", + GetModuleHandleExW)); + } + return hmodule; +} + +tstring getCurrentModulePath() +{ + return getModulePath(getCurrentModuleHandle()); +} + +tstring_array getCommandArgs(CommandArgProgramNameMode progNameMode) +{ + int argc = 0; + tstring_array result; + + LPWSTR *parsedArgs = CommandLineToArgvW(GetCommandLineW(), &argc); + if (parsedArgs == NULL) { + JP_THROW(SysError("CommandLineToArgvW failed", CommandLineToArgvW)); + } + // the 1st element contains program name + for (int i = progNameMode == ExcludeProgramName ? 1 : 0; i < argc; i++) { + result.push_back(parsedArgs[i]); + } + LocalFree(parsedArgs); + + return result; +} + +namespace { + +tstring getEnvVariableImpl(const tstring& name, bool* errorOccured=0) { + std::vector buf(10); + SetLastError(ERROR_SUCCESS); + const DWORD size = GetEnvironmentVariable(name.c_str(), buf.data(), + DWORD(buf.size())); + if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + if (errorOccured) { + *errorOccured = true; + return tstring(); + } + JP_THROW(SysError(tstrings::any() << "GetEnvironmentVariable(" + << name << ") failed. Variable not set", GetEnvironmentVariable)); + } + + if (size > buf.size()) { + buf.resize(size); + GetEnvironmentVariable(name.c_str(), buf.data(), DWORD(buf.size())); + if (GetLastError() != ERROR_SUCCESS) { + if (errorOccured) { + *errorOccured = true; + return tstring(); + } + JP_THROW(SysError(tstrings::any() << "GetEnvironmentVariable(" + << name << ") failed", GetEnvironmentVariable)); + } + } + + if (errorOccured) { + *errorOccured = false; + } + return tstring(buf.data()); +} + +} // namespace + +tstring getEnvVariable(const tstring& name) { + return getEnvVariableImpl(name); +} + +tstring getEnvVariable(const std::nothrow_t&, const tstring& name, + const tstring& defValue) { + bool errorOccured = false; + const tstring reply = getEnvVariableImpl(name, &errorOccured); + if (errorOccured) { + return defValue; + } + return reply; +} + +bool isEnvVariableSet(const tstring& name) { + TCHAR unused[1]; + SetLastError(ERROR_SUCCESS); + GetEnvironmentVariable(name.c_str(), unused, _countof(unused)); + return GetLastError() != ERROR_ENVVAR_NOT_FOUND; +} + +} // end of namespace SysInfo --- old/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-11-18 21:21:53.120817800 -0500 +++ /dev/null 2019-11-18 21:21:54.000000000 -0500 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include - -extern "C" { - - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved) { - return true; - } -} \ No newline at end of file --- /dev/null 2019-11-18 21:21:54.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/WinSysInfo.h 2019-11-18 21:21:49.665778100 -0500 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +#ifndef WINSYSINFO_H +#define WINSYSINFO_H + +#include "SysInfo.h" + + +// +// Windows specific SysInfo. +// +namespace SysInfo { + // gets Windows System folder. A typical path is C:\Windows\System32. + tstring getSystem32Dir(); + + // returns full path to msiexec.exe executable + tstring getWIPath(); + + // Returns handle of the current module (exe or dll). + // The function assumes this code is statically linked to the module. + HMODULE getCurrentModuleHandle(); +} + + +#endif // WINSYSINFO_H --- old/src/jdk.jpackage/windows/native/libjpackage/WindowsRegistry.cpp 2019-11-18 21:22:13.547141400 -0500 +++ /dev/null 2019-11-18 21:22:15.000000000 -0500 @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include -#include -#include -#include - -#include "Utils.h" - -// Max value name size per MSDN plus NULL -#define VALUE_NAME_SIZE 16384 - -#ifdef __cplusplus -extern "C" { -#endif -#undef jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE -#define jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE 1L - - /* - * Class: jdk_jpackage_internal_WindowsRegistry - * Method: readDwordValue - * Signature: (ILjava/lang/String;Ljava/lang/String;I)I - */ - JNIEXPORT jint JNICALL Java_jdk_jpackage_internal_WindowsRegistry_readDwordValue( - JNIEnv *pEnv, jclass c, jint key, jstring jSubKey, jstring jValue, jint defaultValue) { - jint jResult = defaultValue; - - if (key != jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE) { - return jResult; - } - - wstring subKey = GetStringFromJString(pEnv, jSubKey); - wstring value = GetStringFromJString(pEnv, jValue); - - HKEY hSubKey = NULL; - LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(), 0, - KEY_QUERY_VALUE, &hSubKey); - if (status == ERROR_SUCCESS) { - DWORD dwValue = 0; - DWORD cbData = sizeof (DWORD); - status = RegQueryValueEx(hSubKey, value.c_str(), NULL, NULL, - (LPBYTE) & dwValue, &cbData); - if (status == ERROR_SUCCESS) { - jResult = (jint) dwValue; - } - - RegCloseKey(hSubKey); - } - - return jResult; - } - - /* - * Class: jdk_jpackage_internal_WindowsRegistry - * Method: openRegistryKey - * Signature: (ILjava/lang/String;)J - */ - JNIEXPORT jlong JNICALL Java_jdk_jpackage_internal_WindowsRegistry_openRegistryKey( - JNIEnv *pEnv, jclass c, jint key, jstring jSubKey) { - if (key != jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE) { - return 0; - } - - wstring subKey = GetStringFromJString(pEnv, jSubKey); - HKEY hSubKey = NULL; - LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(), 0, - KEY_QUERY_VALUE, &hSubKey); - if (status == ERROR_SUCCESS) { - return (jlong)hSubKey; - } - - return 0; - } - - /* - * Class: jdk_jpackage_internal_WindowsRegistry - * Method: enumRegistryValue - * Signature: (JI)Ljava/lang/String; - */ - JNIEXPORT jstring JNICALL Java_jdk_jpackage_internal_WindowsRegistry_enumRegistryValue( - JNIEnv *pEnv, jclass c, jlong lKey, jint jIndex) { - HKEY hKey = (HKEY)lKey; - TCHAR valueName[VALUE_NAME_SIZE] = {0}; // Max value name size per MSDN plus NULL - DWORD cchValueName = VALUE_NAME_SIZE; - LSTATUS status = RegEnumValue(hKey, (DWORD)jIndex, valueName, &cchValueName, - NULL, NULL, NULL, NULL); - if (status == ERROR_SUCCESS) { - size_t chLength = 0; - if (StringCchLength(valueName, VALUE_NAME_SIZE, &chLength) == S_OK) { - return GetJStringFromString(pEnv, valueName, (jsize)chLength); - } - } - - return NULL; - } - - /* - * Class: jdk_jpackage_internal_WindowsRegistry - * Method: closeRegistryKey - * Signature: (J)V - */ - JNIEXPORT void JNICALL Java_jdk_jpackage_internal_WindowsRegistry_closeRegistryKey( - JNIEnv *pEnc, jclass c, jlong lKey) { - HKEY hKey = (HKEY)lKey; - RegCloseKey(hKey); - } - - /* - * Class: jdk_jpackage_internal_WindowsRegistry - * Method: comparePaths - * Signature: (Ljava/lang/String;Ljava/lang/String;)Z - */ - JNIEXPORT jboolean JNICALL Java_jdk_jpackage_internal_WindowsRegistry_comparePaths( - JNIEnv *pEnv, jclass c, jstring jPath1, jstring jPath2) { - wstring path1 = GetStringFromJString(pEnv, jPath1); - wstring path2 = GetStringFromJString(pEnv, jPath2); - - path1 = GetLongPath(path1); - path2 = GetLongPath(path2); - - if (path1.length() == 0 || path2.length() == 0) { - return JNI_FALSE; - } - - if (path1.length() != path2.length()) { - return JNI_FALSE; - } - - if (_tcsnicmp(path1.c_str(), path2.c_str(), path1.length()) == 0) { - return JNI_TRUE; - } - - return JNI_FALSE; - } - -#ifdef __cplusplus -} -#endif \ No newline at end of file --- /dev/null 2019-11-18 21:22:15.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/WindowsRegistry.cpp 2019-11-18 21:22:10.239152700 -0500 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include + +#include "Utils.h" + +// Max value name size per MSDN plus NULL +#define VALUE_NAME_SIZE 16384 + +#ifdef __cplusplus +extern "C" { +#endif +#undef jdk_incubator_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE +#define jdk_incubator_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE 1L + + /* + * Class: jdk_incubator_jpackage_internal_WindowsRegistry + * Method: readDwordValue + * Signature: (ILjava/lang/String;Ljava/lang/String;I)I + */ + JNIEXPORT jint JNICALL + Java_jdk_incubator_jpackage_internal_WindowsRegistry_readDwordValue( + JNIEnv *pEnv, jclass c, jint key, jstring jSubKey, + jstring jValue, jint defaultValue) { + jint jResult = defaultValue; + + if (key != jdk_incubator_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE) { + return jResult; + } + + wstring subKey = GetStringFromJString(pEnv, jSubKey); + wstring value = GetStringFromJString(pEnv, jValue); + + HKEY hSubKey = NULL; + LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(), 0, + KEY_QUERY_VALUE, &hSubKey); + if (status == ERROR_SUCCESS) { + DWORD dwValue = 0; + DWORD cbData = sizeof (DWORD); + status = RegQueryValueEx(hSubKey, value.c_str(), NULL, NULL, + (LPBYTE) & dwValue, &cbData); + if (status == ERROR_SUCCESS) { + jResult = (jint) dwValue; + } + + RegCloseKey(hSubKey); + } + + return jResult; + } + + /* + * Class: jdk_incubator_jpackage_internal_WindowsRegistry + * Method: openRegistryKey + * Signature: (ILjava/lang/String;)J + */ + JNIEXPORT jlong JNICALL + Java_jdk_incubator_jpackage_internal_WindowsRegistry_openRegistryKey( + JNIEnv *pEnv, jclass c, jint key, jstring jSubKey) { + if (key != jdk_incubator_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE) { + return 0; + } + + wstring subKey = GetStringFromJString(pEnv, jSubKey); + HKEY hSubKey = NULL; + LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(), 0, + KEY_QUERY_VALUE, &hSubKey); + if (status == ERROR_SUCCESS) { + return (jlong)hSubKey; + } + + return 0; + } + + /* + * Class: jdk_incubator_jpackage_internal_WindowsRegistry + * Method: enumRegistryValue + * Signature: (JI)Ljava/lang/String; + */ + JNIEXPORT jstring JNICALL + Java_jdk_incubator_jpackage_internal_WindowsRegistry_enumRegistryValue( + JNIEnv *pEnv, jclass c, jlong lKey, jint jIndex) { + HKEY hKey = (HKEY)lKey; + TCHAR valueName[VALUE_NAME_SIZE] = {0}; // Max size per MSDN plus NULL + DWORD cchValueName = VALUE_NAME_SIZE; + LSTATUS status = RegEnumValue(hKey, (DWORD)jIndex, valueName, + &cchValueName, NULL, NULL, NULL, NULL); + if (status == ERROR_SUCCESS) { + size_t chLength = 0; + if (StringCchLength(valueName, VALUE_NAME_SIZE, &chLength) + == S_OK) { + return GetJStringFromString(pEnv, valueName, (jsize)chLength); + } + } + + return NULL; + } + + /* + * Class: jdk_incubator_jpackage_internal_WindowsRegistry + * Method: closeRegistryKey + * Signature: (J)V + */ + JNIEXPORT void JNICALL + Java_jdk_incubator_jpackage_internal_WindowsRegistry_closeRegistryKey( + JNIEnv *pEnc, jclass c, jlong lKey) { + HKEY hKey = (HKEY)lKey; + RegCloseKey(hKey); + } + + /* + * Class: jdk_incubator_jpackage_internal_WindowsRegistry + * Method: comparePaths + * Signature: (Ljava/lang/String;Ljava/lang/String;)Z + */ + JNIEXPORT jboolean JNICALL + Java_jdk_incubator_jpackage_internal_WindowsRegistry_comparePaths( + JNIEnv *pEnv, jclass c, jstring jPath1, jstring jPath2) { + wstring path1 = GetStringFromJString(pEnv, jPath1); + wstring path2 = GetStringFromJString(pEnv, jPath2); + + path1 = GetLongPath(path1); + path2 = GetLongPath(path2); + + if (path1.length() == 0 || path2.length() == 0) { + return JNI_FALSE; + } + + if (path1.length() != path2.length()) { + return JNI_FALSE; + } + + if (_tcsnicmp(path1.c_str(), path2.c_str(), path1.length()) == 0) { + return JNI_TRUE; + } + + return JNI_FALSE; + } + +#ifdef __cplusplus +} +#endif --- old/src/jdk.jpackage/windows/native/libjpackage/jpackage.cpp 2019-11-18 21:22:34.157438400 -0500 +++ /dev/null 2019-11-18 21:22:35.000000000 -0500 @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include -#include -#include -#include - -#include "IconSwap.h" -#include "VersionInfoSwap.h" -#include "Utils.h" - -using namespace std; - -#ifdef __cplusplus -extern "C" { -#endif - - /* - * Class: jdk_jpackage_internal_WindowsAppImageBuilder - * Method: iconSwap - * Signature: (Ljava/lang/String;Ljava/lang/String;)I - */ - JNIEXPORT jint JNICALL Java_jdk_jpackage_internal_WindowsAppImageBuilder_iconSwap( - JNIEnv *pEnv, jclass c, jstring jIconTarget, jstring jLauncher) { - wstring iconTarget = GetStringFromJString(pEnv, jIconTarget); - wstring launcher = GetStringFromJString(pEnv, jLauncher); - - if (ChangeIcon(iconTarget, launcher)) { - return 0; - } - - return 1; - } - - /* - * Class: jdk_jpackage_internal_WindowsAppImageBuilder - * Method: versionSwap - * Signature: (Ljava/lang/String;Ljava/lang/String;)I - */ - JNIEXPORT jint JNICALL Java_jdk_jpackage_internal_WindowsAppImageBuilder_versionSwap( - JNIEnv *pEnv, jclass c, jstring jExecutableProperties, jstring jLauncher) { - - wstring executableProperties = GetStringFromJString(pEnv, jExecutableProperties); - wstring launcher = GetStringFromJString(pEnv, jLauncher); - - VersionInfoSwap vs(executableProperties, launcher); - if (vs.PatchExecutable()) { - return 0; - } - - return 1; - } - - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { - return TRUE; - } - -#ifdef __cplusplus -} -#endif --- /dev/null 2019-11-18 21:22:36.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/jpackage.cpp 2019-11-18 21:22:30.600803300 -0500 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include + +#include "ResourceEditor.h" +#include "WinErrorHandling.h" +#include "IconSwap.h" +#include "VersionInfoSwap.h" +#include "Utils.h" + +using namespace std; + +#ifdef __cplusplus +extern "C" { +#endif + + /* + * Class: jdk_incubator_jpackage_internal_WindowsAppImageBuilder + * Method: iconSwap + * Signature: (Ljava/lang/String;Ljava/lang/String;)I + */ + JNIEXPORT jint JNICALL + Java_jdk_incubator_jpackage_internal_WindowsAppImageBuilder_iconSwap( + JNIEnv *pEnv, jclass c, jstring jIconTarget, jstring jLauncher) { + wstring iconTarget = GetStringFromJString(pEnv, jIconTarget); + wstring launcher = GetStringFromJString(pEnv, jLauncher); + + if (ChangeIcon(iconTarget, launcher)) { + return 0; + } + + return 1; + } + + /* + * Class: jdk_incubator_jpackage_internal_WindowsAppImageBuilder + * Method: versionSwap + * Signature: (Ljava/lang/String;Ljava/lang/String;)I + */ + JNIEXPORT jint JNICALL + Java_jdk_incubator_jpackage_internal_WindowsAppImageBuilder_versionSwap( + JNIEnv *pEnv, jclass c, jstring jExecutableProperties, + jstring jLauncher) { + + wstring executableProperties = GetStringFromJString(pEnv, + jExecutableProperties); + wstring launcher = GetStringFromJString(pEnv, jLauncher); + + VersionInfoSwap vs(executableProperties, launcher); + if (vs.PatchExecutable()) { + return 0; + } + + return 1; + } + + /* + * Class: jdk_incubator_jpackage_internal_WinExeBundler + * Method: embedMSI + * Signature: (Ljava/lang/String;Ljava/lang/String;)I + */ + JNIEXPORT jint JNICALL Java_jdk_incubator_jpackage_internal_WinExeBundler_embedMSI( + JNIEnv *pEnv, jclass c, jstring jexePath, jstring jmsiPath) { + + const wstring exePath = GetStringFromJString(pEnv, jexePath); + const wstring msiPath = GetStringFromJString(pEnv, jmsiPath); + + JP_TRY; + + ResourceEditor() + .id(L"msi") + .type(RT_RCDATA) + .apply(ResourceEditor::FileLock(exePath), msiPath); + + return 0; + + JP_CATCH_ALL; + + return 1; + } + + BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, + LPVOID lpvReserved) { + return TRUE; + } + +#ifdef __cplusplus +} +#endif --- /dev/null 2019-11-18 21:22:56.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/tstrings.cpp 2019-11-18 21:22:53.151462400 -0500 @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include + +#include "tstrings.h" +#include "ErrorHandling.h" + + +namespace tstrings { + +/* Create formatted string + */ +tstring unsafe_format(tstring::const_pointer format, ...) { + if (!format) { + throw std::invalid_argument("Destination buffer can't be NULL"); + } + + tstring fmtout; + int ret; + const int inc = 256; + + va_list args; + va_start(args, format); + do { + fmtout.resize(fmtout.size() + inc); +#ifdef _MSC_VER + ret = _vsntprintf_s(&*fmtout.begin(), fmtout.size(), _TRUNCATE, format, args); +#else + // With g++ this compiles only with '-std=gnu++0x' option + ret = vsnprintf(&*fmtout.begin(), fmtout.size(), format, args); +#endif + } while(-1 == ret); + va_end(args); + + //update string size by actual value + fmtout.resize(ret); + + return fmtout; +} + +/* + * Tests if two strings are equal according to CompareType. + * + * a - string to compare + * b - string to compare + * ct - CASE_SENSITIVE: case sensitive comparing type + * IGNORE_CASE: case insensitive comparing type + */ +bool equals(const tstring& a, const tstring& b, const CompareType ct) { + if (IGNORE_CASE==ct) { + return toLower(a) == toLower(b); + } + return a == b; +} + +bool startsWith(const tstring &str, const tstring &substr, const CompareType ct) +{ + if (str.size() < substr.size()) { + return false; + } + const tstring startOfStr = str.substr(0, substr.size()); + return tstrings::equals(startOfStr, substr, ct); +} + +bool endsWith(const tstring &str, const tstring &substr, const CompareType ct) +{ + if (str.size() < substr.size()) { + return false; + } + const tstring endOfStr = str.substr(str.size() - substr.size()); + return tstrings::equals(endOfStr, substr, ct); +} + +/* + * Split string into a vector with given delimiter string + * + * strVector - string vector to store split tstring + * str - string to split + * delimiter - delimiter to split the string around + * st - ST_ALL: return value includes an empty string + * ST_EXCEPT_EMPTY_STRING: return value does not include an empty string + * + * Note: It does not support multiple delimiters + */ +void split(tstring_array &strVector, const tstring &str, + const tstring &delimiter, const SplitType st) { + tstring::size_type start = 0, end = 0, length = str.length(); + + if (length == 0 || delimiter.length() == 0) { + return; + } + + end = str.find(delimiter, start); + while(end != tstring::npos) { + if(st == ST_ALL || end - start > 1 ) { + strVector.push_back(str.substr(start, end == tstring::npos ? + tstring::npos : end - start)); + } + start = end > (tstring::npos - delimiter.size()) ? + tstring::npos : end + delimiter.size(); + end = str.find(delimiter, start); + } + + if(st == ST_ALL || start < length) { + strVector.push_back(str.substr(start, length - start)); + } +} + +/* + * Convert uppercase letters to lowercase + */ +tstring toLower(const tstring& str) { + tstring lower(str); + tstring::iterator ok = std::transform(lower.begin(), lower.end(), + lower.begin(), tolower); + if (ok!=lower.end()) { + lower.resize(0); + } + return lower; +} + + +/* + * Replace all substring occurrences in a tstring. + * If 'str' or 'search' is empty the function returns 'str'. + * The given 'str' remains unchanged in any case. + * The function returns changed copy of 'str'. + */ +tstring replace(const tstring &str, const tstring &search, const tstring &replace) +{ + if (search.empty()) { + return str; + } + + tstring s(str); + + for (size_t pos = 0; ; pos += replace.length()) { + pos = s.find(search, pos); + if (pos == tstring::npos) { + break; + } + s.erase(pos, search.length()); + s.insert(pos, replace); + } + return s; +} + + +/* + * Remove trailing spaces + */ + +tstring trim(const tstring& str, const tstring& whitespace) { + const size_t strBegin = str.find_first_not_of(whitespace); + if (strBegin == std::string::npos) { + return tstring(); // no content + } + + const size_t strEnd = str.find_last_not_of(whitespace); + const size_t strRange = strEnd - strBegin + 1; + + return str.substr(strBegin, strRange); +} + +} // namespace tstrings + + +#ifdef TSTRINGS_WITH_WCHAR +namespace tstrings { + +namespace { +/* + * Converts UTF16-encoded string into multi-byte string of the given encoding. + */ +std::string toMultiByte(const std::wstring& utf16str, int encoding) { + std::string reply; + do { + int cm = WideCharToMultiByte(encoding, + 0, + utf16str.c_str(), + int(utf16str.size()), + NULL, + 0, + NULL, + NULL); + if (cm < 0) { + JP_THROW("Unexpected reply from WideCharToMultiByte()"); + } + if (0 == cm) { + break; + } + + reply.resize(cm); + int cm2 = WideCharToMultiByte(encoding, + 0, + utf16str.c_str(), + int(utf16str.size()), + &*reply.begin(), + cm, + NULL, + NULL); + if (cm != cm2) { + JP_THROW("Unexpected reply from WideCharToMultiByte()"); + } + } while(0); + + return reply; +} + +/* + * Converts multi-byte string of the given encoding into UTF16-encoded string. + */ +std::wstring fromMultiByte(const std::string& str, int encoding) { + std::wstring utf16; + do { + int cw = MultiByteToWideChar(encoding, + MB_ERR_INVALID_CHARS, + str.c_str(), + int(str.size()), + NULL, + 0); + if (cw < 0) { + JP_THROW("Unexpected reply from MultiByteToWideChar()"); + } + if (0 == cw) { + break; + } + + utf16.resize(cw); + int cw2 = MultiByteToWideChar(encoding, + MB_ERR_INVALID_CHARS, + str.c_str(), + int(str.size()), + &*utf16.begin(), + cw); + if (cw != cw2) { + JP_THROW("Unexpected reply from MultiByteToWideChar()"); + } + } while(0); + + return utf16; +} +} // namespace + +std::string toUtf8(const std::wstring& utf16str) { + return toMultiByte(utf16str, CP_UTF8); +} + +std::wstring toUtf16(const std::string& utf8str) { + return fromMultiByte(utf8str, CP_UTF8); +} + +} // namespace tstrings +#endif // ifdef TSTRINGS_WITH_WCHAR --- /dev/null 2019-11-18 21:23:07.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/tstrings.h 2019-11-18 21:23:04.277534500 -0500 @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef TSTRINGS_H +#define TSTRINGS_H + +#ifdef _MSC_VER +# define TSTRINGS_WITH_WCHAR +#endif + +#ifdef TSTRINGS_WITH_WCHAR +#include +#include +// Want compiler issue C4995 warnings for encounters of deprecated functions. +#include +#endif + +// STL's string header depends on deprecated functions. +// We don't care about warnings from STL header, so disable them locally. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4995) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + + +#ifndef _T +# define _T(x) x +#endif + + +#ifdef TSTRINGS_WITH_WCHAR +typedef std::wstring tstring; +typedef std::wostringstream tostringstream; +typedef std::wistringstream tistringstream; +typedef std::wstringstream tstringstream; +typedef std::wistream tistream; +typedef std::wostream tostream; +typedef std::wiostream tiostream; +typedef std::wios tios; +#else +typedef std::string tstring; +typedef std::ostringstream tostringstream; +typedef std::istringstream tistringstream; +typedef std::stringstream tstringstream; +typedef std::istream tistream; +typedef std::ostream tostream; +typedef std::iostream tiostream; +typedef std::ios tios; + +typedef const char* LPCTSTR; +typedef char TCHAR; +#endif + +// frequently used "array of tstrings" type +typedef std::vector tstring_array; + +namespace tstrings { + tstring unsafe_format(tstring::const_pointer format, ...); + + enum CompareType {CASE_SENSITIVE, IGNORE_CASE}; + bool equals(const tstring& a, const tstring& b, + const CompareType ct=CASE_SENSITIVE); + bool startsWith(const tstring &str, const tstring &substr, + const CompareType ct=CASE_SENSITIVE); + bool endsWith(const tstring &str, const tstring &substr, + const CompareType ct=CASE_SENSITIVE); + + enum SplitType {ST_ALL, ST_EXCEPT_EMPTY_STRING}; + void split(tstring_array &strVector, const tstring &str, + const tstring &delimiter, const SplitType st = ST_ALL); + inline tstring_array split(const tstring &str, const tstring &delimiter, + const SplitType st = ST_ALL) { + tstring_array result; + split(result, str, delimiter, st); + return result; + } + tstring trim(const tstring& str, const tstring& whitespace = _T(" \t")); + + /** + * Writes sequence of values from [b, e) range into string buffer inserting + * 'delimiter' after each value except of the last one. + * Returns contents of string buffer. + */ + template + tstring join(It b, It e, const tstring& delimiter=tstring()) { + tostringstream buf; + if (b != e) { + for (;;) { + buf << *b; + if (++b == e) { + break; + } + buf << delimiter; + } + } + return buf.str(); + } + + tstring toLower(const tstring& str); + + tstring replace(const tstring &str, const tstring &search, + const tstring &replace); +} + + +namespace tstrings { + inline std::string toUtf8(const std::string& utf8str) { + return utf8str; + } + +#ifdef TSTRINGS_WITH_WCHAR + // conversion to Utf8 + std::string toUtf8(const std::wstring& utf16str); + + // conversion to Utf16 + std::wstring toUtf16(const std::string& utf8str); + + inline std::wstring fromUtf8(const std::string& utf8str) { + return toUtf16(utf8str); + } + +#else + inline std::string fromUtf8(const std::string& utf8str) { + return utf8str; + } +#endif +} // namespace tstrings + + +namespace tstrings { +namespace format_detail { + + template + struct str_arg_value { + const tstring value; + + str_arg_value(const std::string& v): value(fromUtf8(v)) { + } + +#ifdef TSTRINGS_WITH_WCHAR + str_arg_value(const std::wstring& v): value(v) { + } +#endif + + tstring::const_pointer operator () () const { + return value.c_str(); + } + }; + + template <> + struct str_arg_value { + const tstring::const_pointer value; + + str_arg_value(const tstring& v): value(v.c_str()) { + } + + str_arg_value(tstring::const_pointer v): value(v) { + } + + tstring::const_pointer operator () () const { + return value; + } + }; + + inline str_arg_value arg(const std::string& v) { + return v; + } + + inline str_arg_value arg(std::string::const_pointer v) { + return (v ? v : "(null)"); + } + +#ifdef TSTRINGS_WITH_WCHAR + inline str_arg_value arg(const std::wstring& v) { + return v; + } + + inline str_arg_value arg(std::wstring::const_pointer v) { + return (v ? v : L"(null)"); + } +#else + void arg(const std::wstring&); // Compilation error by design. + void arg(std::wstring::const_pointer); // Compilation error by design. +#endif + + template + struct arg_value { + arg_value(const T v): v(v) { + } + T operator () () const { + return v; + } + private: + const T v; + }; + + inline arg_value arg(int v) { + return v; + } + inline arg_value arg(unsigned v) { + return v; + } + inline arg_value arg(long v) { + return v; + } + inline arg_value arg(unsigned long v) { + return v; + } + inline arg_value arg(long long v) { + return v; + } + inline arg_value arg(unsigned long long v) { + return v; + } + inline arg_value arg(float v) { + return v; + } + inline arg_value arg(double v) { + return v; + } + inline arg_value arg(bool v) { + return v; + } + inline arg_value arg(const void* v) { + return v; + } + +} // namespace format_detail +} // namespace tstrings + + +namespace tstrings { + template + inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7) { + return unsafe_format(fmt.c_str(), format_detail::arg(v)(), + format_detail::arg(v2)(), + format_detail::arg(v3)(), + format_detail::arg(v4)(), + format_detail::arg(v5)(), + format_detail::arg(v6)(), + format_detail::arg(v7)()); + } + + template + inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6) { + return unsafe_format(fmt.c_str(), format_detail::arg(v)(), + format_detail::arg(v2)(), + format_detail::arg(v3)(), + format_detail::arg(v4)(), + format_detail::arg(v5)(), + format_detail::arg(v6)()); + } + + template + inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4, const T5& v5) { + return unsafe_format(fmt.c_str(), format_detail::arg(v)(), + format_detail::arg(v2)(), + format_detail::arg(v3)(), + format_detail::arg(v4)(), + format_detail::arg(v5)()); + } + + template + inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4) { + return unsafe_format(fmt.c_str(), format_detail::arg(v)(), + format_detail::arg(v2)(), + format_detail::arg(v3)(), + format_detail::arg(v4)()); + } + + template + inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3) { + return unsafe_format(fmt.c_str(), format_detail::arg(v)(), + format_detail::arg(v2)(), + format_detail::arg(v3)()); + } + + template + inline tstring format(const tstring& fmt, const T& v, const T2& v2) { + return unsafe_format(fmt.c_str(), format_detail::arg(v)(), + format_detail::arg(v2)()); + + } + + template + inline tstring format(const tstring& fmt, const T& v) { + return unsafe_format(fmt.c_str(), format_detail::arg(v)()); + } +} // namespace tstrings + + +namespace tstrings { + /** + * Buffer that accepts both std::wstring and std::string instances doing + * encoding conversions behind the scenes. All std::string-s assumed to be + * UTF8-encoded, all std::wstring-s assumed to be UTF16-encoded. + */ + class any { + public: + any() { + } + + any(std::string::const_pointer msg) { + data << fromUtf8(msg); + } + + any(const std::string& msg) { + data << fromUtf8(msg); + } + +#ifdef TSTRINGS_WITH_WCHAR + any(std::wstring::const_pointer msg) { + data << msg; + } + + any(const std::wstring& msg) { + data << msg; + } + + any& operator << (const std::wstring& v) { + data << v; + return *this; + } + + // need this specialization instead std::wstring::pointer, + // otherwise LPWSTR is handled as abstract pointer (void*) + any& operator << (LPWSTR v) { + data << (v ? v : L"NULL"); + return *this; + } + + // need this specialization instead std::wstring::const_pointer, + // otherwise LPCWSTR is handled as abstract pointer (const void*) + any& operator << (LPCWSTR v) { + data << (v ? v : L"NULL"); + return *this; + } + + std::wstring wstr() const { + return data.str(); + } +#endif + + template + any& operator << (T v) { + data << v; + return *this; + } + + any& operator << (tostream& (*pf)(tostream&)) { + data << pf; + return *this; + } + + any& operator << (tios& (*pf)(tios&)) { + data << pf; + return *this; + } + + any& operator << (std::ios_base& (*pf)(std::ios_base&)) { + data << pf; + return *this; + } + + std::string str() const { + return toUtf8(data.str()); + } + + tstring tstr() const { + return data.str(); + } + + private: + tostringstream data; + }; + + inline tstring to_tstring(const any& val) { + return val.tstr(); + } +} // namespace tstrings + + +inline std::ostream& operator << (std::ostream& os, const tstrings::any& buf) { + os << buf.str(); + return os; +} + +#ifdef TSTRINGS_WITH_WCHAR +inline std::wostream& operator << (std::wostream& os, const tstrings::any& buf) { + os << buf.wstr(); + return os; +} +#endif + +#endif //TSTRINGS_H --- old/src/jdk.jpackage/windows/native/libwixhelper/libwixhelper.cpp 2019-11-18 21:23:18.004296200 -0500 +++ /dev/null 2019-11-18 21:23:19.000000000 -0500 @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include -#include -#include - -extern "C" { - -#ifdef JP_EXPORT_FUNCTION -#error Unexpected JP_EXPORT_FUNCTION define -#endif -#define JP_EXPORT_FUNCTION comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__) - - BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason, - LPVOID lpvReserved) { - return TRUE; - } - - BOOL DirectoryExist(TCHAR *szValue) { - DWORD attr = GetFileAttributes(szValue); - if (attr == INVALID_FILE_ATTRIBUTES) { - return FALSE; - } - - if (attr & FILE_ATTRIBUTE_DIRECTORY) { - return TRUE; - } - - return FALSE; - } - - UINT __stdcall CheckInstallDir(MSIHANDLE hInstall) { - #pragma JP_EXPORT_FUNCTION - - TCHAR *szValue = NULL; - DWORD cchSize = 0; - - UINT result = MsiGetProperty(hInstall, TEXT("APPLICATIONFOLDER"), TEXT(""), &cchSize); - if (result == ERROR_MORE_DATA) { - cchSize = cchSize + 1; // NULL termination - szValue = new TCHAR[cchSize]; - if (szValue) { - result = MsiGetProperty(hInstall, TEXT("APPLICATIONFOLDER"), szValue, &cchSize); - } else { - return ERROR_INSTALL_FAILURE; - } - } - - if (result != ERROR_SUCCESS) { - delete [] szValue; - return ERROR_INSTALL_FAILURE; - } - - if (DirectoryExist(szValue)) { - if (PathIsDirectoryEmpty(szValue)) { - MsiSetProperty(hInstall, TEXT("INSTALLDIR_VALID"), TEXT("1")); - } else { - MsiSetProperty(hInstall, TEXT("INSTALLDIR_VALID"), TEXT("0")); - } - } else { - MsiSetProperty(hInstall, TEXT("INSTALLDIR_VALID"), TEXT("1")); - } - - delete [] szValue; - - return ERROR_SUCCESS; - } -} --- /dev/null 2019-11-18 21:23:19.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libwixhelper/libwixhelper.cpp 2019-11-18 21:23:14.734976200 -0500 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include + +extern "C" { + +#ifdef JP_EXPORT_FUNCTION +#error Unexpected JP_EXPORT_FUNCTION define +#endif +#define JP_EXPORT_FUNCTION comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__) + + BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason, + LPVOID lpvReserved) { + return TRUE; + } + + BOOL DirectoryExist(TCHAR *szValue) { + DWORD attr = GetFileAttributes(szValue); + if (attr == INVALID_FILE_ATTRIBUTES) { + return FALSE; + } + + if (attr & FILE_ATTRIBUTE_DIRECTORY) { + return TRUE; + } + + return FALSE; + } + + UINT __stdcall CheckInstallDir(MSIHANDLE hInstall) { + #pragma JP_EXPORT_FUNCTION + + TCHAR *szValue = NULL; + DWORD cchSize = 0; + + UINT result = MsiGetProperty(hInstall, TEXT("INSTALLDIR"), + TEXT(""), &cchSize); + if (result == ERROR_MORE_DATA) { + cchSize = cchSize + 1; // NULL termination + szValue = new TCHAR[cchSize]; + if (szValue) { + result = MsiGetProperty(hInstall, TEXT("INSTALLDIR"), + szValue, &cchSize); + } else { + return ERROR_INSTALL_FAILURE; + } + } + + if (result != ERROR_SUCCESS) { + delete [] szValue; + return ERROR_INSTALL_FAILURE; + } + + if (DirectoryExist(szValue)) { + if (PathIsDirectoryEmpty(szValue)) { + MsiSetProperty(hInstall, TEXT("INSTALLDIR_VALID"), TEXT("1")); + } else { + MsiSetProperty(hInstall, TEXT("INSTALLDIR_VALID"), TEXT("0")); + } + } else { + MsiSetProperty(hInstall, TEXT("INSTALLDIR_VALID"), TEXT("1")); + } + + delete [] szValue; + + return ERROR_SUCCESS; + } +} --- /dev/null 2019-11-18 21:23:38.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/msiwrapper/Executor.cpp 2019-11-18 21:23:35.137033900 -0500 @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include "Executor.h" +#include "Log.h" +#include "WinErrorHandling.h" + + +namespace { + +void escapeArg(std::wstring& str) { + if (str.empty()) { + return; + } + + if (str.front() == L'\"' && str.back() == L'\"' && str.size() > 1) { + return; + } + + if (str.find_first_of(L" \t") != std::wstring::npos) { + str = L'"' + str + L'"'; + } +} + +} // namespace + + +std::wstring Executor::args() const { + tstring_array tmpArgs; + // argv[0] is the module name. + tmpArgs.push_back(appPath); + tmpArgs.insert(tmpArgs.end(), argsArray.begin(), argsArray.end()); + + std::for_each(tmpArgs.begin(), tmpArgs.end(), escapeArg); + return tstrings::join(tmpArgs.begin(), tmpArgs.end(), _T(" ")); +} + + +int Executor::execAndWaitForExit() const { + UniqueHandle h = startProcess(); + + const DWORD res = ::WaitForSingleObject(h.get(), INFINITE); + if (WAIT_FAILED == res) { + JP_THROW(SysError("WaitForSingleObject() failed", WaitForSingleObject)); + } + + DWORD exitCode = 0; + if (!GetExitCodeProcess(h.get(), &exitCode)) { + // Error reading process's exit code. + JP_THROW(SysError("GetExitCodeProcess() failed", GetExitCodeProcess)); + } + + const DWORD processId = GetProcessId(h.get()); + if (!processId) { + JP_THROW(SysError("GetProcessId() failed.", GetProcessId)); + } + + LOG_TRACE(tstrings::any() << "Process with PID=" << processId + << " terminated. Exit code=" << exitCode); + + return static_cast(exitCode); +} + + +UniqueHandle Executor::startProcess() const { + const std::wstring argsStr = args(); + + std::vector argsBuffer(argsStr.begin(), argsStr.end()); + argsBuffer.push_back(0); // terminating '\0' + + STARTUPINFO startupInfo; + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + + PROCESS_INFORMATION processInfo; + ZeroMemory(&processInfo, sizeof(processInfo)); + + DWORD creationFlags = 0; + + if (!theVisible) { + // For GUI applications. + startupInfo.dwFlags |= STARTF_USESHOWWINDOW; + startupInfo.wShowWindow = SW_HIDE; + + // For console applications. + creationFlags |= CREATE_NO_WINDOW; + } + + tstrings::any msg; + msg << "CreateProcess(" << appPath << ", " << argsStr << ")"; + + if (!CreateProcess(appPath.c_str(), argsBuffer.data(), NULL, NULL, FALSE, + creationFlags, NULL, NULL, &startupInfo, &processInfo)) { + msg << " failed"; + JP_THROW(SysError(msg, CreateProcess)); + } + + msg << " succeeded; PID=" << processInfo.dwProcessId; + LOG_TRACE(msg); + + // Close unneeded handles immediately. + UniqueHandle(processInfo.hThread); + + // Return process handle. + return UniqueHandle(processInfo.hProcess); +} --- old/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-11-18 21:23:48.628999700 -0500 +++ /dev/null 2019-11-18 21:23:50.000000000 -0500 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include - -extern "C" { - - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved) { - return true; - } -} \ No newline at end of file --- /dev/null 2019-11-18 21:23:50.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/msiwrapper/Executor.h 2019-11-18 21:23:45.395063900 -0500 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef EXECUTOR_H +#define EXECUTOR_H + +#include "tstrings.h" +#include "UniqueHandle.h" + + +class Executor { +public: + explicit Executor(const std::wstring& appPath=std::wstring()) { + app(appPath).visible(false); + } + + /** + * Returns command line configured with arg() calls so far. + */ + std::wstring args() const; + + /** + * Set path to application to execute. + */ + Executor& app(const std::wstring& v) { + appPath = v; + return *this; + } + + /** + * Adds another command line argument. + */ + Executor& arg(const std::wstring& v) { + argsArray.push_back(v); + return *this; + } + + /** + * Controls if application window should be visible. + */ + Executor& visible(bool v) { + theVisible = v; + return *this; + } + + /** + * Starts application process and blocks waiting when the started + * process terminates. + * Returns process exit code. + * Throws exception if process start failed. + */ + int execAndWaitForExit() const; + +private: + UniqueHandle startProcess() const; + + bool theVisible; + tstring_array argsArray; + std::wstring appPath; +}; + +#endif // #ifndef EXECUTOR_H --- /dev/null 2019-11-18 21:24:08.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/msiwrapper/MsiWrapper.cpp 2019-11-18 21:24:05.448016500 -0500 @@ -0,0 +1,42 @@ +#include +#include + +#include "SysInfo.h" +#include "FileUtils.h" +#include "Executor.h" +#include "Resources.h" +#include "WinErrorHandling.h" + + +int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR lpCmdLine, int nShowCmd) +{ + JP_TRY; + + // Create temporary directory where to extract msi file. + const auto tempMsiDir = FileUtils::createTempDirectory(); + + // Schedule temporary directory for deletion. + FileUtils::Deleter cleaner; + cleaner.appendRecursiveDirectory(tempMsiDir); + + const auto msiPath = FileUtils::mkpath() << tempMsiDir << L"main.msi"; + + // Extract msi file. + Resource(L"msi", RT_RCDATA).saveToFile(msiPath); + + // Setup executor to run msiexec + Executor msiExecutor(SysInfo::getWIPath()); + msiExecutor.arg(L"/i").arg(msiPath); + const auto args = SysInfo::getCommandArgs(); + std::for_each(args.begin(), args.end(), + [&msiExecutor] (const tstring& arg) { + msiExecutor.arg(arg); + }); + + // Install msi file. + return msiExecutor.execAndWaitForExit(); + + JP_CATCH_ALL; + + return -1; +} --- /dev/null 2019-11-18 21:24:19.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/msiwrapper/Resources.cpp 2019-11-18 21:24:16.046472100 -0500 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "Resources.h" +#include "FileUtils.h" +#include "WinErrorHandling.h" + +#include + + +Resource::Resource(LPCTSTR name, LPCTSTR type, HINSTANCE module) { + init(name, type, module); +} + +Resource::Resource(UINT id, LPCTSTR type, HINSTANCE module) { + init(MAKEINTRESOURCE(id), type, module); +} + +void Resource::init(LPCTSTR name, LPCTSTR type, HINSTANCE module) { + if (IS_INTRESOURCE(name)) { + std::wostringstream printer; + printer << L"#" << reinterpret_cast(name); + nameStr = printer.str(); + namePtr = name; + } else { + nameStr = name; + namePtr = nameStr.c_str(); + } + if (IS_INTRESOURCE(type)) { + std::wostringstream printer; + printer << L"#" << reinterpret_cast(name); + typeStr = printer.str(); + typePtr = type; + } else { + typeStr = type; + typePtr = typeStr.c_str(); + } + instance = module; +} + +std::string Resource::getErrMsg(const std::string &descr) const { + return (tstrings::any() << descr << " (name='" << nameStr << + "', type='" << typeStr << "')").str(); +} + +HRSRC Resource::findResource() const { + LPCTSTR id = namePtr; + // string resources are stored in blocks (stringtables) + // id of the resource is (stringId / 16 + 1) + if (typePtr == RT_STRING) { + id = MAKEINTRESOURCE(UINT(size_t(id) / 16 + 1)); + } + return FindResource(instance, id, typePtr); +} + +LPVOID Resource::getPtr(DWORD &size) const +{ + // LoadString returns the same result if value is zero-length or + // if if the value does not exists, + // so wee need to ensure the stringtable exists + HRSRC resInfo = findResource(); + if (resInfo == NULL) { + JP_THROW(SysError(getErrMsg("cannot find resource"), FindResource)); + } + + HGLOBAL res = LoadResource(instance, resInfo); + if (res == NULL) { + JP_THROW(SysError(getErrMsg("cannot load resource"), LoadResource)); + } + + LPVOID ptr = LockResource(res); + if (res == NULL) { + JP_THROW(SysError(getErrMsg("cannot lock resource"), LockResource)); + } + + if (typePtr == RT_STRING) { + // string resources are stored in stringtables and + // need special handling + // The simplest way (while we don't need handle resource locale) + // is LoadString + // But this adds dependency on user32.dll, + // so implement custom string extraction + + // number in the block (namePtr is an integer) + size_t num = size_t(namePtr) & 0xf; + LPWSTR strPtr = (LPWSTR)ptr; + for (size_t i = 0; i < num; i++) { + // 1st symbol contains string length + strPtr += DWORD(*strPtr) + 1; + } + // *strPtr contains string length, string value starts at strPtr+1 + size = DWORD(*strPtr) * sizeof(wchar_t); + ptr = strPtr+1; + } else { + size = SizeofResource(instance, resInfo); + } + + return ptr; +} + +bool Resource::available() const { + return NULL != findResource(); +} + +unsigned Resource::size() const { + DWORD size = 0; + getPtr(size); + return size; +} + +LPCVOID Resource::rawData() const { + DWORD size = 0; + return getPtr(size); +} + +void Resource::saveToFile(const std::wstring &filePath) const { + DWORD size = 0; + const char *resPtr = (const char *)getPtr(size); + + FileUtils::FileWriter(filePath).write(resPtr, size).finalize(); +} + +Resource::ByteArray Resource::binary() const { + DWORD size = 0; + LPBYTE resPtr = (LPBYTE)getPtr(size); + return ByteArray(resPtr, resPtr+size); +} --- /dev/null 2019-11-18 21:24:29.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/msiwrapper/Resources.h 2019-11-18 21:24:26.796476000 -0500 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef RESOURCES_H +#define RESOURCES_H + +#include "WinSysInfo.h" + + +/** + * Classes for resource loading. + * Common use cases: + * - check if resource is available and save it to file: + * Resource res(_T("MyResource"), _T("CustomResourceType")); + * if (res.available()) { + * res.saveToFile(_T("c:\\temp\\my_resource.bin")); + * } + */ + +class Resource { +public: + // name and type can be specified by string id, + // by integer id (RT_* constants or MAKEINTRESOURCE) + Resource(LPCWSTR name, LPCWSTR type, + HINSTANCE module = SysInfo::getCurrentModuleHandle()); + Resource(UINT id, LPCWSTR type, + HINSTANCE module = SysInfo::getCurrentModuleHandle()); + + bool available() const; + + // all this methods throw exception if the resource is not available + unsigned size() const; + // gets raw pointer to the resource data + LPCVOID rawData() const; + + // save the resource to a file + void saveToFile(const std::wstring &filePath) const; + + typedef std::vector ByteArray; + // returns the resource as byte array + ByteArray binary() const; + +private: + std::wstring nameStr; + LPCWSTR namePtr; // can be integer value or point to nameStr.c_str() + std::wstring typeStr; + LPCWSTR typePtr; // can be integer value or point to nameStr.c_str() + HINSTANCE instance; + + void init(LPCWSTR name, LPCWSTR type, HINSTANCE module); + + // generates error message + std::string getErrMsg(const std::string &descr) const; + HRSRC findResource() const; + LPVOID getPtr(DWORD &size) const; + +private: + // disable copying + Resource(const Resource&); + Resource& operator = (const Resource&); +}; + +#endif // RESOURCES_H --- /dev/null 2019-11-18 21:24:40.000000000 -0500 +++ new/test/jdk/tools/jpackage/TEST.properties 2019-11-18 21:24:37.401420000 -0500 @@ -0,0 +1,3 @@ +keys=jpackagePlatformPackage +requires.properties=jpackage.test.SQETest +maxOutputSize=2000000 Binary files /dev/null and new/test/jdk/tools/jpackage/apps/dukeplug.png differ --- /dev/null 2019-11-18 21:25:01.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Annotations.java 2019-11-18 21:24:58.543886600 -0500 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class Annotations { + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface BeforeEach { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface AfterEach { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface Test { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @Repeatable(ParameterGroup.class) + public @interface Parameter { + + String[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface ParameterGroup { + + Parameter[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface Parameters { + } +} --- /dev/null 2019-11-18 21:25:12.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CfgFile.java 2019-11-18 21:25:09.447766700 -0500 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public final class CfgFile { + public String getValue(String section, String key) { + Objects.requireNonNull(section); + Objects.requireNonNull(key); + + Map entries = data.get(section); + TKit.assertTrue(entries != null, String.format( + "Check section [%s] is found in [%s] cfg file", section, id)); + + String value = entries.get(key); + TKit.assertNotNull(value, String.format( + "Check key [%s] is found in [%s] section of [%s] cfg file", key, + section, id)); + + return value; + } + + private CfgFile(Map> data, String id) { + this.data = data; + this.id = id; + } + + public static CfgFile readFromFile(Path path) throws IOException { + TKit.trace(String.format("Read [%s] jpackage cfg file", path)); + + final Pattern sectionBeginRegex = Pattern.compile( "\\s*\\[([^]]*)\\]\\s*"); + final Pattern keyRegex = Pattern.compile( "\\s*([^=]*)=(.*)" ); + + Map> result = new HashMap<>(); + + String currentSectionName = null; + Map currentSection = new HashMap<>(); + for (String line : Files.readAllLines(path)) { + Matcher matcher = sectionBeginRegex.matcher(line); + if (matcher.find()) { + if (currentSectionName != null) { + result.put(currentSectionName, Collections.unmodifiableMap( + new HashMap<>(currentSection))); + } + currentSectionName = matcher.group(1); + currentSection.clear(); + continue; + } + + matcher = keyRegex.matcher(line); + if (matcher.find()) { + currentSection.put(matcher.group(1), matcher.group(2)); + continue; + } + } + + if (!currentSection.isEmpty()) { + result.put("", Collections.unmodifiableMap(currentSection)); + } + + return new CfgFile(Collections.unmodifiableMap(result), path.toString()); + } + + private final Map> data; + private final String id; +} --- /dev/null 2019-11-18 21:25:23.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CommandArguments.java 2019-11-18 21:25:20.180785300 -0500 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class CommandArguments { + + CommandArguments() { + args = new ArrayList<>(); + } + + final public T addArgument(String v) { + args.add(v); + return (T) this; + } + + final public T addArguments(List v) { + args.addAll(v); + return (T) this; + } + + final public T addArgument(Path v) { + return addArgument(v.toString()); + } + + final public T addArguments(String... v) { + return addArguments(Arrays.asList(v)); + } + + final public T addPathArguments(List v) { + return addArguments(v.stream().map((p) -> p.toString()).collect( + Collectors.toList())); + } + + final public List getAllArguments() { + return List.copyOf(args); + } + + protected void verifyMutable() { + if (!isMutable()) { + throw new UnsupportedOperationException( + "Attempt to modify immutable object"); + } + } + + protected boolean isMutable() { + return true; + } + + protected List args; +} --- /dev/null 2019-11-18 21:25:34.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java 2019-11-18 21:25:30.979184900 -0500 @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.StringReader; +import java.nio.file.Path; +import java.util.*; +import java.util.regex.Pattern; +import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.Functional.ThrowingSupplier; + +public final class Executor extends CommandArguments { + + public Executor() { + saveOutputType = new HashSet<>(Set.of(SaveOutputType.NONE)); + } + + public Executor setExecutable(String v) { + return setExecutable(Path.of(v)); + } + + public Executor setExecutable(Path v) { + executable = Objects.requireNonNull(v); + toolProvider = null; + return this; + } + + public Executor setToolProvider(ToolProvider v) { + toolProvider = Objects.requireNonNull(v); + executable = null; + return this; + } + + public Executor setToolProvider(JavaTool v) { + return setToolProvider(v.asToolProvider()); + } + + public Executor setDirectory(Path v) { + directory = v; + return this; + } + + public Executor setExecutable(JavaTool v) { + return setExecutable(v.getPath()); + } + + /** + * Configures this instance to save full output that command will produce. + * This function is mutual exclusive with + * saveFirstLineOfOutput() function. + * + * @return this + */ + public Executor saveOutput() { + saveOutputType.remove(SaveOutputType.FIRST_LINE); + saveOutputType.add(SaveOutputType.FULL); + return this; + } + + /** + * Configures how to save output that command will produce. If + * v is true, the function call is equivalent to + * saveOutput() call. If v is false, + * the function will result in not preserving command output. + * + * @return this + */ + public Executor saveOutput(boolean v) { + if (v) { + saveOutput(); + } else { + saveOutputType.remove(SaveOutputType.FIRST_LINE); + saveOutputType.remove(SaveOutputType.FULL); + } + return this; + } + + /** + * Configures this instance to save only the first line out output that + * command will produce. This function is mutual exclusive with + * saveOutput() function. + * + * @return this + */ + public Executor saveFirstLineOfOutput() { + saveOutputType.add(SaveOutputType.FIRST_LINE); + saveOutputType.remove(SaveOutputType.FULL); + return this; + } + + /** + * Configures this instance to dump all output that command will produce to + * System.out and System.err. Can be used together with saveOutput() and + * saveFirstLineOfOutput() to save command output and also copy it in the + * default output streams. + * + * @return this + */ + public Executor dumpOutput() { + return dumpOutput(true); + } + + public Executor dumpOutput(boolean v) { + if (v) { + saveOutputType.add(SaveOutputType.DUMP); + } else { + saveOutputType.remove(SaveOutputType.DUMP); + } + return this; + } + + public class Result { + + Result(int exitCode) { + this.exitCode = exitCode; + } + + public String getFirstLineOfOutput() { + return output.get(0); + } + + public List getOutput() { + return output; + } + + public String getPrintableCommandLine() { + return Executor.this.getPrintableCommandLine(); + } + + public Result assertExitCodeIs(int expectedExitCode) { + TKit.assertEquals(expectedExitCode, exitCode, String.format( + "Check command %s exited with %d code", + getPrintableCommandLine(), expectedExitCode)); + return this; + } + + public Result assertExitCodeIsZero() { + return assertExitCodeIs(0); + } + + final int exitCode; + private List output; + } + + public Result execute() { + if (toolProvider != null && directory != null) { + throw new IllegalArgumentException( + "Can't change directory when using tool provider"); + } + + return ThrowingSupplier.toSupplier(() -> { + if (toolProvider != null) { + return runToolProvider(); + } + + if (executable != null) { + return runExecutable(); + } + + throw new IllegalStateException("No command to execute"); + }).get(); + } + + public String executeAndGetFirstLineOfOutput() { + return saveFirstLineOfOutput().execute().assertExitCodeIsZero().getFirstLineOfOutput(); + } + + public List executeAndGetOutput() { + return saveOutput().execute().assertExitCodeIsZero().getOutput(); + } + + private boolean withSavedOutput() { + return saveOutputType.contains(SaveOutputType.FULL) || saveOutputType.contains( + SaveOutputType.FIRST_LINE); + } + + private Path executablePath() { + if (directory == null || executable.isAbsolute()) { + return executable; + } + + // If relative path to executable is used it seems to be broken when + // ProcessBuilder changes the directory. On Windows it changes the + // directory first and on Linux it looks up for executable before + // changing the directory. So to stay of safe side, use absolute path + // to executable. + return executable.toAbsolutePath(); + } + + private Result runExecutable() throws IOException, InterruptedException { + List command = new ArrayList<>(); + command.add(executablePath().toString()); + command.addAll(args); + ProcessBuilder builder = new ProcessBuilder(command); + StringBuilder sb = new StringBuilder(getPrintableCommandLine()); + if (withSavedOutput()) { + builder.redirectErrorStream(true); + sb.append("; save output"); + } else if (saveOutputType.contains(SaveOutputType.DUMP)) { + builder.inheritIO(); + sb.append("; inherit I/O"); + } else { + builder.redirectError(ProcessBuilder.Redirect.DISCARD); + builder.redirectOutput(ProcessBuilder.Redirect.DISCARD); + sb.append("; discard I/O"); + } + if (directory != null) { + builder.directory(directory.toFile()); + sb.append(String.format("; in directory [%s]", directory)); + } + + TKit.trace("Execute " + sb.toString() + "..."); + Process process = builder.start(); + + List outputLines = null; + if (withSavedOutput()) { + try (BufferedReader outReader = new BufferedReader( + new InputStreamReader(process.getInputStream()))) { + if (saveOutputType.contains(SaveOutputType.DUMP) + || saveOutputType.contains(SaveOutputType.FULL)) { + outputLines = outReader.lines().collect(Collectors.toList()); + } else { + outputLines = Arrays.asList( + outReader.lines().findFirst().orElse(null)); + } + } finally { + if (saveOutputType.contains(SaveOutputType.DUMP) && outputLines != null) { + outputLines.stream().forEach(System.out::println); + if (saveOutputType.contains(SaveOutputType.FIRST_LINE)) { + // Pick the first line of saved output if there is one + for (String line: outputLines) { + outputLines = List.of(line); + break; + } + } + } + } + } + + Result reply = new Result(process.waitFor()); + TKit.trace("Done. Exit code: " + reply.exitCode); + + if (outputLines != null) { + reply.output = Collections.unmodifiableList(outputLines); + } + return reply; + } + + private Result runToolProvider(PrintStream out, PrintStream err) { + TKit.trace("Execute " + getPrintableCommandLine() + "..."); + Result reply = new Result(toolProvider.run(out, err, args.toArray( + String[]::new))); + TKit.trace("Done. Exit code: " + reply.exitCode); + return reply; + } + + + private Result runToolProvider() throws IOException { + if (!withSavedOutput()) { + if (saveOutputType.contains(SaveOutputType.DUMP)) { + return runToolProvider(System.out, System.err); + } + + PrintStream nullPrintStream = new PrintStream(new OutputStream() { + @Override + public void write(int b) { + // Nop + } + }); + return runToolProvider(nullPrintStream, nullPrintStream); + } + + try (ByteArrayOutputStream buf = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(buf)) { + Result reply = runToolProvider(ps, ps); + ps.flush(); + try (BufferedReader bufReader = new BufferedReader(new StringReader( + buf.toString()))) { + if (saveOutputType.contains(SaveOutputType.FIRST_LINE)) { + String firstLine = bufReader.lines().findFirst().orElse(null); + if (firstLine != null) { + reply.output = List.of(firstLine); + } + } else if (saveOutputType.contains(SaveOutputType.FULL)) { + reply.output = bufReader.lines().collect( + Collectors.toUnmodifiableList()); + } + + if (saveOutputType.contains(SaveOutputType.DUMP)) { + Stream lines; + if (saveOutputType.contains(SaveOutputType.FULL)) { + lines = reply.output.stream(); + } else { + lines = bufReader.lines(); + } + lines.forEach(System.out::println); + } + } + return reply; + } + } + + public String getPrintableCommandLine() { + final String exec; + String format = "[%s](%d)"; + if (toolProvider == null && executable == null) { + exec = ""; + } else if (toolProvider != null) { + format = "tool provider " + format; + exec = toolProvider.name(); + } else { + exec = executablePath().toString(); + } + + return String.format(format, printCommandLine(exec, args), + args.size() + 1); + } + + private static String printCommandLine(String executable, List args) { + // Want command line printed in a way it can be easily copy/pasted + // to be executed manally + Pattern regex = Pattern.compile("\\s"); + return Stream.concat(Stream.of(executable), args.stream()).map( + v -> (v.isEmpty() || regex.matcher(v).find()) ? "\"" + v + "\"" : v).collect( + Collectors.joining(" ")); + } + + private ToolProvider toolProvider; + private Path executable; + private Set saveOutputType; + private Path directory; + + private static enum SaveOutputType { + NONE, FULL, FIRST_LINE, DUMP + }; +} --- /dev/null 2019-11-18 21:25:44.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/FileAssociations.java 2019-11-18 21:25:41.346765800 -0500 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + + +final public class FileAssociations { + public FileAssociations(String faSuffixName) { + suffixName = faSuffixName; + setFilename("fa"); + setDescription("jpackage test extention"); + } + + private void createFile() { + Map entries = new HashMap<>(Map.of( + "extension", suffixName, + "mime-type", getMime(), + "description", description + )); + if (icon != null) { + if (TKit.isWindows()) { + entries.put("icon", icon.toString().replace("\\", "/")); + } else { + entries.put("icon", icon.toString()); + } + } + TKit.createPropertiesFile(file, entries); + } + + public FileAssociations setFilename(String v) { + file = TKit.workDir().resolve(v + ".properties"); + return this; + } + + public FileAssociations setDescription(String v) { + description = v; + return this; + } + + public FileAssociations setIcon(Path v) { + icon = v; + return this; + } + + Path getPropertiesFile() { + return file; + } + + String getSuffix() { + return "." + suffixName; + } + + String getMime() { + return "application/x-jpackage-" + suffixName; + } + + public void applyTo(PackageTest test) { + test.notForTypes(PackageType.MAC_DMG, () -> { + test.addInitializer(cmd -> { + createFile(); + cmd.addArguments("--file-associations", getPropertiesFile()); + }); + test.addHelloAppFileAssociationsVerifier(this); + }); + } + + private Path file; + final private String suffixName; + private String description; + private Path icon; +} --- /dev/null 2019-11-18 21:25:55.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Functional.java 2019-11-18 21:25:52.036036600 -0500 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.lang.reflect.InvocationTargetException; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + + +public class Functional { + @FunctionalInterface + public interface ThrowingConsumer { + void accept(T t) throws Throwable; + + public static Consumer toConsumer(ThrowingConsumer v) { + return o -> { + try { + v.accept(o); + } catch (Throwable ex) { + rethrowUnchecked(ex); + } + }; + } + } + + @FunctionalInterface + public interface ThrowingSupplier { + T get() throws Throwable; + + public static Supplier toSupplier(ThrowingSupplier v) { + return () -> { + try { + return v.get(); + } catch (Throwable ex) { + rethrowUnchecked(ex); + } + // Unreachable + return null; + }; + } + } + + @FunctionalInterface + public interface ThrowingFunction { + R apply(T t) throws Throwable; + + public static Function toFunction(ThrowingFunction v) { + return (t) -> { + try { + return v.apply(t); + } catch (Throwable ex) { + rethrowUnchecked(ex); + } + // Unreachable + return null; + }; + } + } + + @FunctionalInterface + public interface ThrowingRunnable { + void run() throws Throwable; + + public static Runnable toRunnable(ThrowingRunnable v) { + return () -> { + try { + v.run(); + } catch (Throwable ex) { + rethrowUnchecked(ex); + } + }; + } + } + + public static Supplier identity(Supplier v) { + return v; + } + + public static Consumer identity(Consumer v) { + return v; + } + + public static Runnable identity(Runnable v) { + return v; + } + + public static Function identity(Function v) { + return v; + } + + public static Function identityFunction(Function v) { + return v; + } + + public static Predicate identity(Predicate v) { + return v; + } + + public static Predicate identityPredicate(Predicate v) { + return v; + } + + public static class ExceptionBox extends RuntimeException { + public ExceptionBox(Throwable throwable) { + super(throwable); + } + } + + @SuppressWarnings("unchecked") + public static void rethrowUnchecked(Throwable throwable) throws ExceptionBox { + if (throwable instanceof ExceptionBox) { + throw (ExceptionBox)throwable; + } + + if (throwable instanceof InvocationTargetException) { + new ExceptionBox(throwable.getCause()); + } + + throw new ExceptionBox(throwable); + } +} --- /dev/null 2019-11-18 21:26:05.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java 2019-11-18 21:26:02.412888300 -0500 @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import jdk.jpackage.test.Functional.ThrowingFunction; +import jdk.jpackage.test.Functional.ThrowingSupplier; + +public class HelloApp { + + HelloApp(JavaAppDesc appDesc) { + if (appDesc == null) { + this.appDesc = createDefaltAppDesc(); + } else { + this.appDesc = appDesc; + } + } + + private JarBuilder prepareSources(Path srcDir) throws IOException { + final String qualifiedClassName = appDesc.className(); + + final String className = qualifiedClassName.substring( + qualifiedClassName.lastIndexOf('.') + 1); + final String packageName = appDesc.packageName(); + + final Path srcFile = srcDir.resolve(Path.of(String.join( + File.separator, qualifiedClassName.split("\\.")) + ".java")); + Files.createDirectories(srcFile.getParent()); + + JarBuilder jarBuilder = createJarBuilder().addSourceFile(srcFile); + final String moduleName = appDesc.moduleName(); + if (moduleName != null) { + Path moduleInfoFile = srcDir.resolve("module-info.java"); + TKit.createTextFile(moduleInfoFile, List.of( + String.format("module %s {", moduleName), + String.format(" exports %s;", packageName), + " requires java.desktop;", + "}" + )); + jarBuilder.addSourceFile(moduleInfoFile); + jarBuilder.setModuleVersion(appDesc.moduleVersion()); + } + + // Add package directive and replace class name in java source file. + // Works with simple test Hello.java. + // Don't expect too much from these regexps! + Pattern classNameRegex = Pattern.compile("\\bHello\\b"); + Pattern classDeclaration = Pattern.compile( + "(^.*\\bclass\\s+)\\bHello\\b(.*$)"); + Pattern importDirective = Pattern.compile( + "(?<=import (?:static )?+)[^;]+"); + AtomicBoolean classDeclared = new AtomicBoolean(); + AtomicBoolean packageInserted = new AtomicBoolean(packageName == null); + + var packageInserter = Functional.identityFunction((line) -> { + packageInserted.setPlain(true); + return String.format("package %s;%s%s", packageName, + System.lineSeparator(), line); + }); + + Files.write(srcFile, Files.readAllLines(HELLO_JAVA).stream().map(line -> { + Matcher m; + if (classDeclared.getPlain()) { + if ((m = classNameRegex.matcher(line)).find()) { + line = m.replaceAll(className); + } + return line; + } + + if (!packageInserted.getPlain() && importDirective.matcher(line).find()) { + line = packageInserter.apply(line); + } else if ((m = classDeclaration.matcher(line)).find()) { + classDeclared.setPlain(true); + line = m.group(1) + className + m.group(2); + if (!packageInserted.getPlain()) { + line = packageInserter.apply(line); + } + } + return line; + }).collect(Collectors.toList())); + + return jarBuilder; + } + + private JarBuilder createJarBuilder() { + JarBuilder builder = new JarBuilder(); + if (appDesc.jarWithMainClass()) { + builder.setMainClass(appDesc.className()); + } + return builder; + } + + void addTo(JPackageCommand cmd) { + final String moduleName = appDesc.moduleName(); + final String jarFileName = appDesc.jarFileName(); + final String qualifiedClassName = appDesc.className(); + + if (moduleName != null && appDesc.packageName() == null) { + throw new IllegalArgumentException(String.format( + "Module [%s] with default package", moduleName)); + } + + if (moduleName == null && CLASS_NAME.equals(qualifiedClassName)) { + // Use Hello.java as is. + cmd.addAction((self) -> { + Path jarFile = self.inputDir().resolve(jarFileName); + createJarBuilder().setOutputJar(jarFile).addSourceFile( + HELLO_JAVA).create(); + }); + } else { + cmd.addAction((self) -> { + final Path jarFile; + if (moduleName == null) { + jarFile = self.inputDir().resolve(jarFileName); + } else { + // `--module-path` option should be set by the moment + // when this action is being executed. + jarFile = Path.of(self.getArgumentValue("--module-path", + () -> self.inputDir().toString()), jarFileName); + Files.createDirectories(jarFile.getParent()); + } + + TKit.withTempDirectory("src", + workDir -> prepareSources(workDir).setOutputJar(jarFile).create()); + }); + } + + if (moduleName == null) { + cmd.addArguments("--main-jar", jarFileName); + cmd.addArguments("--main-class", qualifiedClassName); + } else { + cmd.addArguments("--module-path", TKit.workDir().resolve( + "input-modules")); + cmd.addArguments("--module", String.join("/", moduleName, + qualifiedClassName)); + // For modular app assume nothing will go in input directory and thus + // nobody will create input directory, so remove corresponding option + // from jpackage command line. + cmd.removeArgumentWithValue("--input"); + } + if (TKit.isWindows()) { + cmd.addArguments("--win-console"); + } + } + + static JavaAppDesc createDefaltAppDesc() { + return new JavaAppDesc().setClassName(CLASS_NAME).setJarFileName( + "hello.jar"); + } + + static void verifyOutputFile(Path outputFile, List args) { + if (!outputFile.isAbsolute()) { + verifyOutputFile(outputFile.toAbsolutePath().normalize(), args); + return; + } + + TKit.assertFileExists(outputFile); + + List contents = ThrowingSupplier.toSupplier( + () -> Files.readAllLines(outputFile)).get(); + + List expected = new ArrayList<>(List.of( + "jpackage test application", + String.format("args.length: %d", args.size()) + )); + expected.addAll(args); + + TKit.assertStringListEquals(expected, contents, String.format( + "Check contents of [%s] file", outputFile)); + } + + public static void executeLauncherAndVerifyOutput(JPackageCommand cmd) { + final Path launcherPath = cmd.appLauncherPath(); + if (!cmd.isFakeRuntime(String.format("Not running [%s] launcher", + launcherPath))) { + executeAndVerifyOutput(launcherPath, cmd.getAllArgumentValues( + "--arguments")); + } + } + + public static void executeAndVerifyOutput(Path helloAppLauncher, + String... defaultLauncherArgs) { + executeAndVerifyOutput(helloAppLauncher, List.of(defaultLauncherArgs)); + } + + public static void executeAndVerifyOutput(Path helloAppLauncher, + List defaultLauncherArgs) { + // Output file will be created in the current directory. + Path outputFile = TKit.workDir().resolve(OUTPUT_FILENAME); + ThrowingFunction.toFunction(Files::deleteIfExists).apply(outputFile); + new Executor() + .setDirectory(outputFile.getParent()) + .setExecutable(helloAppLauncher) + .dumpOutput() + .execute() + .assertExitCodeIsZero(); + + verifyOutputFile(outputFile, defaultLauncherArgs); + } + + final static String OUTPUT_FILENAME = "appOutput.txt"; + + private final JavaAppDesc appDesc; + + private static final Path HELLO_JAVA = TKit.TEST_SRC_ROOT.resolve( + "apps/image/Hello.java"); + + private final static String CLASS_NAME = HELLO_JAVA.getFileName().toString().split( + "\\.", 2)[0]; +} --- /dev/null 2019-11-18 21:26:16.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java 2019-11-18 21:26:13.341001200 -0500 @@ -0,0 +1,732 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.SecureRandom; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.incubator.jpackage.internal.ApplicationLayout; +import jdk.jpackage.test.Functional.ThrowingConsumer; +import jdk.jpackage.test.Functional.ThrowingFunction; + +/** + * jpackage command line with prerequisite actions. Prerequisite actions can be + * anything. The simplest is to compile test application and pack in a jar for + * use on jpackage command line. + */ +public final class JPackageCommand extends CommandArguments { + + public JPackageCommand() { + actions = new ArrayList<>(); + } + + public JPackageCommand(JPackageCommand cmd) { + this(); + args.addAll(cmd.args); + withToolProvider = cmd.withToolProvider; + saveConsoleOutput = cmd.saveConsoleOutput; + suppressOutput = cmd.suppressOutput; + ignoreDefaultRuntime = cmd.ignoreDefaultRuntime; + immutable = cmd.immutable; + actionsExecuted = cmd.actionsExecuted; + } + + JPackageCommand createImmutableCopy() { + JPackageCommand reply = new JPackageCommand(this); + reply.immutable = true; + return reply; + } + + public JPackageCommand setArgumentValue(String argName, String newValue) { + verifyMutable(); + + String prevArg = null; + ListIterator it = args.listIterator(); + while (it.hasNext()) { + String value = it.next(); + if (prevArg != null && prevArg.equals(argName)) { + if (newValue != null) { + it.set(newValue); + } else { + it.remove(); + it.previous(); + it.remove(); + } + return this; + } + prevArg = value; + } + + if (newValue != null) { + addArguments(argName, newValue); + } + + return this; + } + + public JPackageCommand setArgumentValue(String argName, Path newValue) { + return setArgumentValue(argName, newValue.toString()); + } + + public JPackageCommand removeArgumentWithValue(String argName) { + return setArgumentValue(argName, (String)null); + } + + public JPackageCommand removeArgument(String argName) { + args = args.stream().filter(arg -> !arg.equals(argName)).collect( + Collectors.toList()); + return this; + } + + public boolean hasArgument(String argName) { + return args.contains(argName); + } + + public T getArgumentValue(String argName, + Function defaultValueSupplier, + Function stringConverter) { + String prevArg = null; + for (String arg : args) { + if (prevArg != null && prevArg.equals(argName)) { + return stringConverter.apply(arg); + } + prevArg = arg; + } + if (defaultValueSupplier != null) { + return defaultValueSupplier.apply(this); + } + return null; + } + + public String getArgumentValue(String argName, + Function defaultValueSupplier) { + return getArgumentValue(argName, defaultValueSupplier, v -> v); + } + + public T getArgumentValue(String argName, + Supplier defaultValueSupplier, + Function stringConverter) { + return getArgumentValue(argName, (unused) -> defaultValueSupplier.get(), + stringConverter); + } + + public String getArgumentValue(String argName, + Supplier defaultValueSupplier) { + return getArgumentValue(argName, defaultValueSupplier, v -> v); + } + + public String getArgumentValue(String argName) { + return getArgumentValue(argName, (Supplier)null); + } + + public String[] getAllArgumentValues(String argName) { + List values = new ArrayList<>(); + String prevArg = null; + for (String arg : args) { + if (prevArg != null && prevArg.equals(argName)) { + values.add(arg); + } + prevArg = arg; + } + return values.toArray(String[]::new); + } + + public JPackageCommand addArguments(String name, Path value) { + return addArguments(name, value.toString()); + } + + public boolean isImagePackageType() { + return PackageType.IMAGE == getArgumentValue("--type", + () -> null, PACKAGE_TYPES::get); + } + + public PackageType packageType() { + // Don't try to be in sync with jpackage defaults. Keep it simple: + // if no `--type` explicitely set on the command line, consider + // this is operator's fault. + return getArgumentValue("--type", + () -> { + throw new IllegalStateException("Package type not set"); + }, PACKAGE_TYPES::get); + } + + public Path outputDir() { + return getArgumentValue("--dest", () -> Path.of("."), Path::of); + } + + public Path inputDir() { + return getArgumentValue("--input", () -> null, Path::of); + } + + public String version() { + return getArgumentValue("--app-version", () -> "1.0"); + } + + public String name() { + return getArgumentValue("--name", () -> getArgumentValue("--main-class")); + } + + public boolean isRuntime() { + return hasArgument("--runtime-image") + && !hasArgument("--main-jar") + && !hasArgument("--module") + && !hasArgument("--app-image"); + } + + public JPackageCommand setDefaultInputOutput() { + addArguments("--input", TKit.defaultInputDir()); + addArguments("--dest", TKit.defaultOutputDir()); + return this; + } + + public JPackageCommand setFakeRuntime() { + verifyMutable(); + + ThrowingConsumer createBulkFile = path -> { + Files.createDirectories(path.getParent()); + try (FileOutputStream out = new FileOutputStream(path.toFile())) { + byte[] bytes = new byte[4 * 1024]; + new SecureRandom().nextBytes(bytes); + out.write(bytes); + } + }; + + addAction(cmd -> { + Path fakeRuntimeDir = TKit.workDir().resolve("fake_runtime"); + + TKit.trace(String.format("Init fake runtime in [%s] directory", + fakeRuntimeDir)); + + Files.createDirectories(fakeRuntimeDir); + + if (TKit.isWindows() || TKit.isLinux()) { + // Needed to make WindowsAppBundler happy as it copies MSVC dlls + // from `bin` directory. + // Need to make the code in rpm spec happy as it assumes there is + // always something in application image. + fakeRuntimeDir.resolve("bin").toFile().mkdir(); + } + + if (TKit.isOSX()) { + // Make MacAppImageBuilder happy + createBulkFile.accept(fakeRuntimeDir.resolve(Path.of( + "Contents/Home/lib/jli/libjli.dylib"))); + } + + // Mak sure fake runtime takes some disk space. + // Package bundles with 0KB size are unexpected and considered + // an error by PackageTest. + createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("bin", "bulk"))); + + cmd.addArguments("--runtime-image", fakeRuntimeDir); + }); + + return this; + } + + JPackageCommand addAction(ThrowingConsumer action) { + verifyMutable(); + actions.add(ThrowingConsumer.toConsumer(action)); + return this; + } + + /** + * Shorthand for {@code helloAppImage(null)}. + */ + public static JPackageCommand helloAppImage() { + JavaAppDesc javaAppDesc = null; + return helloAppImage(javaAppDesc); + } + + /** + * Creates new JPackageCommand instance configured with the test Java app. + * For the explanation of `javaAppDesc` parameter, see documentation for + * #JavaAppDesc.parse() method. + * + * @param javaAppDesc Java application description + * @return this + */ + public static JPackageCommand helloAppImage(String javaAppDesc) { + final JavaAppDesc appDesc; + if (javaAppDesc == null) { + appDesc = null; + } else { + appDesc = JavaAppDesc.parse(javaAppDesc); + } + return helloAppImage(appDesc); + } + + public static JPackageCommand helloAppImage(JavaAppDesc javaAppDesc) { + JPackageCommand cmd = new JPackageCommand(); + cmd.setDefaultInputOutput().setDefaultAppName(); + PackageType.IMAGE.applyTo(cmd); + new HelloApp(javaAppDesc).addTo(cmd); + return cmd; + } + + public JPackageCommand setPackageType(PackageType type) { + verifyMutable(); + type.applyTo(this); + return this; + } + + JPackageCommand setDefaultAppName() { + return addArguments("--name", TKit.getCurrentDefaultAppName()); + } + + /** + * Returns path to output bundle of configured jpackage command. + * + * If this is build image command, returns path to application image directory. + */ + public Path outputBundle() { + final String bundleName; + if (isImagePackageType()) { + String dirName = name(); + if (TKit.isOSX()) { + dirName = dirName + ".app"; + } + bundleName = dirName; + } else if (TKit.isLinux()) { + bundleName = LinuxHelper.getBundleName(this); + } else if (TKit.isWindows()) { + bundleName = WindowsHelper.getBundleName(this); + } else if (TKit.isOSX()) { + bundleName = MacHelper.getBundleName(this); + } else { + throw TKit.throwUnknownPlatformError(); + } + + return outputDir().resolve(bundleName); + } + + /** + * Returns application layout. + * + * If this is build image command, returns application image layout of the + * output bundle relative to output directory. Otherwise returns layout of + * installed application relative to the root directory. + * + * If this command builds Java runtime, not an application, returns + * corresponding layout. + */ + public ApplicationLayout appLayout() { + final ApplicationLayout layout; + if (isRuntime()) { + layout = ApplicationLayout.javaRuntime(); + } else { + layout = ApplicationLayout.platformAppImage(); + } + + if (isImagePackageType()) { + return layout.resolveAt(outputBundle()); + } + + return layout.resolveAt(appInstallationDirectory()); + } + + /** + * Returns path to directory where application will be installed or null if + * this is build image command. + * + * E.g. on Linux for app named Foo default the function will return + * `/opt/foo` + */ + public Path appInstallationDirectory() { + if (isImagePackageType()) { + return null; + } + + if (TKit.isLinux()) { + if (isRuntime()) { + // Not fancy, but OK. + return Path.of(getArgumentValue("--install-dir", () -> "/opt"), + LinuxHelper.getPackageName(this)); + } + + // Launcher is in "bin" subfolder of the installation directory. + return appLauncherPath().getParent().getParent(); + } + + if (TKit.isWindows()) { + return WindowsHelper.getInstallationDirectory(this); + } + + if (TKit.isOSX()) { + return MacHelper.getInstallationDirectory(this); + } + + throw TKit.throwUnknownPlatformError(); + } + + /** + * Returns path to application's Java runtime. + * If the command will package Java runtime only, returns correct path to + * runtime directory. + * + * E.g.: + * [jpackage --name Foo --type rpm] -> `/opt/foo/lib/runtime` + * [jpackage --name Foo --type app-image --dest bar] -> `bar/Foo/lib/runtime` + * [jpackage --name Foo --type rpm --runtime-image java] -> `/opt/foo` + */ + public Path appRuntimeDirectory() { + return appLayout().runtimeDirectory(); + } + + /** + * Returns path for application launcher with the given name. + * + * E.g.: [jpackage --name Foo --type rpm] -> `/opt/foo/bin/Foo` + * [jpackage --name Foo --type app-image --dest bar] -> + * `bar/Foo/bin/Foo` + * + * @param launcherName name of launcher or {@code null} for the main + * launcher + * + * @throws IllegalArgumentException if the command is configured for + * packaging Java runtime + */ + public Path appLauncherPath(String launcherName) { + verifyNotRuntime(); + if (launcherName == null) { + launcherName = name(); + } + + if (TKit.isWindows()) { + launcherName = launcherName + ".exe"; + } + + if (isImagePackageType()) { + return appLayout().launchersDirectory().resolve(launcherName); + } + + if (TKit.isLinux()) { + return LinuxHelper.getLauncherPath(this).getParent().resolve(launcherName); + } + + return appLayout().launchersDirectory().resolve(launcherName); + } + + /** + * Shorthand for {@code appLauncherPath(null)}. + */ + public Path appLauncherPath() { + return appLauncherPath(null); + } + + private void verifyNotRuntime() { + if (isRuntime()) { + throw new IllegalArgumentException("Java runtime packaging"); + } + } + + /** + * Returns path to .cfg file of the given application launcher. + * + * E.g.: + * [jpackage --name Foo --type rpm] -> `/opt/foo/lib/app/Foo.cfg` + * [jpackage --name Foo --type app-image --dest bar] -> `bar/Foo/lib/app/Foo.cfg` + * + * @param launcher name of launcher or {@code null} for the main launcher + * + * @throws IllegalArgumentException if the command is configured for + * packaging Java runtime + */ + public Path appLauncherCfgPath(String launcherName) { + verifyNotRuntime(); + if (launcherName == null) { + launcherName = name(); + } + return appLayout().appDirectory().resolve(launcherName + ".cfg"); + } + + public boolean isFakeRuntime(String msg) { + Path runtimeDir = appRuntimeDirectory(); + + final Collection criticalRuntimeFiles; + if (TKit.isWindows()) { + criticalRuntimeFiles = WindowsHelper.CRITICAL_RUNTIME_FILES; + } else if (TKit.isLinux()) { + criticalRuntimeFiles = LinuxHelper.CRITICAL_RUNTIME_FILES; + } else if (TKit.isOSX()) { + criticalRuntimeFiles = MacHelper.CRITICAL_RUNTIME_FILES; + } else { + throw TKit.throwUnknownPlatformError(); + } + + if (criticalRuntimeFiles.stream().filter( + v -> runtimeDir.resolve(v).toFile().exists()).findFirst().orElse( + null) == null) { + // Fake runtime + TKit.trace(String.format( + "%s because application runtime directory [%s] is incomplete", + msg, runtimeDir)); + return true; + } + return false; + } + + public static void useToolProviderByDefault() { + defaultWithToolProvider = true; + } + + public static void useExecutableByDefault() { + defaultWithToolProvider = false; + } + + public JPackageCommand useToolProvider(boolean v) { + verifyMutable(); + withToolProvider = v; + return this; + } + + public JPackageCommand saveConsoleOutput(boolean v) { + verifyMutable(); + saveConsoleOutput = v; + return this; + } + + public JPackageCommand dumpOutput(boolean v) { + verifyMutable(); + suppressOutput = !v; + return this; + } + + public JPackageCommand ignoreDefaultRuntime(boolean v) { + verifyMutable(); + ignoreDefaultRuntime = v; + return this; + } + + public boolean isWithToolProvider() { + return Optional.ofNullable(withToolProvider).orElse( + defaultWithToolProvider); + } + + public JPackageCommand executePrerequisiteActions() { + verifyMutable(); + if (!actionsExecuted) { + actionsExecuted = true; + if (actions != null) { + actions.stream().forEach(r -> r.accept(this)); + } + } + return this; + } + + public Executor createExecutor() { + verifyMutable(); + Executor exec = new Executor() + .saveOutput(saveConsoleOutput).dumpOutput(!suppressOutput) + .addArguments(args); + + if (isWithToolProvider()) { + exec.setToolProvider(JavaTool.JPACKAGE); + } else { + exec.setExecutable(JavaTool.JPACKAGE); + } + + return exec; + } + + public Executor.Result execute() { + executePrerequisiteActions(); + + if (isImagePackageType()) { + TKit.deleteDirectoryContentsRecursive(outputDir()); + } + + return new JPackageCommand(this) + .adjustArgumentsBeforeExecution() + .createExecutor() + .execute(); + } + + public JPackageCommand executeAndAssertHelloAppImageCreated() { + executeAndAssertImageCreated(); + HelloApp.executeLauncherAndVerifyOutput(this); + return this; + } + + public JPackageCommand executeAndAssertImageCreated() { + execute().assertExitCodeIsZero(); + return assertImageCreated(); + } + + public JPackageCommand assertImageCreated() { + verifyIsOfType(PackageType.IMAGE); + TKit.assertDirectoryExists(appRuntimeDirectory()); + + if (!isRuntime()) { + TKit.assertExecutableFileExists(appLauncherPath()); + TKit.assertFileExists(appLauncherCfgPath(null)); + } + + return this; + } + + private JPackageCommand adjustArgumentsBeforeExecution() { + if (!hasArgument("--runtime-image") && !hasArgument("--app-image") && DEFAULT_RUNTIME_IMAGE != null && !ignoreDefaultRuntime) { + addArguments("--runtime-image", DEFAULT_RUNTIME_IMAGE); + } + + if (!hasArgument("--verbose") && TKit.VERBOSE_JPACKAGE) { + addArgument("--verbose"); + } + + return this; + } + + String getPrintableCommandLine() { + return new Executor() + .setExecutable(JavaTool.JPACKAGE) + .addArguments(args) + .getPrintableCommandLine(); + } + + public void verifyIsOfType(Collection types) { + verifyIsOfType(types.toArray(PackageType[]::new)); + } + + public void verifyIsOfType(PackageType ... types) { + final var typesSet = Stream.of(types).collect(Collectors.toSet()); + if (!hasArgument("--type")) { + if (!isImagePackageType()) { + if (TKit.isLinux() && typesSet.equals(PackageType.LINUX)) { + return; + } + + if (TKit.isWindows() && typesSet.equals(PackageType.WINDOWS)) { + return; + } + + if (TKit.isOSX() && typesSet.equals(PackageType.MAC)) { + return; + } + } else if (typesSet.equals(Set.of(PackageType.IMAGE))) { + return; + } + } + + if (!typesSet.contains(packageType())) { + throw new IllegalArgumentException("Unexpected type"); + } + } + + public CfgFile readLaunherCfgFile() { + return readLaunherCfgFile(null); + } + + public CfgFile readLaunherCfgFile(String launcherName) { + verifyIsOfType(PackageType.IMAGE); + if (isRuntime()) { + return null; + } + return ThrowingFunction.toFunction(CfgFile::readFromFile).apply( + appLauncherCfgPath(launcherName)); + } + + public static String escapeAndJoin(String... args) { + return escapeAndJoin(List.of(args)); + } + + public static String escapeAndJoin(List args) { + Pattern whitespaceRegexp = Pattern.compile("\\s"); + + return args.stream().map(v -> { + String str = v; + // Escape quotes. + str = str.replace("\"", "\\\""); + // Escape backslashes. + str = str.replace("\\", "\\\\"); + // If value contains whitespace characters, put the value in quotes + if (whitespaceRegexp.matcher(str).find()) { + str = "\"" + str + "\""; + } + return str; + }).collect(Collectors.joining(" ")); + } + + public static Path relativePathInRuntime(JavaTool tool) { + Path path = tool.relativePathInJavaHome(); + if (TKit.isOSX()) { + path = Path.of("Contents/Home").resolve(path); + } + return path; + } + + public static Stream filterOutput(Stream jpackageOutput) { + // Skip "WARNING: Using incubator ..." first line of output + return jpackageOutput.skip(1); + } + + public static List filterOutput(List jpackageOutput) { + return filterOutput(jpackageOutput.stream()).collect(Collectors.toList()); + } + + @Override + protected boolean isMutable() { + return !immutable; + } + + private Boolean withToolProvider; + private boolean saveConsoleOutput; + private boolean suppressOutput; + private boolean ignoreDefaultRuntime; + private boolean immutable; + private boolean actionsExecuted; + private final List> actions; + private static boolean defaultWithToolProvider; + + private final static Map PACKAGE_TYPES = Functional.identity( + () -> { + Map reply = new HashMap<>(); + for (PackageType type : PackageType.values()) { + reply.put(type.getName(), type); + } + return reply; + }).get(); + + public final static Path DEFAULT_RUNTIME_IMAGE = Functional.identity(() -> { + // Set the property to the path of run-time image to speed up + // building app images and platform bundles by avoiding running jlink + // The value of the property will be automativcally appended to + // jpackage command line if the command line doesn't have + // `--runtime-image` parameter set. + String val = TKit.getConfigProperty("runtime-image"); + if (val != null) { + return Path.of(val); + } + return null; + }).get(); +} --- /dev/null 2019-11-18 21:26:27.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JarBuilder.java 2019-11-18 21:26:24.018798600 -0500 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.test; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import java.util.ArrayList; +import java.util.List; + + +/** + * Tool to compile Java sources and pack in a jar file. + */ +public final class JarBuilder { + + public JarBuilder() { + sourceFiles = new ArrayList<>(); + } + + public JarBuilder setOutputJar(Path v) { + outputJar = v; + return this; + } + + public JarBuilder setMainClass(String v) { + mainClass = v; + return this; + } + + public JarBuilder addSourceFile(Path v) { + sourceFiles.add(v); + return this; + } + + public JarBuilder setModuleVersion(String v) { + moduleVersion = v; + return this; + } + + public void create() { + TKit.withTempDirectory("jar-workdir", workDir -> { + if (!sourceFiles.isEmpty()) { + new Executor() + .setToolProvider(JavaTool.JAVAC) + .addArguments("-d", workDir.toString()) + .addPathArguments(sourceFiles) + .execute().assertExitCodeIsZero(); + } + + Files.createDirectories(outputJar.getParent()); + if (Files.exists(outputJar)) { + TKit.trace(String.format("Delete [%s] existing jar file", outputJar)); + Files.deleteIfExists(outputJar); + } + + Executor jarExe = new Executor() + .setToolProvider(JavaTool.JAR) + .addArguments("-c", "-f", outputJar.toString()); + if (moduleVersion != null) { + jarExe.addArguments(String.format("--module-version=%s", + moduleVersion)); + } + if (mainClass != null) { + jarExe.addArguments("-e", mainClass); + } + jarExe.addArguments("-C", workDir.toString(), "."); + jarExe.execute().assertExitCodeIsZero(); + }); + } + private List sourceFiles; + private Path outputJar; + private String mainClass; + private String moduleVersion; +} --- /dev/null 2019-11-18 21:26:37.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaAppDesc.java 2019-11-18 21:26:34.635347600 -0500 @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.File; +import java.nio.file.Path; + + +public final class JavaAppDesc { + public JavaAppDesc() { + } + + public JavaAppDesc setClassName(String v) { + qualifiedClassName = v; + return this; + } + + public JavaAppDesc setModuleName(String v) { + moduleName = v; + return this; + } + + public JavaAppDesc setJarFileName(String v) { + jarFileName = v; + return this; + } + + public JavaAppDesc setModuleVersion(String v) { + moduleVersion = v; + return this; + } + + public JavaAppDesc setJarWithMainClass(boolean v) { + jarWithMainClass = v; + return this; + } + + public String className() { + return qualifiedClassName; + } + + public Path classFilePath() { + return Path.of(qualifiedClassName.replace(".", File.separator) + + ".class"); + } + + public String moduleName() { + return moduleName; + } + + public String packageName() { + int lastDotIdx = qualifiedClassName.lastIndexOf('.'); + if (lastDotIdx == -1) { + return null; + } + return qualifiedClassName.substring(0, lastDotIdx); + } + + public String jarFileName() { + return jarFileName; + } + + public String moduleVersion() { + return moduleVersion; + } + + public boolean jarWithMainClass() { + return jarWithMainClass; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (jarFileName != null) { + sb.append(jarFileName).append(':'); + } + if (moduleName != null) { + sb.append(moduleName).append('/'); + } + if (qualifiedClassName != null) { + sb.append(qualifiedClassName); + } + if (jarWithMainClass) { + sb.append('!'); + } + if (moduleVersion != null) { + sb.append('@').append(moduleVersion); + } + return sb.toString(); + } + + /** + * Create Java application description form encoded string value. + * + * Syntax of encoded Java application description is + * [jar_file:][module_name/]qualified_class_name[!][@module_version]. + * + * E.g.: `duke.jar:com.other/com.other.foo.bar.Buz!@3.7` encodes modular + * application. Module name is `com.other`. Main class is + * `com.other.foo.bar.Buz`. Module version is `3.7`. Application will be + * compiled and packed in `duke.jar` jar file. jar command will set module + * version (3.7) and main class (Buz) attributes in the jar file. + * + * E.g.: `Ciao` encodes non-modular `Ciao` class in the default package. + * jar command will not put main class attribute in the jar file. + * Default name will be picked for jar file - `hello.jar`. + * + * @param cmd jpackage command to configure + * @param javaAppDesc encoded Java application description + */ + public static JavaAppDesc parse(String javaAppDesc) { + JavaAppDesc desc = HelloApp.createDefaltAppDesc(); + + if (javaAppDesc == null) { + return desc; + } + + String moduleNameAndOther = Functional.identity(() -> { + String[] components = javaAppDesc.split(":", 2); + if (components.length == 2) { + desc.setJarFileName(components[0]); + } + return components[components.length - 1]; + }).get(); + + String classNameAndOther = Functional.identity(() -> { + String[] components = moduleNameAndOther.split("/", 2); + if (components.length == 2) { + desc.setModuleName(components[0]); + } + return components[components.length - 1]; + }).get(); + + Functional.identity(() -> { + String[] components = classNameAndOther.split("@", 2); + if (components[0].endsWith("!")) { + components[0] = components[0].substring(0, + components[0].length() - 1); + desc.setJarWithMainClass(true); + } + desc.setClassName(components[0]); + if (components.length == 2) { + desc.setModuleVersion(components[1]); + } + }).run(); + + return desc; + } + + private String qualifiedClassName; + private String moduleName; + private String jarFileName; + private String moduleVersion; + private boolean jarWithMainClass; +} --- /dev/null 2019-11-18 21:26:48.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaTool.java 2019-11-18 21:26:45.296293800 -0500 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package jdk.jpackage.test; + + +import java.nio.file.Path; +import java.util.spi.ToolProvider; +import jdk.incubator.jpackage.ToolProviderFactory; + +public enum JavaTool { + JAVA("java"), JAVAC("javac"), JPACKAGE("jpackage"), JAR("jar"), JLINK("jlink"); + + JavaTool(String name) { + this.name = name; + this.path = Path.of(System.getProperty("java.home")).resolve( + relativePathInJavaHome()).toAbsolutePath().normalize(); + if (!path.toFile().exists()) { + throw new RuntimeException(String.format( + "Unable to find tool [%s] at path=[%s]", name, path)); + } + } + + Path getPath() { + return path; + } + + public ToolProvider asToolProvider() { + if (this == JPACKAGE) { + return ToolProviderFactory.findFirst("jpackage").orElseThrow( + () -> new RuntimeException("jpackage tool not found")); + } else { + return ToolProvider.findFirst(name).orElse(null); + } + } + + Path relativePathInJavaHome() { + Path path = Path.of("bin", name); + if (TKit.isWindows()) { + path = path.getParent().resolve(path.getFileName().toString() + ".exe"); + } + return path; + } + + private Path path; + private String name; +} --- /dev/null 2019-11-18 21:26:58.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java 2019-11-18 21:26:55.872331200 -0500 @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class LinuxHelper { + private static String getRelease(JPackageCommand cmd) { + return cmd.getArgumentValue("--linux-app-release", () -> "1"); + } + + public static String getPackageName(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.LINUX); + return cmd.getArgumentValue("--linux-package-name", + () -> cmd.name().toLowerCase()); + } + + static String getBundleName(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.LINUX); + + final PackageType packageType = cmd.packageType(); + String format = null; + switch (packageType) { + case LINUX_DEB: + format = "%s_%s-%s_%s"; + break; + + case LINUX_RPM: + format = "%s-%s-%s.%s"; + break; + } + + final String release = getRelease(cmd); + final String version = cmd.version(); + + return String.format(format, getPackageName(cmd), version, release, + getDefaultPackageArch(packageType)) + packageType.getSuffix(); + } + + public static Stream getPackageFiles(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.LINUX); + + final PackageType packageType = cmd.packageType(); + final Path packageFile = cmd.outputBundle(); + + Executor exec = new Executor(); + switch (packageType) { + case LINUX_DEB: + exec.setExecutable("dpkg") + .addArgument("--contents") + .addArgument(packageFile); + break; + + case LINUX_RPM: + exec.setExecutable("rpm") + .addArgument("-qpl") + .addArgument(packageFile); + break; + } + + Stream lines = exec.executeAndGetOutput().stream(); + if (packageType == PackageType.LINUX_DEB) { + // Typical text lines produced by dpkg look like: + // drwxr-xr-x root/root 0 2019-08-30 05:30 ./opt/appcategorytest/runtime/lib/ + // -rw-r--r-- root/root 574912 2019-08-30 05:30 ./opt/appcategorytest/runtime/lib/libmlib_image.so + // Need to skip all fields but absolute path to file. + lines = lines.map(line -> line.substring(line.indexOf(" ./") + 2)); + } + return lines.map(Path::of); + } + + public static List getPrerequisitePackages(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.LINUX); + var packageType = cmd.packageType(); + switch (packageType) { + case LINUX_DEB: + return Stream.of(getDebBundleProperty(cmd.outputBundle(), + "Depends").split(",")).map(String::strip).collect( + Collectors.toList()); + + case LINUX_RPM: + return new Executor().setExecutable("rpm") + .addArguments("-qp", "-R", cmd.outputBundle().toString()) + .executeAndGetOutput(); + } + // Unreachable + return null; + } + + public static String getBundleProperty(JPackageCommand cmd, + String propertyName) { + return getBundleProperty(cmd, + Map.of(PackageType.LINUX_DEB, propertyName, + PackageType.LINUX_RPM, propertyName)); + } + + public static String getBundleProperty(JPackageCommand cmd, + Map propertyName) { + cmd.verifyIsOfType(PackageType.LINUX); + var packageType = cmd.packageType(); + switch (packageType) { + case LINUX_DEB: + return getDebBundleProperty(cmd.outputBundle(), propertyName.get( + packageType)); + + case LINUX_RPM: + return getRpmBundleProperty(cmd.outputBundle(), propertyName.get( + packageType)); + } + // Unrechable + return null; + } + + static Path getLauncherPath(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.LINUX); + + final String launcherName = cmd.name(); + final String launcherRelativePath = Path.of("/bin", launcherName).toString(); + + return getPackageFiles(cmd).filter(path -> path.toString().endsWith( + launcherRelativePath)).findFirst().or(() -> { + TKit.assertUnexpected(String.format( + "Failed to find %s in %s package", launcherName, + getPackageName(cmd))); + return null; + }).get(); + } + + static long getInstalledPackageSizeKB(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.LINUX); + + final Path packageFile = cmd.outputBundle(); + switch (cmd.packageType()) { + case LINUX_DEB: + return Long.parseLong(getDebBundleProperty(packageFile, + "Installed-Size")); + + case LINUX_RPM: + return Long.parseLong(getRpmBundleProperty(packageFile, "Size")) >> 10; + } + + return 0; + } + + static String getDebBundleProperty(Path bundle, String fieldName) { + return new Executor() + .setExecutable("dpkg-deb") + .addArguments("-f", bundle.toString(), fieldName) + .executeAndGetFirstLineOfOutput(); + } + + static String getRpmBundleProperty(Path bundle, String fieldName) { + return new Executor() + .setExecutable("rpm") + .addArguments( + "-qp", + "--queryformat", + String.format("%%{%s}", fieldName), + bundle.toString()) + .executeAndGetFirstLineOfOutput(); + } + + static void verifyPackageBundleEssential(JPackageCommand cmd) { + String packageName = LinuxHelper.getPackageName(cmd); + TKit.assertNotEquals(0L, LinuxHelper.getInstalledPackageSizeKB( + cmd), String.format( + "Check installed size of [%s] package in KB is not zero", + packageName)); + + final boolean checkPrerequisites; + if (cmd.isRuntime()) { + Path runtimeDir = cmd.appRuntimeDirectory(); + Set expectedCriticalRuntimePaths = CRITICAL_RUNTIME_FILES.stream().map( + runtimeDir::resolve).collect(Collectors.toSet()); + Set actualCriticalRuntimePaths = getPackageFiles(cmd).filter( + expectedCriticalRuntimePaths::contains).collect( + Collectors.toSet()); + checkPrerequisites = expectedCriticalRuntimePaths.equals( + actualCriticalRuntimePaths); + } else { + checkPrerequisites = true; + } + + List prerequisites = LinuxHelper.getPrerequisitePackages(cmd); + if (checkPrerequisites) { + final String vitalPackage = "libc"; + TKit.assertTrue(prerequisites.stream().filter( + dep -> dep.contains(vitalPackage)).findAny().isPresent(), + String.format( + "Check [%s] package is in the list of required packages %s of [%s] package", + vitalPackage, prerequisites, packageName)); + } else { + TKit.trace(String.format( + "Not cheking %s required packages of [%s] package", + prerequisites, packageName)); + } + } + + static void addBundleDesktopIntegrationVerifier(PackageTest test, + boolean integrated) { + final String xdgUtils = "xdg-utils"; + + test.addBundleVerifier(cmd -> { + List prerequisites = getPrerequisitePackages(cmd); + boolean xdgUtilsFound = prerequisites.contains(xdgUtils); + if (integrated) { + TKit.assertTrue(xdgUtilsFound, String.format( + "Check [%s] is in the list of required packages %s", + xdgUtils, prerequisites)); + } else { + TKit.assertFalse(xdgUtilsFound, String.format( + "Check [%s] is NOT in the list of required packages %s", + xdgUtils, prerequisites)); + } + }); + + test.forTypes(PackageType.LINUX_DEB, () -> { + addDebBundleDesktopIntegrationVerifier(test, integrated); + }); + } + + private static void addDebBundleDesktopIntegrationVerifier(PackageTest test, + boolean integrated) { + Function, String> verifier = (lines) -> { + // Lookup for xdg commands + return lines.stream().filter(line -> { + Set words = Stream.of(line.split("\\s+")).collect( + Collectors.toSet()); + return words.contains("xdg-desktop-menu") || words.contains( + "xdg-mime") || words.contains("xdg-icon-resource"); + }).findFirst().orElse(null); + }; + + test.addBundleVerifier(cmd -> { + TKit.withTempDirectory("dpkg-control-files", tempDir -> { + // Extract control Debian package files into temporary directory + new Executor() + .setExecutable("dpkg") + .addArguments( + "-e", + cmd.outputBundle().toString(), + tempDir.toString() + ).execute().assertExitCodeIsZero(); + + Path controlFile = Path.of("postinst"); + + // Lookup for xdg commands in postinstall script + String lineWithXsdCommand = verifier.apply( + Files.readAllLines(tempDir.resolve(controlFile))); + String assertMsg = String.format( + "Check if %s@%s control file uses xdg commands", + cmd.outputBundle(), controlFile); + if (integrated) { + TKit.assertNotNull(lineWithXsdCommand, assertMsg); + } else { + TKit.assertNull(lineWithXsdCommand, assertMsg); + } + }); + }); + } + + static void initFileAssociationsTestFile(Path testFile) { + try { + // Write something in test file. + // On Ubuntu and Oracle Linux empty files are considered + // plain text. Seems like a system bug. + // + // $ >foo.jptest1 + // $ xdg-mime query filetype foo.jptest1 + // text/plain + // $ echo > foo.jptest1 + // $ xdg-mime query filetype foo.jptest1 + // application/x-jpackage-jptest1 + // + Files.write(testFile, Arrays.asList("")); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private static Path getSystemDesktopFilesFolder() { + return Stream.of("/usr/share/applications", + "/usr/local/share/applications").map(Path::of).filter(dir -> { + return Files.exists(dir.resolve("defaults.list")); + }).findFirst().orElseThrow(() -> new RuntimeException( + "Failed to locate system .desktop files folder")); + } + + static void addFileAssociationsVerifier(PackageTest test, FileAssociations fa) { + test.addInstallVerifier(cmd -> { + PackageTest.withTestFileAssociationsFile(fa, testFile -> { + String mimeType = queryFileMimeType(testFile); + + TKit.assertEquals(fa.getMime(), mimeType, String.format( + "Check mime type of [%s] file", testFile)); + + String desktopFileName = queryMimeTypeDefaultHandler(mimeType); + + Path desktopFile = getSystemDesktopFilesFolder().resolve( + desktopFileName); + + TKit.assertFileExists(desktopFile); + + TKit.trace(String.format("Reading [%s] file...", desktopFile)); + String mimeHandler = Files.readAllLines(desktopFile).stream().peek( + v -> TKit.trace(v)).filter( + v -> v.startsWith("Exec=")).map( + v -> v.split("=", 2)[1]).findFirst().orElseThrow(); + + TKit.trace(String.format("Done")); + + TKit.assertEquals(cmd.appLauncherPath().toString(), + mimeHandler, String.format( + "Check mime type handler is the main application launcher")); + + }); + }); + + test.addUninstallVerifier(cmd -> { + PackageTest.withTestFileAssociationsFile(fa, testFile -> { + String mimeType = queryFileMimeType(testFile); + + TKit.assertNotEquals(fa.getMime(), mimeType, String.format( + "Check mime type of [%s] file", testFile)); + + String desktopFileName = queryMimeTypeDefaultHandler(fa.getMime()); + + TKit.assertNull(desktopFileName, String.format( + "Check there is no default handler for [%s] mime type", + fa.getMime())); + }); + }); + } + + private static String queryFileMimeType(Path file) { + return new Executor() + .setExecutable("xdg-mime") + .addArguments("query", "filetype", file.toString()) + .executeAndGetFirstLineOfOutput(); + } + + private static String queryMimeTypeDefaultHandler(String mimeType) { + return new Executor() + .setExecutable("xdg-mime") + .addArguments("query", "default", mimeType) + .executeAndGetFirstLineOfOutput(); + } + + public static String getDefaultPackageArch(PackageType type) { + if (archs == null) { + archs = new HashMap<>(); + } + + String arch = archs.get(type); + if (arch == null) { + Executor exec = new Executor(); + switch (type) { + case LINUX_DEB: + exec.setExecutable("dpkg").addArgument( + "--print-architecture"); + break; + + case LINUX_RPM: + exec.setExecutable("rpmbuild").addArgument( + "--eval=%{_target_cpu}"); + break; + } + arch = exec.executeAndGetFirstLineOfOutput(); + archs.put(type, arch); + } + return arch; + } + + static final Set CRITICAL_RUNTIME_FILES = Set.of(Path.of( + "lib/server/libjvm.so")); + + static private Map archs; +} --- /dev/null 2019-11-18 21:27:09.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java 2019-11-18 21:27:06.376977200 -0500 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; +import jdk.jpackage.test.Functional.ThrowingConsumer; +import jdk.jpackage.test.Functional.ThrowingSupplier; +import org.xml.sax.SAXException; + +public class MacHelper { + + public static void withExplodedDmg(JPackageCommand cmd, + ThrowingConsumer consumer) { + cmd.verifyIsOfType(PackageType.MAC_DMG); + + var plist = readPList(new Executor() + .setExecutable("/usr/bin/hdiutil") + .dumpOutput() + .addArguments("attach", cmd.outputBundle().toString(), "-plist") + .executeAndGetOutput()); + + final Path mountPoint = Path.of(plist.queryValue("mount-point")); + try { + Path dmgImage = mountPoint.resolve(cmd.name() + ".app"); + TKit.trace(String.format("Exploded [%s] in [%s] directory", + cmd.outputBundle(), dmgImage)); + ThrowingConsumer.toConsumer(consumer).accept(dmgImage); + } finally { + new Executor() + .setExecutable("/usr/bin/hdiutil") + .addArgument("detach").addArgument(mountPoint) + .execute().assertExitCodeIsZero(); + } + } + + public static PListWrapper readPListFromAppImage(Path appImage) { + return readPList(appImage.resolve("Contents/Info.plist")); + } + + public static PListWrapper readPList(Path path) { + TKit.assertReadableFileExists(path); + return ThrowingSupplier.toSupplier(() -> readPList(Files.readAllLines( + path))).get(); + } + + public static PListWrapper readPList(List lines) { + return readPList(lines.stream()); + } + + public static PListWrapper readPList(Stream lines) { + return ThrowingSupplier.toSupplier(() -> new PListWrapper(lines.collect( + Collectors.joining()))).get(); + } + + static String getBundleName(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.MAC); + return String.format("%s-%s%s", getPackageName(cmd), cmd.version(), + cmd.packageType().getSuffix()); + } + + static Path getInstallationDirectory(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.MAC); + return Path.of(cmd.getArgumentValue("--install-dir", () -> "/Applications")) + .resolve(cmd.name() + ".app"); + } + + private static String getPackageName(JPackageCommand cmd) { + return cmd.getArgumentValue("--mac-package-name", + () -> cmd.name()); + } + + public static final class PListWrapper { + public String queryValue(String keyName) { + XPath xPath = XPathFactory.newInstance().newXPath(); + // Query for the value of element preceding element + // with value equal to `keyName` + String query = String.format( + "//string[preceding-sibling::key = \"%s\"][1]", keyName); + return ThrowingSupplier.toSupplier(() -> (String) xPath.evaluate( + query, doc, XPathConstants.STRING)).get(); + } + + PListWrapper(String xml) throws ParserConfigurationException, + SAXException, IOException { + doc = createDocumentBuilder().parse(new ByteArrayInputStream( + xml.getBytes(StandardCharsets.UTF_8))); + } + + private static DocumentBuilder createDocumentBuilder() throws + ParserConfigurationException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultInstance(); + dbf.setFeature( + "http://apache.org/xml/features/nonvalidating/load-external-dtd", + false); + return dbf.newDocumentBuilder(); + } + + private final org.w3c.dom.Document doc; + } + + static final Set CRITICAL_RUNTIME_FILES = Set.of(Path.of( + "Contents/Home/lib/server/libjvm.dylib")); + +} --- /dev/null 2019-11-18 21:27:20.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java 2019-11-18 21:27:17.196181000 -0500 @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.test; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import static jdk.jpackage.test.TestBuilder.CMDLINE_ARG_PREFIX; + + +public final class Main { + public static void main(String args[]) throws Throwable { + boolean listTests = false; + List tests = new ArrayList<>(); + try (TestBuilder testBuilder = new TestBuilder(tests::add)) { + for (var arg : args) { + TestBuilder.trace(String.format("Parsing [%s]...", arg)); + + if ((CMDLINE_ARG_PREFIX + "list").equals(arg)) { + listTests = true; + continue; + } + + boolean success = false; + try { + testBuilder.processCmdLineArg(arg); + success = true; + } catch (Throwable throwable) { + TKit.unbox(throwable); + } finally { + if (!success) { + TKit.log( + String.format("Error processing parameter=[%s]", + arg)); + } + } + } + } + + // Order tests by their full names to have stable test sequence. + List orderedTests = tests.stream() + .sorted((a, b) -> a.fullName().compareTo(b.fullName())) + .collect(Collectors.toList()); + + if (listTests) { + // Just list the tests + orderedTests.stream().forEach(test -> System.out.println(String.format( + "%s; workDir=[%s]", test.fullName(), test.workDir()))); + return; + } + + TKit.withExtraLogStream(() -> runTests(orderedTests)); + } + + private static void runTests(List tests) { + TKit.runTests(tests); + + final long passedCount = tests.stream().filter(TestInstance::passed).count(); + TKit.log(String.format("[==========] %d tests ran", tests.size())); + TKit.log(String.format("[ PASSED ] %d %s", passedCount, + passedCount == 1 ? "test" : "tests")); + + reportDetails(tests, "[ SKIPPED ]", TestInstance::skipped, false); + reportDetails(tests, "[ FAILED ]", TestInstance::failed, true); + + var withSkipped = reportSummary(tests, "SKIPPED", TestInstance::skipped); + var withFailures = reportSummary(tests, "FAILED", TestInstance::failed); + + if (withFailures != null) { + throw withFailures; + } + + if (withSkipped != null) { + tests.stream().filter(TestInstance::skipped).findFirst().get().rethrowIfSkipped(); + } + } + + private static long reportDetails(List tests, + String label, Predicate selector, boolean printWorkDir) { + + final Function makeMessage = test -> { + if (printWorkDir) { + return String.format("%s %s; workDir=[%s]", label, + test.fullName(), test.workDir()); + } + return String.format("%s %s", label, test.fullName()); + }; + + final long count = tests.stream().filter(selector).count(); + if (count != 0) { + TKit.log(String.format("%s %d %s, listed below", label, count, count + == 1 ? "test" : "tests")); + tests.stream().filter(selector).map(makeMessage).forEachOrdered( + TKit::log); + } + + return count; + } + + private static RuntimeException reportSummary(List tests, + String label, Predicate selector) { + final long count = tests.stream().filter(selector).count(); + if (count != 0) { + final String message = String.format("%d %s %s", count, label, count + == 1 ? "TEST" : "TESTS"); + TKit.log(message); + return new RuntimeException(message); + } + + return null; + } +} --- /dev/null 2019-11-18 21:27:30.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MethodCall.java 2019-11-18 21:27:27.569242800 -0500 @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.Functional.ThrowingConsumer; +import jdk.jpackage.test.TestInstance.TestDesc; + +class MethodCall implements ThrowingConsumer { + + MethodCall(Object[] instanceCtorArgs, Method method) { + this.ctorArgs = Optional.ofNullable(instanceCtorArgs).orElse( + DEFAULT_CTOR_ARGS); + this.method = method; + this.methodArgs = new Object[0]; + } + + MethodCall(Object[] instanceCtorArgs, Method method, Object arg) { + this.ctorArgs = Optional.ofNullable(instanceCtorArgs).orElse( + DEFAULT_CTOR_ARGS); + this.method = method; + this.methodArgs = new Object[]{arg}; + } + + TestDesc createDescription() { + var descBuilder = TestDesc.createBuilder().method(method); + if (methodArgs.length != 0) { + descBuilder.methodArgs(methodArgs); + } + + if (ctorArgs.length != 0) { + descBuilder.ctorArgs(ctorArgs); + } + + return descBuilder.get(); + } + + Method getMethod() { + return method; + } + + Object newInstance() throws NoSuchMethodException, InstantiationException, + IllegalAccessException, IllegalArgumentException, + InvocationTargetException { + if ((method.getModifiers() & Modifier.STATIC) != 0) { + return null; + } + + Constructor ctor = findRequiredConstructor(method.getDeclaringClass(), + ctorArgs); + if (ctor.isVarArgs()) { + // Assume constructor doesn't have fixed, only variable parameters. + return ctor.newInstance(new Object[]{ctorArgs}); + } + + return ctor.newInstance(ctorArgs); + } + + void checkRequiredConstructor() throws NoSuchMethodException { + if ((method.getModifiers() & Modifier.STATIC) == 0) { + findRequiredConstructor(method.getDeclaringClass(), ctorArgs); + } + } + + private static Constructor findVarArgConstructor(Class type) { + return Stream.of(type.getConstructors()).filter( + Constructor::isVarArgs).findFirst().orElse(null); + } + + private Constructor findRequiredConstructor(Class type, Object... ctorArgs) + throws NoSuchMethodException { + + Supplier notFoundException = () -> { + return new NoSuchMethodException(String.format( + "No public contructor in %s for %s arguments", type, + Arrays.deepToString(ctorArgs))); + }; + + if (Stream.of(ctorArgs).allMatch(Objects::nonNull)) { + // No `null` in constructor args, take easy path + try { + return type.getConstructor(Stream.of(ctorArgs).map( + Object::getClass).collect(Collectors.toList()).toArray( + Class[]::new)); + } catch (NoSuchMethodException ex) { + // Failed to find ctor that can take the given arguments. + Constructor varArgCtor = findVarArgConstructor(type); + if (varArgCtor != null) { + // There is one with variable number of arguments. Use it. + return varArgCtor; + } + throw notFoundException.get(); + } + } + + List ctors = Stream.of(type.getConstructors()) + .filter(ctor -> ctor.getParameterCount() == ctorArgs.length) + .collect(Collectors.toList()); + + if (ctors.isEmpty()) { + // No public constructors that can handle the given arguments. + throw notFoundException.get(); + } + + if (ctors.size() == 1) { + return ctors.iterator().next(); + } + + // Revisit this tricky case when it will start bothering. + throw notFoundException.get(); + } + + @Override + public void accept(Object thiz) throws Throwable { + method.invoke(thiz, methodArgs); + } + + private final Object[] methodArgs; + private final Method method; + private final Object[] ctorArgs; + + final static Object[] DEFAULT_CTOR_ARGS = new Object[0]; +} --- /dev/null 2019-11-18 21:27:40.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java 2019-11-18 21:27:37.994370200 -0500 @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.awt.Desktop; +import java.awt.GraphicsEnvironment; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.Functional.ThrowingConsumer; +import jdk.incubator.jpackage.internal.AppImageFile; +import static jdk.jpackage.test.PackageType.*; + +/** + * Instance of PackageTest is for configuring and running a single jpackage + * command to produce platform specific package bundle. + * + * Provides methods to hook up custom configuration of jpackage command and + * verification of the output bundle. + */ +public final class PackageTest { + + /** + * Default test configuration for jpackage command. Default jpackage command + * initialization includes: + *
  • Set --input and --dest parameters. + *
  • Set --name parameter. Value of the parameter is the name of the first + * class with main function found in the callers stack. Defaults can be + * overridden with custom initializers set with subsequent addInitializer() + * function calls. + */ + public PackageTest() { + action = DEFAULT_ACTION; + excludeTypes = new HashSet<>(); + forTypes(); + setExpectedExitCode(0); + handlers = new HashMap<>(); + namedInitializers = new HashSet<>(); + currentTypes.forEach(v -> handlers.put(v, new Handler(v))); + } + + public PackageTest excludeTypes(PackageType... types) { + excludeTypes.addAll(Stream.of(types).collect(Collectors.toSet())); + return forTypes(currentTypes); + } + + public PackageTest excludeTypes(Collection types) { + return excludeTypes(types.toArray(PackageType[]::new)); + } + + public PackageTest forTypes(PackageType... types) { + Collection newTypes; + if (types == null || types.length == 0) { + newTypes = PackageType.NATIVE; + } else { + newTypes = Stream.of(types).collect(Collectors.toSet()); + } + currentTypes = newTypes.stream() + .filter(PackageType::isSupported) + .filter(Predicate.not(excludeTypes::contains)) + .collect(Collectors.toUnmodifiableSet()); + return this; + } + + public PackageTest forTypes(Collection types) { + return forTypes(types.toArray(PackageType[]::new)); + } + + public PackageTest notForTypes(PackageType... types) { + return notForTypes(List.of(types)); + } + + public PackageTest notForTypes(Collection types) { + Set workset = new HashSet<>(currentTypes); + workset.removeAll(types); + return forTypes(workset); + } + + public PackageTest setExpectedExitCode(int v) { + expectedJPackageExitCode = v; + return this; + } + + private PackageTest addInitializer(ThrowingConsumer v, + String id) { + if (id != null) { + if (namedInitializers.contains(id)) { + return this; + } + + namedInitializers.add(id); + } + currentTypes.stream().forEach(type -> handlers.get(type).addInitializer( + ThrowingConsumer.toConsumer(v))); + return this; + } + + public PackageTest addInitializer(ThrowingConsumer v) { + return addInitializer(v, null); + } + + public PackageTest addBundleVerifier( + BiConsumer v) { + currentTypes.stream().forEach( + type -> handlers.get(type).addBundleVerifier(v)); + return this; + } + + public PackageTest addBundleVerifier(ThrowingConsumer v) { + return addBundleVerifier( + (cmd, unused) -> ThrowingConsumer.toConsumer(v).accept(cmd)); + } + + public PackageTest addBundlePropertyVerifier(String propertyName, + BiConsumer pred) { + return addBundleVerifier(cmd -> { + pred.accept(propertyName, + LinuxHelper.getBundleProperty(cmd, propertyName)); + }); + } + + public PackageTest addBundlePropertyVerifier(String propertyName, + String expectedPropertyValue) { + return addBundlePropertyVerifier(propertyName, (unused, v) -> { + TKit.assertEquals(expectedPropertyValue, v, String.format( + "Check value of %s property is [%s]", propertyName, v)); + }); + } + + public PackageTest addBundleDesktopIntegrationVerifier(boolean integrated) { + forTypes(LINUX, () -> { + LinuxHelper.addBundleDesktopIntegrationVerifier(this, integrated); + }); + return this; + } + + public PackageTest addInstallVerifier(ThrowingConsumer v) { + currentTypes.stream().forEach( + type -> handlers.get(type).addInstallVerifier( + ThrowingConsumer.toConsumer(v))); + return this; + } + + public PackageTest addUninstallVerifier(ThrowingConsumer v) { + currentTypes.stream().forEach( + type -> handlers.get(type).addUninstallVerifier( + ThrowingConsumer.toConsumer(v))); + return this; + } + + static void withTestFileAssociationsFile(FileAssociations fa, + ThrowingConsumer consumer) { + final String testFileDefaultName = String.join(".", "test", + fa.getSuffix()); + TKit.withTempFile(testFileDefaultName, fa.getSuffix(), testFile -> { + if (TKit.isLinux()) { + LinuxHelper.initFileAssociationsTestFile(testFile); + } + consumer.accept(testFile); + }); + } + + PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa, + String... faLauncherDefaultArgs) { + + // Setup test app to have valid jpackage command line before + // running check of type of environment. + addInitializer(cmd -> new HelloApp(null).addTo(cmd), "HelloApp"); + + String noActionMsg = "Not running file associations test"; + if (GraphicsEnvironment.isHeadless()) { + TKit.trace(String.format( + "%s because running in headless environment", noActionMsg)); + return this; + } + + addInstallVerifier(cmd -> { + if (cmd.isFakeRuntime(noActionMsg)) { + return; + } + + withTestFileAssociationsFile(fa, testFile -> { + testFile = testFile.toAbsolutePath().normalize(); + + final Path appOutput = testFile.getParent() + .resolve(HelloApp.OUTPUT_FILENAME); + Files.deleteIfExists(appOutput); + + TKit.trace(String.format("Use desktop to open [%s] file", + testFile)); + Desktop.getDesktop().open(testFile.toFile()); + TKit.waitForFileCreated(appOutput, 7); + + List expectedArgs = new ArrayList<>(List.of( + faLauncherDefaultArgs)); + expectedArgs.add(testFile.toString()); + + // Wait a little bit after file has been created to + // make sure there are no pending writes into it. + Thread.sleep(3000); + HelloApp.verifyOutputFile(appOutput, expectedArgs); + }); + }); + + forTypes(PackageType.LINUX, () -> { + LinuxHelper.addFileAssociationsVerifier(this, fa); + }); + + return this; + } + + PackageTest forTypes(Collection types, Runnable action) { + Set oldTypes = Set.of(currentTypes.toArray( + PackageType[]::new)); + try { + forTypes(types); + action.run(); + } finally { + forTypes(oldTypes); + } + return this; + } + + PackageTest forTypes(PackageType type, Runnable action) { + return forTypes(List.of(type), action); + } + + PackageTest notForTypes(Collection types, Runnable action) { + Set workset = new HashSet<>(currentTypes); + workset.removeAll(types); + return forTypes(workset, action); + } + + PackageTest notForTypes(PackageType type, Runnable action) { + return notForTypes(List.of(type), action); + } + + public PackageTest configureHelloApp() { + return configureHelloApp(null); + } + + public PackageTest configureHelloApp(String encodedName) { + addInitializer( + cmd -> new HelloApp(JavaAppDesc.parse(encodedName)).addTo(cmd)); + addInstallVerifier(HelloApp::executeLauncherAndVerifyOutput); + return this; + } + + public void run() { + List supportedHandlers = handlers.values().stream() + .filter(entry -> !entry.isVoid()) + .collect(Collectors.toList()); + + if (supportedHandlers.isEmpty()) { + // No handlers with initializers found. Nothing to do. + return; + } + + Supplier initializer = new Supplier<>() { + @Override + public JPackageCommand get() { + JPackageCommand cmd = new JPackageCommand().setDefaultInputOutput(); + if (bundleOutputDir != null) { + cmd.setArgumentValue("--dest", bundleOutputDir.toString()); + } + cmd.setDefaultAppName(); + return cmd; + } + }; + + supportedHandlers.forEach(handler -> handler.accept(initializer.get())); + } + + public PackageTest setAction(Action value) { + action = value; + return this; + } + + public Action getAction() { + return action; + } + + private class Handler implements Consumer { + + Handler(PackageType type) { + if (!PackageType.NATIVE.contains(type)) { + throw new IllegalArgumentException( + "Attempt to configure a test for image packaging"); + } + this.type = type; + initializers = new ArrayList<>(); + bundleVerifiers = new ArrayList<>(); + installVerifiers = new ArrayList<>(); + uninstallVerifiers = new ArrayList<>(); + } + + boolean isVoid() { + return initializers.isEmpty(); + } + + void addInitializer(Consumer v) { + initializers.add(v); + } + + void addBundleVerifier(BiConsumer v) { + bundleVerifiers.add(v); + } + + void addInstallVerifier(Consumer v) { + installVerifiers.add(v); + } + + void addUninstallVerifier(Consumer v) { + uninstallVerifiers.add(v); + } + + @Override + public void accept(JPackageCommand cmd) { + type.applyTo(cmd); + + initializers.stream().forEach(v -> v.accept(cmd)); + cmd.executePrerequisiteActions(); + + switch (action) { + case CREATE: + Executor.Result result = cmd.execute(); + result.assertExitCodeIs(expectedJPackageExitCode); + if (expectedJPackageExitCode == 0) { + TKit.assertFileExists(cmd.outputBundle()); + } else { + TKit.assertPathExists(cmd.outputBundle(), false); + } + verifyPackageBundle(cmd.createImmutableCopy(), result); + break; + + case VERIFY_INSTALL: + if (expectedJPackageExitCode == 0) { + verifyPackageInstalled(cmd.createImmutableCopy()); + } + break; + + case VERIFY_UNINSTALL: + if (expectedJPackageExitCode == 0) { + verifyPackageUninstalled(cmd.createImmutableCopy()); + } + break; + } + } + + private void verifyPackageBundle(JPackageCommand cmd, + Executor.Result result) { + if (expectedJPackageExitCode == 0) { + if (PackageType.LINUX.contains(cmd.packageType())) { + LinuxHelper.verifyPackageBundleEssential(cmd); + } + } + bundleVerifiers.stream().forEach(v -> v.accept(cmd, result)); + } + + private void verifyPackageInstalled(JPackageCommand cmd) { + TKit.trace(String.format("Verify installed: %s", + cmd.getPrintableCommandLine())); + TKit.assertDirectoryExists(cmd.appRuntimeDirectory()); + if (!cmd.isRuntime()) { + TKit.assertExecutableFileExists(cmd.appLauncherPath()); + + if (PackageType.WINDOWS.contains(cmd.packageType())) { + new WindowsHelper.AppVerifier(cmd); + } + } + + TKit.assertPathExists(AppImageFile.getPathInAppImage( + cmd.appInstallationDirectory()), false); + + installVerifiers.stream().forEach(v -> v.accept(cmd)); + } + + private void verifyPackageUninstalled(JPackageCommand cmd) { + TKit.trace(String.format("Verify uninstalled: %s", + cmd.getPrintableCommandLine())); + if (!cmd.isRuntime()) { + TKit.assertPathExists(cmd.appLauncherPath(), false); + + if (PackageType.WINDOWS.contains(cmd.packageType())) { + new WindowsHelper.AppVerifier(cmd); + } + } + + TKit.assertPathExists(cmd.appInstallationDirectory(), false); + + uninstallVerifiers.stream().forEach(v -> v.accept(cmd)); + } + + private final PackageType type; + private final List> initializers; + private final List> bundleVerifiers; + private final List> installVerifiers; + private final List> uninstallVerifiers; + } + + private Collection currentTypes; + private Set excludeTypes; + private int expectedJPackageExitCode; + private Map handlers; + private Set namedInitializers; + private Action action; + + /** + * Test action. + */ + static public enum Action { + /** + * Create bundle. + */ + CREATE, + /** + * Verify bundle installed. + */ + VERIFY_INSTALL, + /** + * Verify bundle uninstalled. + */ + VERIFY_UNINSTALL; + + @Override + public String toString() { + return name().toLowerCase().replace('_', '-'); + } + }; + private final static Action DEFAULT_ACTION; + private final static File bundleOutputDir; + + static { + final String propertyName = "output"; + String val = TKit.getConfigProperty(propertyName); + if (val == null) { + bundleOutputDir = null; + } else { + bundleOutputDir = new File(val).getAbsoluteFile(); + + if (!bundleOutputDir.isDirectory()) { + throw new IllegalArgumentException(String.format( + "Invalid value of %s sytem property: [%s]. Should be existing directory", + TKit.getConfigPropertyName(propertyName), + bundleOutputDir)); + } + } + } + + static { + final String propertyName = "action"; + String action = Optional.ofNullable(TKit.getConfigProperty(propertyName)).orElse( + Action.CREATE.toString()).toLowerCase(); + DEFAULT_ACTION = Stream.of(Action.values()).filter( + a -> a.toString().equals(action)).findFirst().orElseThrow( + () -> new IllegalArgumentException(String.format( + "Unrecognized value of %s property: [%s]", + TKit.getConfigPropertyName(propertyName), action))); + } +} --- /dev/null 2019-11-18 21:27:51.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java 2019-11-18 21:27:48.305340100 -0500 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * jpackage type traits. + */ +public enum PackageType { + WIN_MSI(".msi", + TKit.isWindows() ? "jdk.incubator.jpackage.internal.WinMsiBundler" : null), + WIN_EXE(".exe", + TKit.isWindows() ? "jdk.incubator.jpackage.internal.WinMsiBundler" : null), + LINUX_DEB(".deb", + TKit.isLinux() ? "jdk.incubator.jpackage.internal.LinuxDebBundler" : null), + LINUX_RPM(".rpm", + TKit.isLinux() ? "jdk.incubator.jpackage.internal.LinuxRpmBundler" : null), + MAC_DMG(".dmg", TKit.isOSX() ? "jdk.incubator.jpackage.internal.MacDmgBundler" : null), + MAC_PKG(".pkg", TKit.isOSX() ? "jdk.incubator.jpackage.internal.MacPkgBundler" : null), + IMAGE("app-image", null, null); + + PackageType(String packageName, String bundleSuffix, String bundlerClass) { + name = packageName; + suffix = bundleSuffix; + if (bundlerClass != null && !Inner.DISABLED_PACKAGERS.contains(getName())) { + supported = isBundlerSupported(bundlerClass); + } else { + supported = false; + } + + if (suffix != null && supported) { + TKit.trace(String.format("Bundler %s supported", getName())); + } + } + + PackageType(String bundleSuffix, String bundlerClass) { + this(bundleSuffix.substring(1), bundleSuffix, bundlerClass); + } + + void applyTo(JPackageCommand cmd) { + cmd.addArguments("--type", getName()); + } + + String getSuffix() { + return suffix; + } + + boolean isSupported() { + return supported; + } + + String getName() { + return name; + } + + static PackageType fromSuffix(String packageFilename) { + if (packageFilename != null) { + for (PackageType v : values()) { + if (packageFilename.endsWith(v.getSuffix())) { + return v; + } + } + } + return null; + } + + private static boolean isBundlerSupported(String bundlerClass) { + try { + Class clazz = Class.forName(bundlerClass); + Method supported = clazz.getMethod("supported", boolean.class); + return ((Boolean) supported.invoke( + clazz.getConstructor().newInstance(), true)); + } catch (ClassNotFoundException | IllegalAccessException ex) { + } catch (InstantiationException | NoSuchMethodException + | InvocationTargetException ex) { + Functional.rethrowUnchecked(ex); + } + return false; + } + + private final String name; + private final String suffix; + private final boolean supported; + + public final static Set LINUX = Set.of(LINUX_DEB, LINUX_RPM); + public final static Set WINDOWS = Set.of(WIN_EXE, WIN_MSI); + public final static Set MAC = Set.of(MAC_PKG, MAC_DMG); + public final static Set NATIVE = Stream.concat( + Stream.concat(LINUX.stream(), WINDOWS.stream()), + MAC.stream()).collect(Collectors.toUnmodifiableSet()); + + private final static class Inner { + + private final static Set DISABLED_PACKAGERS = Optional.ofNullable( + TKit.tokenizeConfigProperty("disabledPackagers")).orElse( + Collections.emptySet()); + } +} --- /dev/null 2019-11-18 21:28:02.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java 2019-11-18 21:27:59.232263400 -0500 @@ -0,0 +1,852 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.*; +import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; +import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.Functional.ExceptionBox; +import jdk.jpackage.test.Functional.ThrowingConsumer; +import jdk.jpackage.test.Functional.ThrowingRunnable; +import jdk.jpackage.test.Functional.ThrowingSupplier; + +final public class TKit { + + private static final String OS = System.getProperty("os.name").toLowerCase(); + + public static final Path TEST_SRC_ROOT = Functional.identity(() -> { + Path root = Path.of(System.getProperty("test.src")); + + for (int i = 0; i != 10; ++i) { + if (root.resolve("apps").toFile().isDirectory()) { + return root.toAbsolutePath(); + } + root = root.resolve(".."); + } + + throw new RuntimeException("Failed to locate apps directory"); + }).get(); + + public final static String ICON_SUFFIX = Functional.identity(() -> { + if (isOSX()) { + return ".icns"; + } + + if (isLinux()) { + return ".png"; + } + + if (isWindows()) { + return ".ico"; + } + + throw throwUnknownPlatformError(); + }).get(); + + public static void run(String args[], ThrowingRunnable testBody) { + if (currentTest != null) { + throw new IllegalStateException( + "Unexpeced nested or concurrent Test.run() call"); + } + + TestInstance test = new TestInstance(testBody); + ThrowingRunnable.toRunnable(() -> runTests(List.of(test))).run(); + test.rethrowIfSkipped(); + if (!test.passed()) { + throw new RuntimeException(); + } + } + + static void withExtraLogStream(ThrowingRunnable action) { + if (extraLogStream != null) { + ThrowingRunnable.toRunnable(action).run(); + } else { + try (PrintStream logStream = openLogStream()) { + extraLogStream = logStream; + ThrowingRunnable.toRunnable(action).run(); + } finally { + extraLogStream = null; + } + } + } + + static void runTests(List tests) { + if (currentTest != null) { + throw new IllegalStateException( + "Unexpeced nested or concurrent Test.run() call"); + } + + withExtraLogStream(() -> { + tests.stream().forEach(test -> { + currentTest = test; + try { + ignoreExceptions(test).run(); + } finally { + currentTest = null; + if (extraLogStream != null) { + extraLogStream.flush(); + } + } + }); + }); + } + + static Runnable ignoreExceptions(ThrowingRunnable action) { + return () -> { + try { + try { + action.run(); + } catch (Throwable ex) { + unbox(ex); + } + } catch (Throwable throwable) { + printStackTrace(throwable); + } + }; + } + + static void unbox(Throwable throwable) throws Throwable { + try { + throw throwable; + } catch (ExceptionBox | InvocationTargetException ex) { + unbox(ex.getCause()); + } + } + + public static Path workDir() { + return currentTest.workDir(); + } + + static Path defaultInputDir() { + return workDir().resolve("input"); + } + + static Path defaultOutputDir() { + return workDir().resolve("output"); + } + + static String getCurrentDefaultAppName() { + // Construct app name from swapping and joining test base name + // and test function name. + // Say the test name is `FooTest.testBasic`. Then app name would be `BasicFooTest`. + String appNamePrefix = currentTest.functionName(); + if (appNamePrefix != null && appNamePrefix.startsWith("test")) { + appNamePrefix = appNamePrefix.substring("test".length()); + } + return Stream.of(appNamePrefix, currentTest.baseName()).filter( + v -> v != null && !v.isEmpty()).collect(Collectors.joining()); + } + + public static boolean isWindows() { + return (OS.contains("win")); + } + + public static boolean isOSX() { + return (OS.contains("mac")); + } + + public static boolean isLinux() { + return ((OS.contains("nix") || OS.contains("nux"))); + } + + static void log(String v) { + System.out.println(v); + if (extraLogStream != null) { + extraLogStream.println(v); + } + } + + public static void createTextFile(Path propsFilename, Collection lines) { + createTextFile(propsFilename, lines.stream()); + } + + public static void createTextFile(Path propsFilename, Stream lines) { + trace(String.format("Create [%s] text file...", + propsFilename.toAbsolutePath().normalize())); + ThrowingRunnable.toRunnable(() -> Files.write(propsFilename, + lines.peek(TKit::trace).collect(Collectors.toList()))).run(); + trace("Done"); + } + + public static void createPropertiesFile(Path propsFilename, + Collection> props) { + trace(String.format("Create [%s] properties file...", + propsFilename.toAbsolutePath().normalize())); + ThrowingRunnable.toRunnable(() -> Files.write(propsFilename, + props.stream().map(e -> String.join("=", e.getKey(), + e.getValue())).peek(TKit::trace).collect(Collectors.toList()))).run(); + trace("Done"); + } + + public static void createPropertiesFile(Path propsFilename, + Map.Entry... props) { + createPropertiesFile(propsFilename, List.of(props)); + } + + public static void createPropertiesFile(Path propsFilename, + Map props) { + createPropertiesFile(propsFilename, props.entrySet()); + } + + public static void trace(String v) { + if (TRACE) { + log("TRACE: " + v); + } + } + + private static void traceAssert(String v) { + if (TRACE_ASSERTS) { + log("TRACE: " + v); + } + } + + public static void error(String v) { + log("ERROR: " + v); + throw new AssertionError(v); + } + + private final static String TEMP_FILE_PREFIX = null; + + private static Path createUniqueFileName(String defaultName) { + final String[] nameComponents; + + int separatorIdx = defaultName.lastIndexOf('.'); + final String baseName; + if (separatorIdx == -1) { + baseName = defaultName; + nameComponents = new String[]{baseName}; + } else { + baseName = defaultName.substring(0, separatorIdx); + nameComponents = new String[]{baseName, defaultName.substring( + separatorIdx + 1)}; + } + + final Path basedir = workDir(); + int i = 0; + for (; i < 100; ++i) { + Path path = basedir.resolve(String.join(".", nameComponents)); + if (!path.toFile().exists()) { + return path; + } + nameComponents[0] = String.format("%s.%d", baseName, i); + } + throw new IllegalStateException(String.format( + "Failed to create unique file name from [%s] basename after %d attempts", + baseName, i)); + } + + public static Path createTempDirectory(String role) throws IOException { + if (role == null) { + return Files.createTempDirectory(workDir(), TEMP_FILE_PREFIX); + } + return Files.createDirectory(createUniqueFileName(role)); + } + + public static Path createTempFile(String role, String suffix) throws + IOException { + if (role == null) { + return Files.createTempFile(workDir(), TEMP_FILE_PREFIX, suffix); + } + return Files.createFile(createUniqueFileName(role)); + } + + public static Path withTempFile(String role, String suffix, + ThrowingConsumer action) { + final Path tempFile = ThrowingSupplier.toSupplier(() -> createTempFile( + role, suffix)).get(); + boolean keepIt = true; + try { + ThrowingConsumer.toConsumer(action).accept(tempFile); + keepIt = false; + return tempFile; + } finally { + if (tempFile != null && !keepIt) { + ThrowingRunnable.toRunnable(() -> Files.deleteIfExists(tempFile)).run(); + } + } + } + + public static Path withTempDirectory(String role, + ThrowingConsumer action) { + final Path tempDir = ThrowingSupplier.toSupplier( + () -> createTempDirectory(role)).get(); + boolean keepIt = true; + try { + ThrowingConsumer.toConsumer(action).accept(tempDir); + keepIt = false; + return tempDir; + } finally { + if (tempDir != null && tempDir.toFile().isDirectory() && !keepIt) { + deleteDirectoryRecursive(tempDir, ""); + } + } + } + + private static class DirectoryCleaner implements Consumer { + DirectoryCleaner traceMessage(String v) { + msg = v; + return this; + } + + DirectoryCleaner contentsOnly(boolean v) { + contentsOnly = v; + return this; + } + + @Override + public void accept(Path root) { + if (msg == null) { + if (contentsOnly) { + msg = String.format("Cleaning [%s] directory recursively", + root); + } else { + msg = String.format("Deleting [%s] directory recursively", + root); + } + } + + if (!msg.isEmpty()) { + trace(msg); + } + + List errors = new ArrayList<>(); + try { + final List paths; + if (contentsOnly) { + try (var pathStream = Files.walk(root, 0)) { + paths = pathStream.collect(Collectors.toList()); + } + } else { + paths = List.of(root); + } + + for (var path : paths) { + try (var pathStream = Files.walk(path)) { + pathStream + .sorted(Comparator.reverseOrder()) + .sequential() + .forEachOrdered(file -> { + try { + if (isWindows()) { + Files.setAttribute(file, "dos:readonly", false); + } + Files.delete(file); + } catch (IOException ex) { + errors.add(ex); + } + }); + } + } + + } catch (IOException ex) { + errors.add(ex); + } + errors.forEach(error -> trace(error.toString())); + } + + private String msg; + private boolean contentsOnly; + } + + /** + * Deletes contents of the given directory recursively. Shortcut for + * deleteDirectoryContentsRecursive(path, null) + * + * @param path path to directory to clean + */ + public static void deleteDirectoryContentsRecursive(Path path) { + deleteDirectoryContentsRecursive(path, null); + } + + /** + * Deletes contents of the given directory recursively. If path is not a + * directory, request is silently ignored. + * + * @param path path to directory to clean + * @param msg log message. If null, the default log message is used. If + * empty string, no log message will be saved. + */ + public static void deleteDirectoryContentsRecursive(Path path, String msg) { + if (path.toFile().isDirectory()) { + new DirectoryCleaner().contentsOnly(true).traceMessage(msg).accept( + path); + } + } + + /** + * Deletes the given directory recursively. Shortcut for + * deleteDirectoryRecursive(path, null) + * + * @param path path to directory to delete + */ + public static void deleteDirectoryRecursive(Path path) { + deleteDirectoryRecursive(path, null); + } + + /** + * Deletes the given directory recursively. If path is not a + * directory, request is silently ignored. + * + * @param path path to directory to delete + * @param msg log message. If null, the default log message is used. If + * empty string, no log message will be saved. + */ + public static void deleteDirectoryRecursive(Path path, String msg) { + if (path.toFile().isDirectory()) { + new DirectoryCleaner().traceMessage(msg).accept(path); + } + } + + public static RuntimeException throwUnknownPlatformError() { + if (isWindows() || isLinux() || isOSX()) { + throw new IllegalStateException( + "Platform is known. throwUnknownPlatformError() called by mistake"); + } + throw new IllegalStateException("Unknown platform"); + } + + public static RuntimeException throwSkippedException(String reason) { + trace("Skip the test: " + reason); + RuntimeException ex = ThrowingSupplier.toSupplier( + () -> (RuntimeException) Class.forName("jtreg.SkippedException").getConstructor( + String.class).newInstance(reason)).get(); + + currentTest.notifySkipped(ex); + throw ex; + } + + public static Path createRelativePathCopy(final Path file) { + Path fileCopy = workDir().resolve(file.getFileName()).toAbsolutePath().normalize(); + + ThrowingRunnable.toRunnable(() -> Files.copy(file, fileCopy, + StandardCopyOption.REPLACE_EXISTING)).run(); + + final Path basePath = Path.of(".").toAbsolutePath().normalize(); + try { + return basePath.relativize(fileCopy); + } catch (IllegalArgumentException ex) { + // May happen on Windows: java.lang.IllegalArgumentException: 'other' has different root + trace(String.format("Failed to relativize [%s] at [%s]", fileCopy, + basePath)); + printStackTrace(ex); + } + return file; + } + + static void waitForFileCreated(Path fileToWaitFor, + long timeoutSeconds) throws IOException { + + trace(String.format("Wait for file [%s] to be available", + fileToWaitFor.toAbsolutePath())); + + WatchService ws = FileSystems.getDefault().newWatchService(); + + Path watchDirectory = fileToWaitFor.toAbsolutePath().getParent(); + watchDirectory.register(ws, ENTRY_CREATE, ENTRY_MODIFY); + + long waitUntil = System.currentTimeMillis() + timeoutSeconds * 1000; + for (;;) { + long timeout = waitUntil - System.currentTimeMillis(); + assertTrue(timeout > 0, String.format( + "Check timeout value %d is positive", timeout)); + + WatchKey key = ThrowingSupplier.toSupplier(() -> ws.poll(timeout, + TimeUnit.MILLISECONDS)).get(); + if (key == null) { + if (fileToWaitFor.toFile().exists()) { + trace(String.format( + "File [%s] is available after poll timeout expired", + fileToWaitFor)); + return; + } + assertUnexpected(String.format("Timeout expired", timeout)); + } + + for (WatchEvent event : key.pollEvents()) { + if (event.kind() == StandardWatchEventKinds.OVERFLOW) { + continue; + } + Path contextPath = (Path) event.context(); + if (Files.isSameFile(watchDirectory.resolve(contextPath), + fileToWaitFor)) { + trace(String.format("File [%s] is available", fileToWaitFor)); + return; + } + } + + if (!key.reset()) { + assertUnexpected("Watch key invalidated"); + } + } + } + + static void printStackTrace(Throwable throwable) { + if (extraLogStream != null) { + throwable.printStackTrace(extraLogStream); + } + throwable.printStackTrace(); + } + + private static String concatMessages(String msg, String msg2) { + if (msg2 != null && !msg2.isBlank()) { + return msg + ": " + msg2; + } + return msg; + } + + public static void assertEquals(long expected, long actual, String msg) { + currentTest.notifyAssert(); + if (expected != actual) { + error(concatMessages(String.format( + "Expected [%d]. Actual [%d]", expected, actual), + msg)); + } + + traceAssert(String.format("assertEquals(%d): %s", expected, msg)); + } + + public static void assertNotEquals(long expected, long actual, String msg) { + currentTest.notifyAssert(); + if (expected == actual) { + error(concatMessages(String.format("Unexpected [%d] value", actual), + msg)); + } + + traceAssert(String.format("assertNotEquals(%d, %d): %s", expected, + actual, msg)); + } + + public static void assertEquals(String expected, String actual, String msg) { + currentTest.notifyAssert(); + if ((actual != null && !actual.equals(expected)) + || (expected != null && !expected.equals(actual))) { + error(concatMessages(String.format( + "Expected [%s]. Actual [%s]", expected, actual), + msg)); + } + + traceAssert(String.format("assertEquals(%s): %s", expected, msg)); + } + + public static void assertNotEquals(String expected, String actual, String msg) { + currentTest.notifyAssert(); + if ((actual != null && !actual.equals(expected)) + || (expected != null && !expected.equals(actual))) { + + traceAssert(String.format("assertNotEquals(%s, %s): %s", expected, + actual, msg)); + return; + } + + error(concatMessages(String.format("Unexpected [%s] value", actual), msg)); + } + + public static void assertNull(Object value, String msg) { + currentTest.notifyAssert(); + if (value != null) { + error(concatMessages(String.format("Unexpected not null value [%s]", + value), msg)); + } + + traceAssert(String.format("assertNull(): %s", msg)); + } + + public static void assertNotNull(Object value, String msg) { + currentTest.notifyAssert(); + if (value == null) { + error(concatMessages("Unexpected null value", msg)); + } + + traceAssert(String.format("assertNotNull(%s): %s", value, msg)); + } + + public static void assertTrue(boolean actual, String msg) { + assertTrue(actual, msg, null); + } + + public static void assertFalse(boolean actual, String msg) { + assertFalse(actual, msg, null); + } + + public static void assertTrue(boolean actual, String msg, Runnable onFail) { + currentTest.notifyAssert(); + if (!actual) { + if (onFail != null) { + onFail.run(); + } + error(concatMessages("Failed", msg)); + } + + traceAssert(String.format("assertTrue(): %s", msg)); + } + + public static void assertFalse(boolean actual, String msg, Runnable onFail) { + currentTest.notifyAssert(); + if (actual) { + if (onFail != null) { + onFail.run(); + } + error(concatMessages("Failed", msg)); + } + + traceAssert(String.format("assertFalse(): %s", msg)); + } + + public static void assertPathExists(Path path, boolean exists) { + if (exists) { + assertTrue(path.toFile().exists(), String.format( + "Check [%s] path exists", path)); + } else { + assertFalse(path.toFile().exists(), String.format( + "Check [%s] path doesn't exist", path)); + } + } + + public static void assertDirectoryExists(Path path) { + assertPathExists(path, true); + assertTrue(path.toFile().isDirectory(), String.format( + "Check [%s] is a directory", path)); + } + + public static void assertFileExists(Path path) { + assertPathExists(path, true); + assertTrue(path.toFile().isFile(), String.format("Check [%s] is a file", + path)); + } + + public static void assertExecutableFileExists(Path path) { + assertFileExists(path); + assertTrue(path.toFile().canExecute(), String.format( + "Check [%s] file is executable", path)); + } + + public static void assertReadableFileExists(Path path) { + assertFileExists(path); + assertTrue(path.toFile().canRead(), String.format( + "Check [%s] file is readable", path)); + } + + public static void assertUnexpected(String msg) { + currentTest.notifyAssert(); + error(concatMessages("Unexpected", msg)); + } + + public static void assertStringListEquals(List expected, + List actual, String msg) { + currentTest.notifyAssert(); + + traceAssert(String.format("assertStringListEquals(): %s", msg)); + + String idxFieldFormat = Functional.identity(() -> { + int listSize = expected.size(); + int width = 0; + while (listSize != 0) { + listSize = listSize / 10; + width++; + } + return "%" + width + "d"; + }).get(); + + AtomicInteger counter = new AtomicInteger(0); + Iterator actualIt = actual.iterator(); + expected.stream().sequential().filter(expectedStr -> actualIt.hasNext()).forEach(expectedStr -> { + int idx = counter.incrementAndGet(); + String actualStr = actualIt.next(); + + if ((actualStr != null && !actualStr.equals(expectedStr)) + || (expectedStr != null && !expectedStr.equals(actualStr))) { + error(concatMessages(String.format( + "(" + idxFieldFormat + ") Expected [%s]. Actual [%s]", + idx, expectedStr, actualStr), msg)); + } + + traceAssert(String.format( + "assertStringListEquals(" + idxFieldFormat + ", %s)", idx, + expectedStr)); + }); + + if (expected.size() < actual.size()) { + // Actual string list is longer than expected + error(concatMessages(String.format( + "Actual list is longer than expected by %d elements", + actual.size() - expected.size()), msg)); + } + + if (actual.size() < expected.size()) { + // Actual string list is shorter than expected + error(concatMessages(String.format( + "Actual list is longer than expected by %d elements", + expected.size() - actual.size()), msg)); + } + } + + public final static class TextStreamAsserter { + TextStreamAsserter(String value) { + this.value = value; + predicate(String::contains); + } + + public TextStreamAsserter label(String v) { + label = v; + return this; + } + + public TextStreamAsserter predicate(BiPredicate v) { + predicate = v; + return this; + } + + public TextStreamAsserter negate() { + negate = true; + return this; + } + + public TextStreamAsserter orElseThrow(RuntimeException v) { + return orElseThrow(() -> v); + } + + public TextStreamAsserter orElseThrow(Supplier v) { + createException = v; + return this; + } + + public void apply(Stream lines) { + String matchedStr = lines.filter(line -> predicate.test(line, value)).findFirst().orElse( + null); + String labelStr = Optional.ofNullable(label).orElse("output"); + if (negate) { + String msg = String.format( + "Check %s doesn't contain [%s] string", labelStr, value); + if (createException == null) { + assertNull(matchedStr, msg); + } else { + trace(msg); + if (matchedStr != null) { + throw createException.get(); + } + } + } else { + String msg = String.format("Check %s contains [%s] string", + labelStr, value); + if (createException == null) { + assertNotNull(matchedStr, msg); + } else { + trace(msg); + if (matchedStr == null) { + throw createException.get(); + } + } + } + } + + private BiPredicate predicate; + private String label; + private boolean negate; + private Supplier createException; + final private String value; + } + + public static TextStreamAsserter assertTextStream(String what) { + return new TextStreamAsserter(what); + } + + private static PrintStream openLogStream() { + if (LOG_FILE == null) { + return null; + } + + return ThrowingSupplier.toSupplier(() -> new PrintStream( + new FileOutputStream(LOG_FILE.toFile(), true))).get(); + } + + private static TestInstance currentTest; + private static PrintStream extraLogStream; + + private static final boolean TRACE; + private static final boolean TRACE_ASSERTS; + + static final boolean VERBOSE_JPACKAGE; + static final boolean VERBOSE_TEST_SETUP; + + static String getConfigProperty(String propertyName) { + return System.getProperty(getConfigPropertyName(propertyName)); + } + + static String getConfigPropertyName(String propertyName) { + return "jpackage.test." + propertyName; + } + + static Set tokenizeConfigProperty(String propertyName) { + final String val = TKit.getConfigProperty(propertyName); + if (val == null) { + return null; + } + return Stream.of(val.toLowerCase().split(",")).map(String::strip).filter( + Predicate.not(String::isEmpty)).collect(Collectors.toSet()); + } + + static final Path LOG_FILE = Functional.identity(() -> { + String val = getConfigProperty("logfile"); + if (val == null) { + return null; + } + return Path.of(val); + }).get(); + + static { + Set logOptions = tokenizeConfigProperty("suppress-logging"); + if (logOptions == null) { + TRACE = true; + TRACE_ASSERTS = true; + VERBOSE_JPACKAGE = true; + VERBOSE_TEST_SETUP = true; + } else if (logOptions.contains("all")) { + TRACE = false; + TRACE_ASSERTS = false; + VERBOSE_JPACKAGE = false; + VERBOSE_TEST_SETUP = false; + } else { + Predicate> isNonOf = options -> { + return Collections.disjoint(logOptions, options); + }; + + TRACE = isNonOf.test(Set.of("trace", "t")); + TRACE_ASSERTS = isNonOf.test(Set.of("assert", "a")); + VERBOSE_JPACKAGE = isNonOf.test(Set.of("jpackage", "jp")); + VERBOSE_TEST_SETUP = isNonOf.test(Set.of("init", "i")); + } + } +} --- /dev/null 2019-11-18 21:28:13.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestBuilder.java 2019-11-18 21:28:09.956667100 -0500 @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.test; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.Annotations.AfterEach; +import jdk.jpackage.test.Annotations.BeforeEach; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.ParameterGroup; +import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Functional.ThrowingConsumer; +import jdk.jpackage.test.Functional.ThrowingFunction; + +final class TestBuilder implements AutoCloseable { + + @Override + public void close() throws Exception { + flushTestGroup(); + } + + TestBuilder(Consumer testConsumer) { + argProcessors = Map.of( + CMDLINE_ARG_PREFIX + "after-run", + arg -> getJavaMethodsFromArg(arg).map( + this::wrap).forEachOrdered(afterActions::add), + + CMDLINE_ARG_PREFIX + "before-run", + arg -> getJavaMethodsFromArg(arg).map( + this::wrap).forEachOrdered(beforeActions::add), + + CMDLINE_ARG_PREFIX + "run", + arg -> addTestGroup(getJavaMethodsFromArg(arg).map( + ThrowingFunction.toFunction( + TestBuilder::toMethodCalls)).flatMap(s -> s).collect( + Collectors.toList())), + + CMDLINE_ARG_PREFIX + "exclude", + arg -> (excludedTests = Optional.ofNullable( + excludedTests).orElse(new HashSet())).add(arg), + + CMDLINE_ARG_PREFIX + "include", + arg -> (includedTests = Optional.ofNullable( + includedTests).orElse(new HashSet())).add(arg), + + CMDLINE_ARG_PREFIX + "space-subst", + arg -> spaceSubstitute = arg, + + CMDLINE_ARG_PREFIX + "group", + arg -> flushTestGroup(), + + CMDLINE_ARG_PREFIX + "dry-run", + arg -> dryRun = true + ); + this.testConsumer = testConsumer; + clear(); + } + + void processCmdLineArg(String arg) throws Throwable { + int separatorIdx = arg.indexOf('='); + final String argName; + final String argValue; + if (separatorIdx != -1) { + argName = arg.substring(0, separatorIdx); + argValue = arg.substring(separatorIdx + 1); + } else { + argName = arg; + argValue = null; + } + try { + ThrowingConsumer argProcessor = argProcessors.get(argName); + if (argProcessor == null) { + throw new ParseException("Unrecognized"); + } + argProcessor.accept(argValue); + } catch (ParseException ex) { + ex.setContext(arg); + throw ex; + } + } + + private void addTestGroup(List newTestGroup) { + if (testGroup != null) { + testGroup.addAll(newTestGroup); + } else { + testGroup = newTestGroup; + } + } + + private static Stream filterTests(Stream tests, + Set filters, UnaryOperator pred, String logMsg) { + if (filters == null) { + return tests; + } + + // Log all matches before returning from the function + return tests.filter(test -> { + String testDescription = test.createDescription().testFullName(); + boolean match = filters.stream().anyMatch( + v -> testDescription.contains(v)); + if (match) { + trace(String.format(logMsg + ": %s", testDescription)); + } + return pred.apply(match); + }).collect(Collectors.toList()).stream(); + } + + private Stream filterTestGroup() { + Objects.requireNonNull(testGroup); + + UnaryOperator> restoreSpaces = filters -> { + if (spaceSubstitute == null || filters == null) { + return filters; + } + return filters.stream().map( + filter -> filter.replace(spaceSubstitute, " ")).collect( + Collectors.toSet()); + }; + + if (includedTests != null) { + return filterTests(testGroup.stream(), restoreSpaces.apply( + includedTests), x -> x, "Include"); + } + + return filterTests(testGroup.stream(), + restoreSpaces.apply(excludedTests), x -> !x, "Exclude"); + } + + private void flushTestGroup() { + if (testGroup != null) { + filterTestGroup().forEach(testBody -> createTestInstance(testBody)); + clear(); + } + } + + private void createTestInstance(MethodCall testBody) { + final List curBeforeActions; + final List curAfterActions; + + Method testMethod = testBody.getMethod(); + if (Stream.of(BeforeEach.class, AfterEach.class).anyMatch( + type -> testMethod.isAnnotationPresent(type))) { + curBeforeActions = beforeActions; + curAfterActions = afterActions; + } else { + curBeforeActions = new ArrayList<>(beforeActions); + curAfterActions = new ArrayList<>(afterActions); + + selectFrameMethods(testMethod.getDeclaringClass(), BeforeEach.class).map( + this::wrap).forEachOrdered(curBeforeActions::add); + selectFrameMethods(testMethod.getDeclaringClass(), AfterEach.class).map( + this::wrap).forEachOrdered(curAfterActions::add); + } + + TestInstance test = new TestInstance(testBody, curBeforeActions, + curAfterActions, dryRun); + if (includedTests == null) { + trace(String.format("Create: %s", test.fullName())); + } + testConsumer.accept(test); + } + + private void clear() { + beforeActions = new ArrayList<>(); + afterActions = new ArrayList<>(); + excludedTests = null; + includedTests = null; + spaceSubstitute = null; + testGroup = null; + } + + private static Class probeClass(String name) { + try { + return Class.forName(name); + } catch (ClassNotFoundException ex) { + return null; + } + } + + private static Stream selectFrameMethods(Class type, Class annotationType) { + return Stream.of(type.getMethods()) + .filter(m -> m.getParameterCount() == 0) + .filter(m -> !m.isAnnotationPresent(Test.class)) + .filter(m -> m.isAnnotationPresent(annotationType)) + .sorted((a, b) -> a.getName().compareTo(b.getName())); + } + + private static Stream cmdLineArgValueToMethodNames(String v) { + List result = new ArrayList<>(); + String defaultClassName = null; + for (String token : v.split(",")) { + Class testSet = probeClass(token); + if (testSet != null) { + // Test set class specified. Pull in all public methods + // from the class with @Test annotation removing name duplicates. + // Overloads will be handled at the next phase of processing. + defaultClassName = token; + Stream.of(testSet.getMethods()).filter( + m -> m.isAnnotationPresent(Test.class)).map( + Method::getName).distinct().forEach( + name -> result.add(String.join(".", token, name))); + + continue; + } + + final String qualifiedMethodName; + final int lastDotIdx = token.lastIndexOf('.'); + if (lastDotIdx != -1) { + qualifiedMethodName = token; + defaultClassName = token.substring(0, lastDotIdx); + } else if (defaultClassName == null) { + throw new ParseException("Default class name not found in"); + } else { + qualifiedMethodName = String.join(".", defaultClassName, token); + } + result.add(qualifiedMethodName); + } + return result.stream(); + } + + private static boolean filterMethod(String expectedMethodName, Method method) { + if (!method.getName().equals(expectedMethodName)) { + return false; + } + switch (method.getParameterCount()) { + case 0: + return !isParametrized(method); + case 1: + return isParametrized(method); + } + return false; + } + + private static boolean isParametrized(Method method) { + return method.isAnnotationPresent(ParameterGroup.class) || method.isAnnotationPresent( + Parameter.class); + } + + private static List getJavaMethodFromString( + String qualifiedMethodName) { + int lastDotIdx = qualifiedMethodName.lastIndexOf('.'); + if (lastDotIdx == -1) { + throw new ParseException("Class name not found in"); + } + String className = qualifiedMethodName.substring(0, lastDotIdx); + String methodName = qualifiedMethodName.substring(lastDotIdx + 1); + Class methodClass; + try { + methodClass = Class.forName(className); + } catch (ClassNotFoundException ex) { + throw new ParseException(String.format("Class [%s] not found;", + className)); + } + // Get the list of all public methods as need to deal with overloads. + List methods = Stream.of(methodClass.getMethods()).filter( + (m) -> filterMethod(methodName, m)).collect(Collectors.toList()); + if (methods.isEmpty()) { + new ParseException(String.format( + "Method [%s] not found in [%s] class;", + methodName, className)); + } + + trace(String.format("%s -> %s", qualifiedMethodName, methods)); + return methods; + } + + private static Stream getJavaMethodsFromArg(String argValue) { + return cmdLineArgValueToMethodNames(argValue).map( + ThrowingFunction.toFunction( + TestBuilder::getJavaMethodFromString)).flatMap( + List::stream).sequential(); + } + + private static Parameter[] getMethodParameters(Method method) { + if (method.isAnnotationPresent(ParameterGroup.class)) { + return ((ParameterGroup) method.getAnnotation(ParameterGroup.class)).value(); + } + + if (method.isAnnotationPresent(Parameter.class)) { + return new Parameter[]{(Parameter) method.getAnnotation( + Parameter.class)}; + } + + // Unexpected + return null; + } + + private static Stream toCtorArgs(Method method) throws + IllegalAccessException, InvocationTargetException { + Class type = method.getDeclaringClass(); + List paremetersProviders = Stream.of(type.getMethods()) + .filter(m -> m.getParameterCount() == 0) + .filter(m -> (m.getModifiers() & Modifier.STATIC) != 0) + .filter(m -> m.isAnnotationPresent(Parameters.class)) + .sorted() + .collect(Collectors.toList()); + if (paremetersProviders.isEmpty()) { + // Single instance using the default constructor. + return Stream.ofNullable(MethodCall.DEFAULT_CTOR_ARGS); + } + + // Pick the first method from the list. + Method paremetersProvider = paremetersProviders.iterator().next(); + if (paremetersProviders.size() > 1) { + trace(String.format( + "Found %d public static methods without arguments with %s annotation. Will use %s", + paremetersProviders.size(), Parameters.class, + paremetersProvider)); + paremetersProviders.stream().map(Method::toString).forEach( + TestBuilder::trace); + } + + // Construct collection of arguments for test class instances. + return ((Collection) paremetersProvider.invoke(null)).stream(); + } + + private static Stream toMethodCalls(Method method) throws + IllegalAccessException, InvocationTargetException { + return toCtorArgs(method).map(v -> toMethodCalls(v, method)).flatMap( + s -> s).peek(methodCall -> { + // Make sure required constructor is accessible if the one is needed. + // Need to probe all methods as some of them might be static + // and some class members. + // Only class members require ctors. + try { + methodCall.checkRequiredConstructor(); + } catch (NoSuchMethodException ex) { + throw new ParseException(ex.getMessage() + "."); + } + }); + } + + private static Stream toMethodCalls(Object[] ctorArgs, Method method) { + if (!isParametrized(method)) { + return Stream.of(new MethodCall(ctorArgs, method)); + } + Parameter[] annotations = getMethodParameters(method); + if (annotations.length == 0) { + return Stream.of(new MethodCall(ctorArgs, method)); + } + return Stream.of(annotations).map((a) -> { + Class paramType = method.getParameterTypes()[0]; + final Object annotationValue; + if (!paramType.isArray()) { + annotationValue = fromString(a.value()[0], paramType); + } else { + Class paramComponentType = paramType.getComponentType(); + annotationValue = Array.newInstance(paramComponentType, a.value().length); + var idx = new AtomicInteger(-1); + Stream.of(a.value()).map(v -> fromString(v, paramComponentType)).sequential().forEach( + v -> Array.set(annotationValue, idx.incrementAndGet(), v)); + } + return new MethodCall(ctorArgs, method, annotationValue); + }); + } + + private static Object fromString(String value, Class toType) { + Function converter = conv.get(toType); + if (converter == null) { + throw new RuntimeException(String.format( + "Failed to find a conversion of [%s] string to %s type", + value, toType)); + } + return converter.apply(value); + } + + // Wraps Method.invike() into ThrowingRunnable.run() + private ThrowingConsumer wrap(Method method) { + return (test) -> { + Class methodClass = method.getDeclaringClass(); + String methodName = String.join(".", methodClass.getName(), + method.getName()); + TKit.log(String.format("[ CALL ] %s()", methodName)); + if (!dryRun) { + if (methodClass.isInstance(test)) { + method.invoke(test); + } else { + method.invoke(null); + } + } + }; + } + + private static class ParseException extends IllegalArgumentException { + + ParseException(String msg) { + super(msg); + } + + void setContext(String badCmdLineArg) { + this.badCmdLineArg = badCmdLineArg; + } + + @Override + public String getMessage() { + String msg = super.getMessage(); + if (badCmdLineArg != null) { + msg = String.format("%s parameter=[%s]", msg, badCmdLineArg); + } + return msg; + } + private String badCmdLineArg; + } + + static void trace(String msg) { + if (TKit.VERBOSE_TEST_SETUP) { + TKit.log(msg); + } + } + + private final Map> argProcessors; + private Consumer testConsumer; + private List testGroup; + private List beforeActions; + private List afterActions; + private Set excludedTests; + private Set includedTests; + private String spaceSubstitute; + private boolean dryRun; + + private final static Map> conv = Map.of( + boolean.class, Boolean::valueOf, + Boolean.class, Boolean::valueOf, + int.class, Integer::valueOf, + Integer.class, Integer::valueOf, + long.class, Long::valueOf, + Long.class, Long::valueOf, + String.class, String::valueOf); + + final static String CMDLINE_ARG_PREFIX = "--jpt-"; +} --- /dev/null 2019-11-18 21:28:23.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestInstance.java 2019-11-18 21:28:20.542798900 -0500 @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.test; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.Functional.ThrowingConsumer; +import jdk.jpackage.test.Functional.ThrowingFunction; +import jdk.jpackage.test.Functional.ThrowingRunnable; + +final class TestInstance implements ThrowingRunnable { + + static class TestDesc { + private TestDesc() { + } + + String testFullName() { + StringBuilder sb = new StringBuilder(); + sb.append(clazz.getSimpleName()); + if (instanceArgs != null) { + sb.append('(').append(instanceArgs).append(')'); + } + if (functionName != null) { + sb.append('.'); + sb.append(functionName); + if (functionArgs != null) { + sb.append('(').append(functionArgs).append(')'); + } + } + return sb.toString(); + } + + static Builder createBuilder() { + return new Builder(); + } + + static final class Builder implements Supplier { + private Builder() { + } + + Builder method(Method v) { + method = v; + return this; + } + + Builder ctorArgs(Object... v) { + ctorArgs = ofNullable(v); + return this; + } + + Builder methodArgs(Object... v) { + methodArgs = ofNullable(v); + return this; + } + + @Override + public TestDesc get() { + TestDesc desc = new TestDesc(); + if (method == null) { + desc.clazz = enclosingMainMethodClass(); + } else { + desc.clazz = method.getDeclaringClass(); + desc.functionName = method.getName(); + desc.functionArgs = formatArgs(methodArgs); + desc.instanceArgs = formatArgs(ctorArgs); + } + return desc; + } + + private static String formatArgs(List values) { + if (values == null) { + return null; + } + return values.stream().map(v -> { + if (v != null && v.getClass().isArray()) { + return String.format("%s(length=%d)", + Arrays.deepToString((Object[]) v), + Array.getLength(v)); + } + return String.format("%s", v); + }).collect(Collectors.joining(", ")); + } + + private static List ofNullable(Object... values) { + List result = new ArrayList(); + for (var v: values) { + result.add(v); + } + return result; + } + + private List ctorArgs; + private List methodArgs; + private Method method; + } + + static TestDesc create(Method m, Object... args) { + TestDesc desc = new TestDesc(); + desc.clazz = m.getDeclaringClass(); + desc.functionName = m.getName(); + if (args.length != 0) { + desc.functionArgs = Stream.of(args).map(v -> { + if (v.getClass().isArray()) { + return String.format("%s(length=%d)", + Arrays.deepToString((Object[]) v), + Array.getLength(v)); + } + return String.format("%s", v); + }).collect(Collectors.joining(", ")); + } + return desc; + } + + private Class clazz; + private String functionName; + private String functionArgs; + private String instanceArgs; + } + + TestInstance(ThrowingRunnable testBody) { + assertCount = 0; + this.testConstructor = (unused) -> null; + this.testBody = (unused) -> testBody.run(); + this.beforeActions = Collections.emptyList(); + this.afterActions = Collections.emptyList(); + this.testDesc = TestDesc.createBuilder().get(); + this.dryRun = false; + this.workDir = createWorkDirName(testDesc); + } + + TestInstance(MethodCall testBody, List beforeActions, + List afterActions, boolean dryRun) { + assertCount = 0; + this.testConstructor = v -> ((MethodCall)v).newInstance(); + this.testBody = testBody; + this.beforeActions = beforeActions; + this.afterActions = afterActions; + this.testDesc = testBody.createDescription(); + this.dryRun = dryRun; + this.workDir = createWorkDirName(testDesc); + } + + void notifyAssert() { + assertCount++; + } + + void notifySkipped(RuntimeException ex) { + skippedTestException = ex; + } + + boolean passed() { + return status == Status.Passed; + } + + boolean skipped() { + return status == Status.Skipped; + } + + boolean failed() { + return status == Status.Failed; + } + + String functionName() { + return testDesc.functionName; + } + + String baseName() { + return testDesc.clazz.getSimpleName(); + } + + String fullName() { + return testDesc.testFullName(); + } + + void rethrowIfSkipped() { + if (skippedTestException != null) { + throw skippedTestException; + } + } + + Path workDir() { + return workDir; + } + + @Override + public void run() throws Throwable { + final String fullName = fullName(); + TKit.log(String.format("[ RUN ] %s", fullName)); + try { + Object testInstance = testConstructor.apply(testBody); + beforeActions.forEach(a -> ThrowingConsumer.toConsumer(a).accept( + testInstance)); + try { + if (!dryRun) { + Files.createDirectories(workDir); + testBody.accept(testInstance); + } + } finally { + afterActions.forEach(a -> TKit.ignoreExceptions(() -> a.accept( + testInstance))); + } + status = Status.Passed; + } finally { + if (skippedTestException != null) { + status = Status.Skipped; + } else if (status == null) { + status = Status.Failed; + } + + if (!KEEP_WORK_DIR.contains(status)) { + TKit.deleteDirectoryRecursive(workDir); + } + + TKit.log(String.format("%s %s; checks=%d", status, fullName, + assertCount)); + } + } + + private static Class enclosingMainMethodClass() { + StackTraceElement st[] = Thread.currentThread().getStackTrace(); + for (StackTraceElement ste : st) { + if ("main".equals(ste.getMethodName())) { + return Functional.ThrowingSupplier.toSupplier(() -> Class.forName( + ste.getClassName())).get(); + } + } + return null; + } + + private static boolean isCalledByJavatest() { + StackTraceElement st[] = Thread.currentThread().getStackTrace(); + for (StackTraceElement ste : st) { + if (ste.getClassName().startsWith("com.sun.javatest.")) { + return true; + } + } + return false; + } + + private static Path createWorkDirName(TestDesc testDesc) { + Path result = Path.of("."); + if (!isCalledByJavatest()) { + result = result.resolve(testDesc.clazz.getSimpleName()); + } + + List components = new ArrayList<>(); + + final String testFunctionName = testDesc.functionName; + if (testFunctionName != null) { + components.add(testFunctionName); + } + + final boolean isPrametrized = Stream.of(testDesc.functionArgs, + testDesc.instanceArgs).anyMatch(Objects::nonNull); + if (isPrametrized) { + components.add(String.format("%08x", testDesc.testFullName().hashCode())); + } + + if (!components.isEmpty()) { + result = result.resolve(String.join(".", components)); + } + + return result; + } + + private enum Status { + Passed("[ OK ]"), + Failed("[ FAILED ]"), + Skipped("[ SKIPPED ]"); + + Status(String msg) { + this.msg = msg; + } + + @Override + public String toString() { + return msg; + } + + private final String msg; + } + + private int assertCount; + private Status status; + private RuntimeException skippedTestException; + private final TestDesc testDesc; + private final ThrowingFunction testConstructor; + private final ThrowingConsumer testBody; + private final List beforeActions; + private final List afterActions; + private final boolean dryRun; + private final Path workDir; + + private final static Set KEEP_WORK_DIR = Functional.identity( + () -> { + final String propertyName = "keep-work-dir"; + Set keepWorkDir = TKit.tokenizeConfigProperty( + propertyName); + if (keepWorkDir == null) { + return Set.of(Status.Failed); + } + + Predicate> isOneOf = options -> { + return !Collections.disjoint(keepWorkDir, options); + }; + + Set result = new HashSet<>(); + if (isOneOf.test(Set.of("pass", "p"))) { + result.add(Status.Passed); + } + if (isOneOf.test(Set.of("fail", "f"))) { + result.add(Status.Failed); + } + + return Collections.unmodifiableSet(result); + }).get(); + +} --- /dev/null 2019-11-18 21:28:34.000000000 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java 2019-11-18 21:28:31.178406900 -0500 @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class WindowsHelper { + + static String getBundleName(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.WINDOWS); + return String.format("%s-%s%s", cmd.name(), cmd.version(), + cmd.packageType().getSuffix()); + } + + static Path getInstallationDirectory(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.WINDOWS); + Path installDir = Path.of( + cmd.getArgumentValue("--install-dir", () -> cmd.name())); + if (isUserLocalInstall(cmd)) { + return USER_LOCAL.resolve(installDir); + } + return PROGRAM_FILES.resolve(installDir); + } + + private static boolean isUserLocalInstall(JPackageCommand cmd) { + return cmd.hasArgument("--win-per-user-install"); + } + + static class AppVerifier { + + AppVerifier(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.WINDOWS); + this.cmd = cmd; + verifyStartMenuShortcut(); + verifyDesktopShortcut(); + verifyFileAssociationsRegistry(); + } + + private void verifyDesktopShortcut() { + boolean appInstalled = cmd.appLauncherPath().toFile().exists(); + if (cmd.hasArgument("--win-shortcut")) { + if (isUserLocalInstall(cmd)) { + verifyUserLocalDesktopShortcut(appInstalled); + verifySystemDesktopShortcut(false); + } else { + verifySystemDesktopShortcut(appInstalled); + verifyUserLocalDesktopShortcut(false); + } + } else { + verifySystemDesktopShortcut(false); + verifyUserLocalDesktopShortcut(false); + } + } + + private Path desktopShortcutPath() { + return Path.of(cmd.name() + ".lnk"); + } + + private void verifyShortcut(Path path, boolean exists) { + if (exists) { + TKit.assertFileExists(path); + } else { + TKit.assertPathExists(path, false); + } + } + + private void verifySystemDesktopShortcut(boolean exists) { + Path dir = Path.of(queryRegistryValueCache( + SYSTEM_SHELL_FOLDERS_REGKEY, "Common Desktop")); + verifyShortcut(dir.resolve(desktopShortcutPath()), exists); + } + + private void verifyUserLocalDesktopShortcut(boolean exists) { + Path dir = Path.of( + queryRegistryValueCache(USER_SHELL_FOLDERS_REGKEY, "Desktop")); + verifyShortcut(dir.resolve(desktopShortcutPath()), exists); + } + + private void verifyStartMenuShortcut() { + boolean appInstalled = cmd.appLauncherPath().toFile().exists(); + if (cmd.hasArgument("--win-menu")) { + if (isUserLocalInstall(cmd)) { + verifyUserLocalStartMenuShortcut(appInstalled); + verifySystemStartMenuShortcut(false); + } else { + verifySystemStartMenuShortcut(appInstalled); + verifyUserLocalStartMenuShortcut(false); + } + } else { + verifySystemStartMenuShortcut(false); + verifyUserLocalStartMenuShortcut(false); + } + } + + private Path startMenuShortcutPath() { + return Path.of(cmd.getArgumentValue("--win-menu-group", + () -> "Unknown"), cmd.name() + ".lnk"); + } + + private void verifyStartMenuShortcut(Path shortcutsRoot, boolean exists) { + Path shortcutPath = shortcutsRoot.resolve(startMenuShortcutPath()); + verifyShortcut(shortcutPath, exists); + if (!exists) { + TKit.assertPathExists(shortcutPath.getParent(), false); + } + } + + private void verifySystemStartMenuShortcut(boolean exists) { + verifyStartMenuShortcut(Path.of(queryRegistryValueCache( + SYSTEM_SHELL_FOLDERS_REGKEY, "Common Programs")), exists); + + } + + private void verifyUserLocalStartMenuShortcut(boolean exists) { + verifyStartMenuShortcut(Path.of(queryRegistryValueCache( + USER_SHELL_FOLDERS_REGKEY, "Programs")), exists); + } + + private void verifyFileAssociationsRegistry() { + Stream.of(cmd.getAllArgumentValues("--file-associations")).map( + Path::of).forEach(this::verifyFileAssociationsRegistry); + } + + private void verifyFileAssociationsRegistry(Path faFile) { + boolean appInstalled = cmd.appLauncherPath().toFile().exists(); + try { + TKit.trace(String.format( + "Get file association properties from [%s] file", + faFile)); + Map faProps = Files.readAllLines(faFile).stream().filter( + line -> line.trim().startsWith("extension=") || line.trim().startsWith( + "mime-type=")).map( + line -> { + String[] keyValue = line.trim().split("=", 2); + return Map.entry(keyValue[0], keyValue[1]); + }).collect(Collectors.toMap( + entry -> entry.getKey(), + entry -> entry.getValue())); + String suffix = faProps.get("extension"); + String contentType = faProps.get("mime-type"); + TKit.assertNotNull(suffix, String.format( + "Check file association suffix [%s] is found in [%s] property file", + suffix, faFile)); + TKit.assertNotNull(contentType, String.format( + "Check file association content type [%s] is found in [%s] property file", + contentType, faFile)); + verifyFileAssociations(appInstalled, "." + suffix, contentType); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private void verifyFileAssociations(boolean exists, String suffix, + String contentType) { + String contentTypeFromRegistry = queryRegistryValue(Path.of( + "HKLM\\Software\\Classes", suffix).toString(), + "Content Type"); + String suffixFromRegistry = queryRegistryValue( + "HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\" + contentType, + "Extension"); + + if (exists) { + TKit.assertEquals(suffix, suffixFromRegistry, + "Check suffix in registry is as expected"); + TKit.assertEquals(contentType, contentTypeFromRegistry, + "Check content type in registry is as expected"); + } else { + TKit.assertNull(suffixFromRegistry, + "Check suffix in registry not found"); + TKit.assertNull(contentTypeFromRegistry, + "Check content type in registry not found"); + } + } + + private final JPackageCommand cmd; + } + + private static String queryRegistryValue(String keyPath, String valueName) { + Executor.Result status = new Executor() + .setExecutable("reg") + .saveOutput() + .addArguments("query", keyPath, "/v", valueName) + .execute(); + if (status.exitCode == 1) { + // Should be the case of no such registry value or key + String lookupString = "ERROR: The system was unable to find the specified registry key or value."; + status.getOutput().stream().filter(line -> line.equals(lookupString)).findFirst().orElseThrow( + () -> new RuntimeException(String.format( + "Failed to find [%s] string in the output", + lookupString))); + TKit.trace(String.format( + "Registry value [%s] at [%s] path not found", valueName, + keyPath)); + return null; + } + + String value = status.assertExitCodeIsZero().getOutput().stream().skip(2).findFirst().orElseThrow(); + // Extract the last field from the following line: + // Common Desktop REG_SZ C:\Users\Public\Desktop + value = value.split(" REG_SZ ")[1]; + + TKit.trace(String.format("Registry value [%s] at [%s] path is [%s]", + valueName, keyPath, value)); + + return value; + } + + private static String queryRegistryValueCache(String keyPath, + String valueName) { + String key = String.format("[%s][%s]", keyPath, valueName); + String value = REGISTRY_VALUES.get(key); + if (value == null) { + value = queryRegistryValue(keyPath, valueName); + REGISTRY_VALUES.put(key, value); + } + + return value; + } + + static final Set CRITICAL_RUNTIME_FILES = Set.of(Path.of( + "bin\\server\\jvm.dll")); + + // jtreg resets %ProgramFiles% environment variable by some reason. + private final static Path PROGRAM_FILES = Path.of(Optional.ofNullable( + System.getenv("ProgramFiles")).orElse("C:\\Program Files")); + + private final static Path USER_LOCAL = Path.of(System.getProperty( + "user.home"), + "AppData", "Local"); + + private final static String SYSTEM_SHELL_FOLDERS_REGKEY = "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; + private final static String USER_SHELL_FOLDERS_REGKEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; + + private static final Map REGISTRY_VALUES = new HashMap<>(); +} --- /dev/null 2019-11-18 21:28:44.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/jdk/incubator/jpackage/internal/AppImageFileTest.java 2019-11-18 21:28:41.490681900 -0500 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.LinkedHashMap; +import org.junit.Assert; +import org.junit.Test; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; + +public class AppImageFileTest { + + @Rule + public final TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void testIdentity() throws IOException { + Map params = new LinkedHashMap<>(); + params.put("name", "Foo"); + params.put("app-version", "2.3"); + params.put("description", "Duck is the King"); + AppImageFile aif = create(params); + + Assert.assertEquals("Foo", aif.getLauncherName()); + } + + @Test + public void testInvalidCommandLine() throws IOException { + // Just make sure AppImageFile will tolerate jpackage params that would + // never create app image at both load/save phases. + // People would edit this file just because they can. + // We should be ready to handle curious minds. + Map params = new LinkedHashMap<>(); + params.put("invalidParamName", "randomStringValue"); + create(params); + + params = new LinkedHashMap<>(); + params.put("name", "foo"); + params.put("app-version", ""); + create(params); + } + + @Test + public void testInavlidXml() throws IOException { + assertInvalid(createFromXml("")); + assertInvalid(createFromXml("")); + assertInvalid(createFromXml( + "", + "", + "")); + assertInvalid(createFromXml( + "", + "A", + "B", + "")); + } + + @Test + public void testValidXml() throws IOException { + Assert.assertEquals("Foo", (createFromXml( + "", + "Foo", + "")).getLauncherName()); + + Assert.assertEquals("Boo", (createFromXml( + "", + "Boo", + "Bar", + "")).getLauncherName()); + + var file = createFromXml( + "", + "Foo", + "", + ""); + Assert.assertEquals("Foo", file.getLauncherName()); + Assert.assertArrayEquals(new String[0], + file.getAddLauncherNames().toArray(String[]::new)); + } + + @Test + public void testMainLauncherName() throws IOException { + Map params = new LinkedHashMap<>(); + params.put("name", "Foo"); + params.put("description", "Duck App Description"); + AppImageFile aif = create(params); + + Assert.assertEquals("Foo", aif.getLauncherName()); + } + + @Test + public void testAddLauncherNames() throws IOException { + Map params = new LinkedHashMap<>(); + List> launchersAsMap = new ArrayList<>(); + + Map addLauncher2Params = new LinkedHashMap(); + addLauncher2Params.put("name", "Launcher2Name"); + launchersAsMap.add(addLauncher2Params); + + Map addLauncher3Params = new LinkedHashMap(); + addLauncher3Params.put("name", "Launcher3Name"); + launchersAsMap.add(addLauncher3Params); + + params.put("name", "Duke App"); + params.put("description", "Duke App Description"); + params.put("add-launcher", launchersAsMap); + AppImageFile aif = create(params); + + List addLauncherNames = aif.getAddLauncherNames(); + Assert.assertEquals(2, addLauncherNames.size()); + Assert.assertTrue(addLauncherNames.contains("Launcher2Name")); + Assert.assertTrue(addLauncherNames.contains("Launcher3Name")); + + } + + private AppImageFile create(Map params) throws IOException { + AppImageFile.save(tempFolder.getRoot().toPath(), params); + return AppImageFile.load(tempFolder.getRoot().toPath()); + } + + private void assertInvalid(AppImageFile file) { + Assert.assertNull(file.getLauncherName()); + Assert.assertNull(file.getAddLauncherNames()); + } + + private AppImageFile createFromXml(String... xmlData) throws IOException { + Path directory = tempFolder.getRoot().toPath(); + Path path = AppImageFile.getPathInAppImage(directory); + path.toFile().mkdirs(); + Files.delete(path); + + ArrayList data = new ArrayList(); + data.add(""); + data.addAll(List.of(xmlData)); + + Files.write(path, data, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + + AppImageFile image = AppImageFile.load(directory); + return image; + } + +} --- /dev/null 2019-11-18 21:28:55.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/jdk/incubator/jpackage/internal/ApplicationLayoutTest.java 2019-11-18 21:28:52.258943500 -0500 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Test; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; +import static org.junit.Assert.*; + + +public class ApplicationLayoutTest { + + @Rule + public final TemporaryFolder tempFolder = new TemporaryFolder(); + + private void fillLinuxAppImage() throws IOException { + appImage = tempFolder.newFolder("Foo").toPath(); + + Path base = appImage.getFileName(); + + tempFolder.newFolder(base.toString(), "bin"); + tempFolder.newFolder(base.toString(), "lib", "app", "mods"); + tempFolder.newFolder(base.toString(), "lib", "runtime", "bin"); + tempFolder.newFile(base.resolve("bin/Foo").toString()); + tempFolder.newFile(base.resolve("lib/app/Foo.cfg").toString()); + tempFolder.newFile(base.resolve("lib/app/hello.jar").toString()); + tempFolder.newFile(base.resolve("lib/Foo.png").toString()); + tempFolder.newFile(base.resolve("lib/libapplauncher.so").toString()); + tempFolder.newFile(base.resolve("lib/runtime/bin/java").toString()); + } + + @Test + public void testLinux() throws IOException { + fillLinuxAppImage(); + testApplicationLayout(ApplicationLayout.linuxAppImage()); + } + + private void testApplicationLayout(ApplicationLayout layout) throws IOException { + ApplicationLayout srcLayout = layout.resolveAt(appImage); + assertApplicationLayout(srcLayout); + + ApplicationLayout dstLayout = layout.resolveAt( + appImage.getParent().resolve( + "Copy" + appImage.getFileName().toString())); + srcLayout.move(dstLayout); + Files.deleteIfExists(appImage); + assertApplicationLayout(dstLayout); + + dstLayout.copy(srcLayout); + assertApplicationLayout(srcLayout); + assertApplicationLayout(dstLayout); + } + + private void assertApplicationLayout(ApplicationLayout layout) throws IOException { + assertTrue(Files.isRegularFile(layout.appDirectory().resolve("Foo.cfg"))); + assertTrue(Files.isRegularFile(layout.appDirectory().resolve("hello.jar"))); + assertTrue(Files.isDirectory(layout.appModsDirectory())); + assertTrue(Files.isRegularFile(layout.launchersDirectory().resolve("Foo"))); + assertTrue(Files.isRegularFile(layout.destktopIntegrationDirectory().resolve("Foo.png"))); + assertTrue(Files.isRegularFile(layout.dllDirectory().resolve("libapplauncher.so"))); + assertTrue(Files.isRegularFile(layout.runtimeDirectory().resolve("bin/java"))); + } + + private Path appImage; +} --- /dev/null 2019-11-18 21:29:05.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/jdk/incubator/jpackage/internal/CompareDottedVersionTest.java 2019-11-18 21:29:02.881530000 -0500 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import static org.junit.Assert.*; + +@RunWith(Parameterized.class) +public class CompareDottedVersionTest { + + public CompareDottedVersionTest(boolean greedy, String version1, + String version2, int result) { + this.version1 = version1; + this.version2 = version2; + this.expectedResult = result; + + if (greedy) { + createTestee = DottedVersion::greedy; + } else { + createTestee = DottedVersion::lazy; + } + } + + @Parameters + public static List data() { + List data = new ArrayList<>(); + for (var greedy : List.of(true, false)) { + data.addAll(List.of(new Object[][] { + { greedy, "00.0.0", "0", 0 }, + { greedy, "0.035", "0.0035", 0 }, + { greedy, "1", "1", 0 }, + { greedy, "2", "2.0", 0 }, + { greedy, "2.00", "2.0", 0 }, + { greedy, "1.2.3.4", "1.2.3.4.5", -1 }, + { greedy, "34", "33", 1 }, + { greedy, "34.0.78", "34.1.78", -1 } + })); + } + + data.addAll(List.of(new Object[][] { + { false, "", "1", -1 }, + { false, "1.2.4-R4", "1.2.4-R5", 0 }, + { false, "1.2.4.-R4", "1.2.4.R5", 0 }, + { false, "7+1", "7+4", 0 }, + { false, "2+14", "2-14", 0 }, + { false, "23.4.RC4", "23.3.RC10", 1 }, + { false, "77.0", "77.99999999999999999999999999999999999999999999999", 0 }, + })); + + return data; + } + + @Test + public void testIt() { + int actualResult = compare(version1, version2); + assertEquals(expectedResult, actualResult); + + int actualNegateResult = compare(version2, version1); + assertEquals(actualResult, -1 * actualNegateResult); + } + + private int compare(String x, String y) { + int result = createTestee.apply(x).compareTo(y); + + if (result < 0) { + return -1; + } + + if (result > 0) { + return 1; + } + + return 0; + } + + private final String version1; + private final String version2; + private final int expectedResult; + private final Function createTestee; +} --- /dev/null 2019-11-18 21:29:17.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/jdk/incubator/jpackage/internal/DeployParamsTest.java 2019-11-18 21:29:13.973483700 -0500 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.File; +import java.io.IOException; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.junit.Rule; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +/** + * Test for JDK-8211285 + */ +public class DeployParamsTest { + + @Rule + public final TemporaryFolder tempFolder = new TemporaryFolder(); + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + @Before + public void setUp() throws IOException { + testRoot = tempFolder.newFolder(); + } + + @Test + public void testValidAppName() throws PackagerException { + initParamsAppName(); + + setAppNameAndValidate("Test"); + + setAppNameAndValidate("Test Name"); + + setAppNameAndValidate("Test - Name !!!"); + } + + @Test + public void testInvalidAppName() throws PackagerException { + initForInvalidAppNamePackagerException(); + initParamsAppName(); + setAppNameAndValidate("Test\nName"); + } + + @Test + public void testInvalidAppName2() throws PackagerException { + initForInvalidAppNamePackagerException(); + initParamsAppName(); + setAppNameAndValidate("Test\rName"); + } + + @Test + public void testInvalidAppName3() throws PackagerException { + initForInvalidAppNamePackagerException(); + initParamsAppName(); + setAppNameAndValidate("TestName\\"); + } + + @Test + public void testInvalidAppName4() throws PackagerException { + initForInvalidAppNamePackagerException(); + initParamsAppName(); + setAppNameAndValidate("Test \" Name"); + } + + private void initForInvalidAppNamePackagerException() { + thrown.expect(PackagerException.class); + + String msg = "Error: Invalid Application name"; + + // Unfortunately org.hamcrest.core.StringStartsWith is not available + // with older junit, DIY + + // thrown.expectMessage(startsWith("Error: Invalid Application name")); + thrown.expectMessage(new BaseMatcher() { + @Override + @SuppressWarnings("unchecked") + public boolean matches(Object o) { + if (o instanceof String) { + return ((String) o).startsWith(msg); + } + return false; + } + + @Override + public void describeTo(Description d) { + d.appendText(msg); + } + }); + } + + // Returns deploy params initialized to pass all validation, except for + // app name + private void initParamsAppName() { + params = new DeployParams(); + + params.setOutput(testRoot); + params.addResource(testRoot, new File(testRoot, "test.jar")); + params.addBundleArgument(Arguments.CLIOptions.APPCLASS.getId(), + "TestClass"); + params.addBundleArgument(Arguments.CLIOptions.MAIN_JAR.getId(), + "test.jar"); + params.addBundleArgument(Arguments.CLIOptions.INPUT.getId(), "input"); + } + + private void setAppNameAndValidate(String appName) throws PackagerException { + params.addBundleArgument(Arguments.CLIOptions.NAME.getId(), appName); + params.validate(); + } + + private File testRoot = null; + private DeployParams params; +} --- /dev/null 2019-11-18 21:29:28.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/jdk/incubator/jpackage/internal/DottedVersionTest.java 2019-11-18 21:29:25.050851000 -0500 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Stream; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import static org.junit.Assert.*; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class DottedVersionTest { + + public DottedVersionTest(boolean greedy) { + this.greedy = greedy; + if (greedy) { + createTestee = DottedVersion::greedy; + } else { + createTestee = DottedVersion::lazy; + } + } + + @Parameterized.Parameters + public static List data() { + return List.of(new Object[] { true }, new Object[] { false }); + } + + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); + + @Test + public void testValid() { + final List validStrings = List.of( + "1.0", + "1", + "2.234.045", + "2.234.0", + "0", + "0.1" + ); + + final List validLazyStrings; + if (greedy) { + validLazyStrings = Collections.emptyList(); + } else { + validLazyStrings = List.of( + "1.-1", + "5.", + "4.2.", + "3..2", + "2.a", + "0a", + ".", + " ", + " 1", + "1. 2", + "+1", + "-1", + "-0", + "1234567890123456789012345678901234567890" + ); + } + + Stream.concat(validStrings.stream(), validLazyStrings.stream()) + .forEach(value -> { + DottedVersion version = createTestee.apply(value); + assertEquals(version.toString(), value); + }); + } + + @Test + public void testNull() { + exceptionRule.expect(NullPointerException.class); + createTestee.apply(null); + } + + @Test + public void testEmpty() { + if (greedy) { + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage("Version may not be empty string"); + createTestee.apply(""); + } else { + assertTrue(0 == createTestee.apply("").compareTo("")); + assertTrue(0 == createTestee.apply("").compareTo("0")); + } + } + + private final boolean greedy; + private final Function createTestee; +} --- /dev/null 2019-11-18 21:29:39.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/jdk/incubator/jpackage/internal/InvalidDottedVersionTest.java 2019-11-18 21:29:36.577883400 -0500 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class InvalidDottedVersionTest { + + public InvalidDottedVersionTest(String version) { + this.version = version; + } + + @Parameters + public static List data() { + return Stream.of( + "1.-1", + "5.", + "4.2.", + "3..2", + "2.a", + "0a", + ".", + " ", + " 1", + "1. 2", + "+1", + "-1", + "-0", + "1234567890123456789012345678901234567890" + ).map(version -> new Object[] { version }).collect(Collectors.toList()); + } + + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); + + @Test + public void testIt() { + exceptionRule.expect(IllegalArgumentException.class); + new DottedVersion(version); + } + + private final String version; +} --- /dev/null 2019-11-18 21:29:50.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/jdk/incubator/jpackage/internal/OverridableResourceTest.java 2019-11-18 21:29:47.639511300 -0500 @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import jdk.incubator.jpackage.internal.resources.ResourceLocator; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class OverridableResourceTest { + + @Rule + public final TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void testDefault() throws IOException { + byte[] actualBytes = saveToFile(new OverridableResource(DEFAULT_NAME)); + + try (InputStream is = ResourceLocator.class.getResourceAsStream( + DEFAULT_NAME)) { + assertArrayEquals(is.readAllBytes(), actualBytes); + } + } + + @Test + public void testDefaultWithSubstitution() throws IOException { + OverridableResource resource = new OverridableResource(DEFAULT_NAME); + + List linesBeforeSubstitution = convertToStringList(saveToFile( + resource)); + + if (SUBSTITUTION_DATA.size() != 1) { + // Test setup issue + throw new IllegalArgumentException( + "Substitution map should contain only a single entry"); + } + + resource.setSubstitutionData(SUBSTITUTION_DATA); + List linesAfterSubstitution = convertToStringList(saveToFile( + resource)); + + assertEquals(linesBeforeSubstitution.size(), linesAfterSubstitution.size()); + + Iterator beforeIt = linesBeforeSubstitution.iterator(); + Iterator afterIt = linesAfterSubstitution.iterator(); + + var substitutionEntry = SUBSTITUTION_DATA.entrySet().iterator().next(); + + boolean linesMismatch = false; + while (beforeIt.hasNext()) { + String beforeStr = beforeIt.next(); + String afterStr = afterIt.next(); + + if (beforeStr.equals(afterStr)) { + assertFalse(beforeStr.contains(substitutionEntry.getKey())); + } else { + linesMismatch = true; + assertTrue(beforeStr.contains(substitutionEntry.getKey())); + assertTrue(afterStr.contains(substitutionEntry.getValue())); + assertFalse(afterStr.contains(substitutionEntry.getKey())); + } + } + + assertTrue(linesMismatch); + } + + @Test + public void testCustom() throws IOException { + testCustom(DEFAULT_NAME); + } + + @Test + public void testCustomNoDefault() throws IOException { + testCustom(null); + } + + private void testCustom(String defaultName) throws IOException { + List expectedResourceData = List.of("A", "B", "C"); + + Path customFile = createCustomFile("foo", expectedResourceData); + + List actualResourceData = convertToStringList(saveToFile( + new OverridableResource(defaultName) + .setPublicName(customFile.getFileName()) + .setResourceDir(customFile.getParent()))); + + assertArrayEquals(expectedResourceData.toArray(String[]::new), + actualResourceData.toArray(String[]::new)); + } + + @Test + public void testCustomtWithSubstitution() throws IOException { + testCustomtWithSubstitution(DEFAULT_NAME); + } + + @Test + public void testCustomtWithSubstitutionNoDefault() throws IOException { + testCustomtWithSubstitution(null); + } + + private void testCustomtWithSubstitution(String defaultName) throws IOException { + final List resourceData = List.of("A", "[BB]", "C", "Foo", + "GoodbyeHello"); + final Path customFile = createCustomFile("foo", resourceData); + + final Map substitutionData = new HashMap(Map.of("B", + "Bar", "Foo", "B")); + substitutionData.put("Hello", null); + + final List expectedResourceData = List.of("A", "[BarBar]", "C", + "B", "Goodbye"); + + final List actualResourceData = convertToStringList(saveToFile( + new OverridableResource(defaultName) + .setPublicName(customFile.getFileName()) + .setSubstitutionData(substitutionData) + .setResourceDir(customFile.getParent()))); + assertArrayEquals(expectedResourceData.toArray(String[]::new), + actualResourceData.toArray(String[]::new)); + + // Don't call setPublicName() + final Path dstFile = tempFolder.newFolder().toPath().resolve(customFile.getFileName()); + new OverridableResource(defaultName) + .setSubstitutionData(substitutionData) + .setResourceDir(customFile.getParent()) + .saveToFile(dstFile); + assertArrayEquals(expectedResourceData.toArray(String[]::new), + convertToStringList(Files.readAllBytes(dstFile)).toArray( + String[]::new)); + + // Verify setSubstitutionData() stores a copy of passed in data + Map substitutionData2 = new HashMap(substitutionData); + var resource = new OverridableResource(defaultName) + .setResourceDir(customFile.getParent()); + + resource.setSubstitutionData(substitutionData2); + substitutionData2.clear(); + Files.delete(dstFile); + resource.saveToFile(dstFile); + assertArrayEquals(expectedResourceData.toArray(String[]::new), + convertToStringList(Files.readAllBytes(dstFile)).toArray( + String[]::new)); + } + + @Test + public void testNoDefault() throws IOException { + Path dstFolder = tempFolder.newFolder().toPath(); + Path dstFile = dstFolder.resolve(Path.of("foo", "bar")); + + new OverridableResource(null).saveToFile(dstFile); + + assertFalse(dstFile.toFile().exists()); + } + + private final static String DEFAULT_NAME; + private final static Map SUBSTITUTION_DATA; + static { + if (Platform.isWindows()) { + DEFAULT_NAME = "WinLauncher.template"; + SUBSTITUTION_DATA = Map.of("COMPANY_NAME", "Foo9090345"); + } else if (Platform.isLinux()) { + DEFAULT_NAME = "template.control"; + SUBSTITUTION_DATA = Map.of("APPLICATION_PACKAGE", "Package1967"); + } else if (Platform.isMac()) { + DEFAULT_NAME = "Info-lite.plist.template"; + SUBSTITUTION_DATA = Map.of("DEPLOY_BUNDLE_IDENTIFIER", "12345"); + } else { + throw Platform.throwUnknownPlatformError(); + } + } + + private byte[] saveToFile(OverridableResource resource) throws IOException { + Path dstFile = tempFolder.newFile().toPath(); + resource.saveToFile(dstFile); + assertThat(0, is(not(dstFile.toFile().length()))); + + return Files.readAllBytes(dstFile); + } + + private Path createCustomFile(String publicName, List data) throws + IOException { + Path resourceFolder = tempFolder.newFolder().toPath(); + Path customFile = resourceFolder.resolve(publicName); + + Files.write(customFile, data); + + return customFile; + } + + private static List convertToStringList(byte[] data) { + return List.of(new String(data, StandardCharsets.UTF_8).split("\\R")); + } +} --- /dev/null 2019-11-18 21:30:01.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/jdk/incubator/jpackage/internal/PathGroupTest.java 2019-11-18 21:29:58.613562600 -0500 @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + + +public class PathGroupTest { + + @Rule + public final TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test(expected = NullPointerException.class) + public void testNullId() { + new PathGroup(Map.of()).getPath(null); + } + + @Test + public void testEmptyPathGroup() { + PathGroup pg = new PathGroup(Map.of()); + + assertNull(pg.getPath("foo")); + + assertEquals(0, pg.paths().size()); + assertEquals(0, pg.roots().size()); + } + + @Test + public void testRootsSinglePath() { + final PathGroup pg = new PathGroup(Map.of("main", PATH_FOO)); + + List paths = pg.paths(); + assertEquals(1, paths.size()); + assertEquals(PATH_FOO, paths.iterator().next()); + + List roots = pg.roots(); + assertEquals(1, roots.size()); + assertEquals(PATH_FOO, roots.iterator().next()); + } + + @Test + public void testDuplicatedRoots() { + final PathGroup pg = new PathGroup(Map.of("main", PATH_FOO, "another", + PATH_FOO, "root", PATH_EMPTY)); + + List paths = pg.paths(); + Collections.sort(paths); + + assertEquals(3, paths.size()); + assertEquals(PATH_EMPTY, paths.get(0)); + assertEquals(PATH_FOO, paths.get(1)); + assertEquals(PATH_FOO, paths.get(2)); + + List roots = pg.roots(); + assertEquals(1, roots.size()); + assertEquals(PATH_EMPTY, roots.get(0)); + } + + @Test + public void testRoots() { + final PathGroup pg = new PathGroup(Map.of(1, Path.of("foo"), 2, Path.of( + "foo", "bar"), 3, Path.of("foo", "bar", "buz"))); + + List paths = pg.paths(); + assertEquals(3, paths.size()); + assertTrue(paths.contains(Path.of("foo"))); + assertTrue(paths.contains(Path.of("foo", "bar"))); + assertTrue(paths.contains(Path.of("foo", "bar", "buz"))); + + List roots = pg.roots(); + assertEquals(1, roots.size()); + assertEquals(Path.of("foo"), roots.get(0)); + } + + @Test + public void testResolveAt() { + final PathGroup pg = new PathGroup(Map.of(0, PATH_FOO, 1, PATH_BAR, 2, + PATH_EMPTY)); + + final Path aPath = Path.of("a"); + + final PathGroup pg2 = pg.resolveAt(aPath); + assertThat(pg, not(equalTo(pg2))); + + List paths = pg.paths(); + assertEquals(3, paths.size()); + assertTrue(paths.contains(PATH_EMPTY)); + assertTrue(paths.contains(PATH_FOO)); + assertTrue(paths.contains(PATH_BAR)); + assertEquals(PATH_EMPTY, pg.roots().get(0)); + + paths = pg2.paths(); + assertEquals(3, paths.size()); + assertTrue(paths.contains(aPath.resolve(PATH_EMPTY))); + assertTrue(paths.contains(aPath.resolve(PATH_FOO))); + assertTrue(paths.contains(aPath.resolve(PATH_BAR))); + assertEquals(aPath, pg2.roots().get(0)); + } + + @Test + public void testTransform() throws IOException { + for (var transform : TransformType.values()) { + testTransform(false, transform); + } + } + + @Test + public void testTransformWithExcludes() throws IOException { + for (var transform : TransformType.values()) { + testTransform(true, transform); + } + } + + enum TransformType { Copy, Move, Handler }; + + private void testTransform(boolean withExcludes, TransformType transform) + throws IOException { + final PathGroup pg = new PathGroup(Map.of(0, PATH_FOO, 1, PATH_BAR, 2, + PATH_EMPTY, 3, PATH_BAZ)); + + final Path srcDir = tempFolder.newFolder().toPath(); + final Path dstDir = tempFolder.newFolder().toPath(); + + Files.createDirectories(srcDir.resolve(PATH_FOO).resolve("a/b/c/d")); + Files.createFile(srcDir.resolve(PATH_FOO).resolve("a/b/c/file1")); + Files.createFile(srcDir.resolve(PATH_FOO).resolve("a/b/file2")); + Files.createFile(srcDir.resolve(PATH_FOO).resolve("a/b/file3")); + Files.createFile(srcDir.resolve(PATH_BAR)); + Files.createFile(srcDir.resolve(PATH_EMPTY).resolve("file4")); + Files.createDirectories(srcDir.resolve(PATH_BAZ).resolve("1/2/3")); + + var dst = pg.resolveAt(dstDir); + var src = pg.resolveAt(srcDir); + if (withExcludes) { + // Exclude from transformation. + src.setPath(new Object(), srcDir.resolve(PATH_FOO).resolve("a/b/c")); + src.setPath(new Object(), srcDir.resolve(PATH_EMPTY).resolve("file4")); + } + + var srcFilesBeforeTransform = walkFiles(srcDir); + + if (transform == TransformType.Handler) { + List> copyFile = new ArrayList<>(); + List createDirectory = new ArrayList<>(); + src.transform(dst, new PathGroup.TransformHandler() { + @Override + public void copyFile(Path src, Path dst) throws IOException { + copyFile.add(Map.entry(src, dst)); + } + + @Override + public void createDirectory(Path dir) throws IOException { + createDirectory.add(dir); + } + }); + + Consumer assertFile = path -> { + var entry = Map.entry(srcDir.resolve(path), dstDir.resolve(path)); + assertTrue(copyFile.contains(entry)); + }; + + Consumer assertDir = path -> { + assertTrue(createDirectory.contains(dstDir.resolve(path))); + }; + + assertEquals(withExcludes ? 3 : 5, copyFile.size()); + assertEquals(withExcludes ? 8 : 10, createDirectory.size()); + + assertFile.accept(PATH_FOO.resolve("a/b/file2")); + assertFile.accept(PATH_FOO.resolve("a/b/file3")); + assertFile.accept(PATH_BAR); + assertDir.accept(PATH_FOO.resolve("a/b")); + assertDir.accept(PATH_FOO.resolve("a")); + assertDir.accept(PATH_FOO); + assertDir.accept(PATH_BAZ); + assertDir.accept(PATH_BAZ.resolve("1")); + assertDir.accept(PATH_BAZ.resolve("1/2")); + assertDir.accept(PATH_BAZ.resolve("1/2/3")); + assertDir.accept(PATH_EMPTY); + + if (!withExcludes) { + assertFile.accept(PATH_FOO.resolve("a/b/c/file1")); + assertFile.accept(PATH_EMPTY.resolve("file4")); + assertDir.accept(PATH_FOO.resolve("a/b/c/d")); + assertDir.accept(PATH_FOO.resolve("a/b/c")); + } + + assertArrayEquals(new Path[] { Path.of("") }, walkFiles(dstDir)); + return; + } + + if (transform == TransformType.Copy) { + src.copy(dst); + } else if (transform == TransformType.Move) { + src.move(dst); + } + + final List excludedPaths; + if (withExcludes) { + excludedPaths = List.of( + PATH_EMPTY.resolve("file4"), + PATH_FOO.resolve("a/b/c") + ); + } else { + excludedPaths = Collections.emptyList(); + } + UnaryOperator removeExcludes = paths -> { + return Stream.of(paths) + .filter(path -> !excludedPaths.stream().anyMatch( + path::startsWith)) + .collect(Collectors.toList()).toArray(Path[]::new); + }; + + var dstFiles = walkFiles(dstDir); + assertArrayEquals(removeExcludes.apply(srcFilesBeforeTransform), dstFiles); + + if (transform == TransformType.Copy) { + assertArrayEquals(dstFiles, removeExcludes.apply(walkFiles(srcDir))); + } else if (transform == TransformType.Move) { + assertFalse(Files.exists(srcDir)); + } + } + + private static Path[] walkFiles(Path root) throws IOException { + try (var files = Files.walk(root)) { + return files.map(root::relativize).sorted().collect( + Collectors.toList()).toArray(Path[]::new); + } + } + + private final static Path PATH_FOO = Path.of("foo"); + private final static Path PATH_BAR = Path.of("bar"); + private final static Path PATH_BAZ = Path.of("baz"); + private final static Path PATH_EMPTY = Path.of(""); +} --- /dev/null 2019-11-18 21:30:13.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/jdk/incubator/jpackage/internal/ToolValidatorTest.java 2019-11-18 21:30:09.944132400 -0500 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.incubator.jpackage.internal; + +import java.nio.file.Path; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.*; +import org.junit.Test; + + +public class ToolValidatorTest { + + @Test + public void testAvailable() { + assertNull(new ToolValidator(TOOL_JAVA).validate()); + } + + @Test + public void testNotAvailable() { + assertValidationFailure(new ToolValidator(TOOL_UNKNOWN).validate(), true); + } + + @Test + public void testVersionParserUsage() { + // Without minimal version configured, version parser should not be used + new ToolValidator(TOOL_JAVA).setVersionParser(unused -> { + throw new RuntimeException(); + }).validate(); + + // Minimal version is 1, actual is 10. Should be OK. + assertNull(new ToolValidator(TOOL_JAVA).setMinimalVersion( + new DottedVersion("1")).setVersionParser(unused -> "10").validate()); + + // Minimal version is 5, actual is 4.99.37. Error expected. + assertValidationFailure(new ToolValidator(TOOL_JAVA).setMinimalVersion( + new DottedVersion("5")).setVersionParser(unused -> "4.99.37").validate(), + false); + + // Minimal version is 8, actual is 10, lexicographical comparison is used. Error expected. + assertValidationFailure(new ToolValidator(TOOL_JAVA).setMinimalVersion( + "8").setVersionParser(unused -> "10").validate(), false); + + // Minimal version is 8, actual is 10, Use DottedVersion class for comparison. Should be OK. + assertNull(new ToolValidator(TOOL_JAVA).setMinimalVersion( + new DottedVersion("8")).setVersionParser(unused -> "10").validate()); + } + + private static void assertValidationFailure(ConfigException v, + boolean withCause) { + assertNotNull(v); + assertThat("", is(not(v.getMessage().strip()))); + assertThat("", is(not(v.advice.strip()))); + if (withCause) { + assertNotNull(v.getCause()); + } else { + assertNull(v.getCause()); + } + } + + private final static String TOOL_JAVA; + private final static String TOOL_UNKNOWN = Path.of(System.getProperty( + "java.home"), "bin").toString(); + + static { + String fname = "java"; + if (Platform.isWindows()) { + fname = fname + ".exe"; + } + TOOL_JAVA = Path.of(System.getProperty("java.home"), "bin", fname).toString(); + } +} --- old/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-11-18 21:30:25.011351000 -0500 +++ /dev/null 2019-11-18 21:30:26.000000000 -0500 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include - -extern "C" { - - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved) { - return true; - } -} \ No newline at end of file --- /dev/null 2019-11-18 21:30:26.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/junit.java 2019-11-18 21:30:21.370684800 -0500 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary jpackage unit tests + * @library ${jtreg.home}/lib/junit.jar + * @run shell run_junit.sh + */ --- /dev/null 2019-11-18 21:30:46.000000000 -0500 +++ new/test/jdk/tools/jpackage/junit/run_junit.sh 2019-11-18 21:30:43.229223900 -0500 @@ -0,0 +1,33 @@ +#!/bin/bash + +set -x + +set -e +if [ -z "$BASH" ]; then + # The script relies on Bash arrays, rerun in Bash. + /bin/bash $0 $@ + exit +fi + +sources=() +classes=() +for s in $(find "${TESTSRC}" -name "*.java" | grep -v junit.java); do + sources+=( "$s" ) + classes+=( $(echo "$s" | sed -e "s|${TESTSRC}/||" -e 's|/|.|g' -e 's/.java$//') ) +done + +common_args=(\ + --add-modules jdk.incubator.jpackage \ + --patch-module jdk.incubator.jpackage="${TESTSRC}${PS}${TESTCLASSES}" \ + --add-reads jdk.incubator.jpackage=ALL-UNNAMED \ + --add-exports jdk.incubator.jpackage/jdk.incubator.jpackage.internal=ALL-UNNAMED \ + -classpath "${TESTCLASSPATH}" \ +) + +# Compile classes for junit +"${COMPILEJAVA}/bin/javac" ${TESTTOOLVMOPTS} ${TESTJAVACOPTS} \ + "${common_args[@]}" -d "${TESTCLASSES}" "${sources[@]}" + +# Run junit +"${TESTJAVA}/bin/java" ${TESTVMOPTS} ${TESTJAVAOPTS} \ + "${common_args[@]}" org.junit.runner.JUnitCore "${classes[@]}" --- /dev/null 2019-11-18 21:30:57.000000000 -0500 +++ new/test/jdk/tools/jpackage/linux/AppCategoryTest.java 2019-11-18 21:30:54.447021700 -0500 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; + + +/** + * Test --linux-app-category parameter. Output of the test should be + * appcategorytest_1.0-1_amd64.deb or appcategorytest-1.0-1.amd64.rpm package + * bundle. The output package should provide the same functionality as the + * default package. + * + * deb: + * Section property of the package should be set to Foo value. + * + * rpm: + * Group property of the package should be set to Foo value. + */ + + +/* + * @test + * @summary jpackage with --linux-app-category + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m AppCategoryTest + */ +public class AppCategoryTest { + + public static void main(String[] args) { + final String CATEGORY = "Foo"; + + TKit.run(args, () -> { + new PackageTest() + .forTypes(PackageType.LINUX) + .configureHelloApp() + .addInitializer(cmd -> { + cmd.addArguments("--linux-app-category", CATEGORY); + }) + .forTypes(PackageType.LINUX_DEB) + .addBundlePropertyVerifier("Section", CATEGORY) + .forTypes(PackageType.LINUX_RPM) + .addBundlePropertyVerifier("Group", CATEGORY) + .run(); + }); + } +} --- /dev/null 2019-11-18 21:31:09.000000000 -0500 +++ new/test/jdk/tools/jpackage/linux/LicenseTypeTest.java 2019-11-18 21:31:05.825625900 -0500 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; + + +/** + * Test --linux-rpm-license-type parameter. Output of the test should be + * licensetypetest-1.0-1.amd64.rpm package bundle. The output package + * should provide the same functionality as the + * default package. + * License property of the package should be set to JP_LICENSE_TYPE. + */ + + +/* + * @test + * @summary jpackage with --linux-rpm-license-type + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m LicenseTypeTest + */ +public class LicenseTypeTest { + + public static void main(String[] args) { + final String LICENSE_TYPE = "JP_LICENSE_TYPE"; + + TKit.run(args, () -> { + new PackageTest().forTypes(PackageType.LINUX_RPM).configureHelloApp() + .addInitializer(cmd -> { + cmd.addArguments("--linux-rpm-license-type", LICENSE_TYPE); + }) + .addBundlePropertyVerifier("License", LICENSE_TYPE) + .run(); + }); + } +} --- /dev/null 2019-11-18 21:31:20.000000000 -0500 +++ new/test/jdk/tools/jpackage/linux/LinuxBundleNameTest.java 2019-11-18 21:31:17.036763600 -0500 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; + + +/** + * Test --linux-package-name parameter. Output of the test should be + * quickbrownfox2_1.0-1_amd64.deb or quickbrownfox2-1.0-1.amd64.rpm package + * bundle. The output package should provide the same functionality as the + * default package. + * + * deb: + * Package property of the package should be set to quickbrownfox2. + * + * rpm: + * Name property of the package should be set to quickbrownfox2. + */ + + +/* + * @test + * @summary jpackage with --linux-package-name + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m LinuxBundleNameTest + */ +public class LinuxBundleNameTest { + + public static void main(String[] args) { + final String PACKAGE_NAME = "quickbrownfox2"; + + TKit.run(args, () -> { + new PackageTest() + .forTypes(PackageType.LINUX) + .configureHelloApp() + .addInitializer(cmd -> { + cmd.addArguments("--linux-package-name", PACKAGE_NAME); + }) + .forTypes(PackageType.LINUX_DEB) + .addBundlePropertyVerifier("Package", PACKAGE_NAME) + .forTypes(PackageType.LINUX_RPM) + .addBundlePropertyVerifier("Name", PACKAGE_NAME) + .run(); + }); + } +} --- /dev/null 2019-11-18 21:31:31.000000000 -0500 +++ new/test/jdk/tools/jpackage/linux/LinuxResourceTest.java 2019-11-18 21:31:28.414115800 -0500 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.nio.file.Path; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.LinuxHelper; +import jdk.jpackage.test.Annotations.Test; +import java.util.List; + +/* + * @test + * @summary jpackage with --resource-dir + * @library ../helpers + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile LinuxResourceTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=LinuxResourceTest + */ + +public class LinuxResourceTest { + @Test + public static void testHardcodedProperties() throws IOException { + new PackageTest() + .forTypes(PackageType.LINUX) + .configureHelloApp() + .addInitializer(cmd -> { + cmd + .setFakeRuntime() + .saveConsoleOutput(true) + .addArguments("--resource-dir", TKit.createTempDirectory("resources")); + }) + .forTypes(PackageType.LINUX_DEB) + .addInitializer(cmd -> { + Path controlFile = Path.of(cmd.getArgumentValue("--resource-dir"), + "control"); + TKit.createTextFile(controlFile, List.of( + "Package: dont-install-me", + "Version: 1.2.3-R2", + "Section: APPLICATION_SECTION", + "Maintainer: APPLICATION_MAINTAINER", + "Priority: optional", + "Architecture: bar", + "Provides: dont-install-me", + "Description: APPLICATION_DESCRIPTION", + "Installed-Size: APPLICATION_INSTALLED_SIZE", + "Depends: PACKAGE_DEFAULT_DEPENDENCIES" + )); + }) + .addBundleVerifier((cmd, result) -> { + TKit.assertTextStream("Using custom package resource [DEB control file]") + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream(String.format( + "Expected value of \"Package\" property is [%s]. Actual value in output package is [dont-install-me]", + LinuxHelper.getPackageName(cmd))) + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream( + "Expected value of \"Version\" property is [1.0-1]. Actual value in output package is [1.2.3-R2]") + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream(String.format( + "Expected value of \"Architecture\" property is [%s]. Actual value in output package is [bar]", + LinuxHelper.getDefaultPackageArch(cmd.packageType()))) + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + }) + .forTypes(PackageType.LINUX_RPM) + .addInitializer(cmd -> { + Path specFile = Path.of(cmd.getArgumentValue("--resource-dir"), + LinuxHelper.getPackageName(cmd) + ".spec"); + TKit.createTextFile(specFile, List.of( + "Name: dont-install-me", + "Version: 1.2.3", + "Release: R2", + "Summary: APPLICATION_SUMMARY", + "License: APPLICATION_LICENSE_TYPE", + "Prefix: %{dirname:APPLICATION_DIRECTORY}", + "Provides: dont-install-me", + "%description", + "APPLICATION_DESCRIPTION", + "%prep", + "%build", + "%install", + "rm -rf %{buildroot}", + "install -d -m 755 %{buildroot}APPLICATION_DIRECTORY", + "cp -r %{_sourcedir}APPLICATION_DIRECTORY/* %{buildroot}APPLICATION_DIRECTORY", + "%files", + "APPLICATION_DIRECTORY" + )); + }) + .addBundleVerifier((cmd, result) -> { + TKit.assertTextStream("Using custom package resource [RPM spec file]") + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream(String.format( + "Expected value of \"Name\" property is [%s]. Actual value in output package is [dont-install-me]", + LinuxHelper.getPackageName(cmd))) + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream( + "Expected value of \"Version\" property is [1.0]. Actual value in output package is [1.2.3]") + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream( + "Expected value of \"Release\" property is [1]. Actual value in output package is [R2]") + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + }) + .run(); + } +} --- /dev/null 2019-11-18 21:31:43.000000000 -0500 +++ new/test/jdk/tools/jpackage/linux/MaintainerTest.java 2019-11-18 21:31:39.769249400 -0500 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.TKit; + + +/** + * Test --linux-deb-maintainer parameter. Output of the test should be + * maintainertest_1.0-1_amd64.deb package bundle. The output package + * should provide the same functionality as the + * default package. + * Value of Maintainer property of the package should contain + * jpackage-test@java.com email address. + */ + + +/* + * @test + * @summary jpackage with --linux-deb-maintainer + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m MaintainerTest + */ +public class MaintainerTest { + + public static void main(String[] args) { + final String MAINTAINER = "jpackage-test@java.com"; + + TKit.run(args, () -> { + new PackageTest().forTypes(PackageType.LINUX_DEB).configureHelloApp() + .addInitializer(cmd -> { + cmd.addArguments("--linux-deb-maintainer", MAINTAINER); + }) + .addBundlePropertyVerifier("Maintainer", (propName, propValue) -> { + String lookupValue = "<" + MAINTAINER + ">"; + TKit.assertTrue(propValue.endsWith(lookupValue), + String.format("Check value of %s property [%s] ends with %s", + propName, propValue, lookupValue)); + }) + .run(); + }); + } +} --- /dev/null 2019-11-18 21:31:54.000000000 -0500 +++ new/test/jdk/tools/jpackage/linux/PackageDepsTest.java 2019-11-18 21:31:51.103314000 -0500 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.LinuxHelper; + + +/** + * Test --linux-package-deps parameter. Output of the test should be + * apackagedepstestprereq_1.0-1_amd64.deb and packagedepstest_1.0-1_amd64.deb or + * apackagedepstestprereq-1.0-1.amd64.rpm and packagedepstest-1.0-1.amd64.rpm + * package bundles. The output packages should provide the same functionality as + * the default package. + * + * deb: Value of Depends property of packagedepstest package should contain + * apackagedepstestprereq word. + * + * rpm: Value of Requires property of packagedepstest package should contain + * apackagedepstestprereq word. + */ + + +/* + * @test + * @summary jpackage with --linux-package-deps + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m PackageDepsTest + */ +public class PackageDepsTest { + + public static void main(String[] args) { + // Pick the name of prerequisite package to be alphabetically + // preceeding the main package name. + // This is needed to make Bash script batch installing/uninstalling packages + // produced by jtreg tests install/uninstall packages in the right order. + final String PREREQ_PACKAGE_NAME = "apackagedepstestprereq"; + + TKit.run(args, () -> { + new PackageTest() + .forTypes(PackageType.LINUX) + .configureHelloApp() + .addInitializer(cmd -> { + cmd.setArgumentValue("--name", PREREQ_PACKAGE_NAME); + }) + .run(); + + new PackageTest() + .forTypes(PackageType.LINUX) + .configureHelloApp() + .addInitializer(cmd -> { + cmd.addArguments("--linux-package-deps", PREREQ_PACKAGE_NAME); + }) + .forTypes(PackageType.LINUX) + .addBundleVerifier(cmd -> { + TKit.assertTrue( + LinuxHelper.getPrerequisitePackages(cmd).contains( + PREREQ_PACKAGE_NAME), String.format( + "Check package depends on [%s] package", + PREREQ_PACKAGE_NAME)); + }) + .run(); + }); + } +} --- /dev/null 2019-11-18 21:32:05.000000000 -0500 +++ new/test/jdk/tools/jpackage/linux/ReleaseTest.java 2019-11-18 21:32:02.161949800 -0500 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.TKit; + + +/** + * Test --linux-app-release parameter. Output of the test should be + * releasetest_1.0-Rc3_amd64.deb or releasetest-1.0-Rc3.amd64.rpm package + * bundle. The output package should provide the same functionality as the + * default package. + * + * deb: + * Version property of the package should end with -Rc3 substring. + * + * rpm: + * Release property of the package should be set to Rc3 value. + */ + +/* + * @test + * @summary jpackage with --linux-app-release + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m ReleaseTest + */ +public class ReleaseTest { + + public static void main(String[] args) { + final String RELEASE = "Rc3"; + + TKit.run(args, () -> { + new PackageTest() + .forTypes(PackageType.LINUX) + .configureHelloApp() + .addInitializer(cmd -> { + cmd.addArguments("--linux-app-release", RELEASE); + }) + .forTypes(PackageType.LINUX_RPM) + .addBundlePropertyVerifier("Release", RELEASE) + .forTypes(PackageType.LINUX_DEB) + .addBundlePropertyVerifier("Version", (propName, propValue) -> { + TKit.assertTrue(propValue.endsWith("-" + RELEASE), + String.format("Check value of %s property [%s] ends with %s", + propName, propValue, RELEASE)); + }) + .run(); + }); + } +} --- /dev/null 2019-11-18 21:32:17.000000000 -0500 +++ new/test/jdk/tools/jpackage/linux/ShortcutHintTest.java 2019-11-18 21:32:13.911348600 -0500 @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Map; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; +import jdk.jpackage.test.FileAssociations; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.*; + +/** + * Test --linux-shortcut parameter. Output of the test should be + * shortcuthinttest_1.0-1_amd64.deb or shortcuthinttest-1.0-1.amd64.rpm package + * bundle. The output package should provide the same functionality as the + * default package and also create a desktop shortcut. + * + * Finding a shortcut of the application launcher through GUI depends on desktop + * environment. + * + * deb: + * Search online for `Ways To Open A Ubuntu Application` for instructions. + * + * rpm: + * + */ + +/* + * @test + * @summary jpackage with --linux-shortcut + * @library ../helpers + * @key jpackagePlatformPackage + * @requires jpackage.test.SQETest == null + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile ShortcutHintTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=ShortcutHintTest + */ + +/* + * @test + * @summary jpackage with --linux-shortcut + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @requires jpackage.test.SQETest != null + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile ShortcutHintTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=ShortcutHintTest.testBasic + */ + +public class ShortcutHintTest { + + @Test + public static void testBasic() { + createTest().addInitializer(cmd -> { + cmd.addArgument("--linux-shortcut"); + }).run(); + } + + private static PackageTest createTest() { + return new PackageTest() + .forTypes(PackageType.LINUX) + .configureHelloApp() + .addBundleDesktopIntegrationVerifier(true); + + } + + /** + * Adding `--icon` to jpackage command line should create desktop shortcut + * even though `--linux-shortcut` is omitted. + */ + @Test + public static void testCustomIcon() { + createTest().addInitializer(cmd -> { + cmd.setFakeRuntime(); + cmd.addArguments("--icon", TKit.TEST_SRC_ROOT.resolve( + "apps/dukeplug.png")); + }).run(); + } + + /** + * Adding `--file-associations` to jpackage command line should create + * desktop shortcut even though `--linux-shortcut` is omitted. + */ + @Test + public static void testFileAssociations() { + PackageTest test = createTest().addInitializer( + JPackageCommand::setFakeRuntime); + new FileAssociations("ShortcutHintTest_testFileAssociations").applyTo( + test); + test.run(); + } + + /** + * Additional launcher with icon should create desktop shortcut even though + * `--linux-shortcut` is omitted. + */ + @Test + public static void testAdditionaltLaunchers() { + createTest().addInitializer(cmd -> { + cmd.setFakeRuntime(); + + final String launcherName = "Foo"; + final Path propsFile = TKit.workDir().resolve( + launcherName + ".properties"); + + cmd.addArguments("--add-launcher", String.format("%s=%s", + launcherName, propsFile)); + + TKit.createPropertiesFile(propsFile, Map.entry("icon", + TKit.TEST_SRC_ROOT.resolve("apps/dukeplug.png").toString())); + }).run(); + } + + /** + * .desktop file from resource dir. + */ + @Test + public static void testDesktopFileFromResourceDir() { + final String expectedVersionString = "Version=12345678"; + TKit.withTempDirectory("resources", tempDir -> { + createTest().addInitializer(cmd -> { + cmd.setFakeRuntime(); + + cmd.addArgument("--linux-shortcut"); + cmd.addArguments("--resource-dir", tempDir); + + // Create custom .desktop file in resource directory + TKit.createTextFile(tempDir.resolve(cmd.name() + ".desktop"), + List.of( + "[Desktop Entry]", + "Name=APPLICATION_NAME", + "Exec=APPLICATION_LAUNCHER", + "Terminal=false", + "Type=Application", + "Categories=DEPLOY_BUNDLE_CATEGORY", + expectedVersionString + )); + }) + .addInstallVerifier(cmd -> { + Path desktopFile = cmd.appLayout().destktopIntegrationDirectory().resolve( + String.format("%s-%s.desktop", + LinuxHelper.getPackageName(cmd), cmd.name())); + TKit.assertFileExists(desktopFile); + TKit.assertTextStream(expectedVersionString) + .label(String.format("[%s] file", desktopFile)) + .predicate(String::equals) + .apply(Files.readAllLines(desktopFile).stream()); + }).run(); + }); + } +} --- /dev/null 2019-11-18 21:32:28.000000000 -0500 +++ new/test/jdk/tools/jpackage/macosx/MacPropertiesTest.java 2019-11-18 21:32:25.267015200 -0500 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Annotations.Parameter; + + +/** + * Test --mac-package-name, --mac-package-identifier parameters. + */ + +/* + * @test + * @summary jpackage with --mac-package-name, --mac-package-identifier + * @library ../helpers + * @build jdk.jpackage.test.* + * @requires (os.family == "mac") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile MacPropertiesTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=MacPropertiesTest + */ +public class MacPropertiesTest { + @Test + @Parameter("MacPackageNameTest") + public void testPackageName(String packageName) { + testParameterInAppImage("--mac-package-name", "CFBundleName", + packageName); + } + + @Test + @Parameter("Foo") + public void testPackageIdetifier(String packageId) { + testParameterInAppImage("--mac-package-identifier", "CFBundleIdentifier", + packageId); + } + + private static void testParameterInAppImage(String jpackageParameterName, + String plistKeyName, String value) { + JPackageCommand cmd = JPackageCommand.helloAppImage() + .addArguments(jpackageParameterName, value); + + cmd.executeAndAssertHelloAppImageCreated(); + + var plist = MacHelper.readPListFromAppImage(cmd.outputBundle()); + + TKit.assertEquals(value, plist.queryValue(plistKeyName), String.format( + "Check value of %s plist key", plistKeyName)); + } +} --- /dev/null 2019-11-18 21:32:39.000000000 -0500 +++ new/test/jdk/tools/jpackage/macosx/NameWithSpaceTest.java 2019-11-18 21:32:36.460161200 -0500 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.Annotations.Test; + +/** + * Name with space packaging test. Output of the test should be + * "Name With Space-*.*" package bundle. + * + * macOS only: + * + * Test should generates basic pkg and dmg. Name of packages and application itself + * should have name: "Name With Space". Package should be installed into "/Applications" + * folder and verified that it can be installed and run. + */ + +/* + * @test + * @summary jpackage test with name containing spaces + * @library ../helpers + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile NameWithSpaceTest.java + * @requires (os.family == "mac") + * @key jpackagePlatformPackage + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=NameWithSpaceTest + */ +public class NameWithSpaceTest { + + @Test + public static void test() { + new PackageTest() + .configureHelloApp() + .addBundleDesktopIntegrationVerifier(false) + .addInitializer(cmd -> { + cmd.setArgumentValue("--name", "Name With Space"); + }) + .run(); + } +} --- /dev/null 2019-11-18 21:32:51.000000000 -0500 +++ new/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java 2019-11-18 21:32:47.992392700 -0500 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; + +/** + * Tests generation of app image with --mac-sign and related arguments. Test will + * generate app image and verify signature of main launcher and app bundle itself. + * This test requires that machine is configured with test certificate for + * "Developer ID Application: jpackage.openjdk.java.net" in jpackagerTest keychain with + * always allowed access to this keychain for user which runs test. + */ + +/* + * @test + * @summary jpackage with --type app-image --mac-sign + * @library ../helpers + * @library /test/lib + * @library base + * @build SigningBase + * @build SigningCheck + * @build jtreg.SkippedException + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @requires (os.family == "mac") + * @run main/othervm -Xmx512m SigningAppImageTest + */ +public class SigningAppImageTest { + + public static void main(String[] args) throws Exception { + TKit.run(args, () -> { + SigningCheck.checkCertificates(); + + JPackageCommand cmd = JPackageCommand.helloAppImage(); + cmd.addArguments("--mac-sign", "--mac-signing-key-user-name", + SigningBase.DEV_NAME, "--mac-signing-keychain", + "jpackagerTest.keychain"); + cmd.executeAndAssertHelloAppImageCreated(); + + Path launcherPath = cmd.appLauncherPath(); + SigningBase.verifyCodesign(launcherPath, true); + + Path appImage = cmd.outputBundle(); + SigningBase.verifyCodesign(appImage, true); + SigningBase.verifySpctl(appImage, "exec"); + }); + } +} --- /dev/null 2019-11-18 21:33:02.000000000 -0500 +++ new/test/jdk/tools/jpackage/macosx/SigningPackageTest.java 2019-11-18 21:32:59.510645100 -0500 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import java.nio.file.Paths; +import jdk.jpackage.test.*; + +/** + * Tests generation of dmg and pkg with --mac-sign and related arguments. Test will + * generate pkg and verifies its signature. It verifies that dmg is not signed, but app + * image inside dmg is signed. This test requires that machine is configured with test + * certificate for "Developer ID Installer: jpackage.openjdk.java.net" in jpackagerTest + * keychain with always allowed access to this keychain for user which runs test. + */ + +/* + * @test + * @summary jpackage with --type pkg,dmg --mac-sign + * @library ../helpers + * @library /test/lib + * @library base + * @key jpackagePlatformPackage + * @build SigningBase + * @build SigningCheck + * @build jtreg.SkippedException + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @requires (os.family == "mac") + * @run main/othervm -Xmx512m SigningPackageTest + */ +public class SigningPackageTest { + + private static void verifyPKG(JPackageCommand cmd) { + Path outputBundle = cmd.outputBundle(); + SigningBase.verifyPkgutil(outputBundle); + SigningBase.verifySpctl(outputBundle, "install"); + } + + private static void verifyDMG(JPackageCommand cmd) { + Path outputBundle = cmd.outputBundle(); + SigningBase.verifyCodesign(outputBundle, false); + } + + private static void verifyAppImageInDMG(JPackageCommand cmd) { + MacHelper.withExplodedDmg(cmd, dmgImage -> { + Path launcherPath = dmgImage.resolve(Path.of("Contents", "MacOS", cmd.name())); + SigningBase.verifyCodesign(launcherPath, true); + SigningBase.verifyCodesign(dmgImage, true); + SigningBase.verifySpctl(dmgImage, "exec"); + }); + } + + public static void main(String[] args) throws Exception { + TKit.run(args, () -> { + SigningCheck.checkCertificates(); + + new PackageTest() + .configureHelloApp() + .forTypes(PackageType.MAC) + .addInitializer(cmd -> { + cmd.addArguments("--mac-sign", + "--mac-signing-key-user-name", SigningBase.DEV_NAME, + "--mac-signing-keychain", "jpackagerTest.keychain"); + }) + .forTypes(PackageType.MAC_PKG) + .addBundleVerifier(SigningPackageTest::verifyPKG) + .forTypes(PackageType.MAC_DMG) + .addBundleVerifier(SigningPackageTest::verifyDMG) + .addBundleVerifier(SigningPackageTest::verifyAppImageInDMG) + .run(); + }); + } +} --- /dev/null 2019-11-18 21:33:14.000000000 -0500 +++ new/test/jdk/tools/jpackage/macosx/base/SigningBase.java 2019-11-18 21:33:10.795593800 -0500 @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import java.util.List; + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.Executor; + +public class SigningBase { + + public static String DEV_NAME = "jpackage.openjdk.java.net"; + public static String APP_CERT + = "Developer ID Application: " + DEV_NAME; + public static String INSTALLER_CERT + = "Developer ID Installer: " + DEV_NAME; + public static String KEYCHAIN = "jpackagerTest.keychain"; + + private static void checkString(List result, String lookupString) { + TKit.assertTextStream(lookupString).predicate( + (line, what) -> line.trim().equals(what)).apply(result.stream()); + } + + private static List codesignResult(Path target, boolean signed) { + int exitCode = signed ? 0 : 1; + List result = new Executor() + .setExecutable("codesign") + .addArguments("--verify", "--deep", "--strict", "--verbose=2", + target.toString()) + .saveOutput() + .execute() + .assertExitCodeIs(exitCode).getOutput(); + + return result; + } + + private static void verifyCodesignResult(List result, Path target, + boolean signed) { + result.stream().forEachOrdered(TKit::trace); + if (signed) { + String lookupString = target.toString() + ": valid on disk"; + checkString(result, lookupString); + lookupString = target.toString() + ": satisfies its Designated Requirement"; + checkString(result, lookupString); + } else { + String lookupString = target.toString() + + ": code object is not signed at all"; + checkString(result, lookupString); + } + } + + private static List spctlResult(Path target, String type) { + List result = new Executor() + .setExecutable("/usr/sbin/spctl") + .addArguments("-vvv", "--assess", "--type", type, + target.toString()) + .executeAndGetOutput(); + + return result; + } + + private static void verifySpctlResult(List result, Path target, String type) { + result.stream().forEachOrdered(TKit::trace); + String lookupString = target.toString() + ": accepted"; + checkString(result, lookupString); + lookupString = "source=" + DEV_NAME; + checkString(result, lookupString); + if (type.equals("install")) { + lookupString = "origin=" + INSTALLER_CERT; + } else { + lookupString = "origin=" + APP_CERT; + } + checkString(result, lookupString); + } + + private static List pkgutilResult(Path target) { + List result = new Executor() + .setExecutable("/usr/sbin/pkgutil") + .addArguments("--check-signature", + target.toString()) + .executeAndGetOutput(); + + return result; + } + + private static void verifyPkgutilResult(List result) { + result.stream().forEachOrdered(TKit::trace); + String lookupString = "Status: signed by a certificate trusted for current user"; + checkString(result, lookupString); + lookupString = "1. " + INSTALLER_CERT; + checkString(result, lookupString); + } + + public static void verifyCodesign(Path target, boolean signed) { + List result = codesignResult(target, signed); + verifyCodesignResult(result, target, signed); + } + + public static void verifySpctl(Path target, String type) { + List result = spctlResult(target, type); + verifySpctlResult(result, target, type); + } + + public static void verifyPkgutil(Path target) { + List result = pkgutilResult(target); + verifyPkgutilResult(result); + } + +} --- /dev/null 2019-11-18 21:33:25.000000000 -0500 +++ new/test/jdk/tools/jpackage/macosx/base/SigningCheck.java 2019-11-18 21:33:21.791310700 -0500 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.Executor; + +import jdk.incubator.jpackage.internal.MacCertificate; + +public class SigningCheck { + + public static void checkCertificates() { + List result = findCertificate(SigningBase.APP_CERT, SigningBase.KEYCHAIN); + String key = findKey(SigningBase.APP_CERT, result); + validateCertificate(key); + validateCertificateTrust(SigningBase.APP_CERT); + + result = findCertificate(SigningBase.INSTALLER_CERT, SigningBase.KEYCHAIN); + key = findKey(SigningBase.INSTALLER_CERT, result); + validateCertificate(key); + validateCertificateTrust(SigningBase.INSTALLER_CERT); + } + + private static List findCertificate(String name, String keyChain) { + List result = new Executor() + .setExecutable("security") + .addArguments("find-certificate", "-c", name, "-a", keyChain) + .executeAndGetOutput(); + + return result; + } + + private static String findKey(String name, List result) { + Pattern p = Pattern.compile("\"alis\"=\"([^\"]+)\""); + Matcher m = p.matcher(result.stream().collect(Collectors.joining())); + if (!m.find()) { + TKit.trace("Did not found a key for '" + name + "'"); + return null; + } + String matchedKey = m.group(1); + if (m.find()) { + TKit.trace("Found more than one key for '" + name + "'"); + return null; + } + TKit.trace("Using key '" + matchedKey); + return matchedKey; + } + + private static void validateCertificate(String key) { + if (key != null) { + MacCertificate certificate = new MacCertificate(key); + if (!certificate.isValid()) { + TKit.throwSkippedException("Certifcate expired: " + key); + } else { + return; + } + } + + TKit.throwSkippedException("Cannot find required certifciates: " + key); + } + + private static void validateCertificateTrust(String name) { + List result = new Executor() + .setExecutable("security") + .addArguments("dump-trust-settings") + .executeAndGetOutput(); + result.stream().forEachOrdered(TKit::trace); + TKit.assertTextStream(name) + .predicate((line, what) -> line.trim().endsWith(what)) + .orElseThrow(() -> TKit.throwSkippedException( + "Certifcate not trusted by current user: " + name)) + .apply(result.stream()); + } + +} --- /dev/null 2019-11-18 21:33:36.000000000 -0500 +++ new/test/jdk/tools/jpackage/manage_packages.sh 2019-11-18 21:33:33.214867900 -0500 @@ -0,0 +1,231 @@ +#!/bin/bash + +# +# Script to install/uninstall packages produced by jpackage jtreg +# tests doing platform specific packaging. +# +# The script will install/uninstall all packages from the files +# found in the current directory or the one specified with command line option. +# +# When jtreg jpackage tests are executed with jpackage.test.output +# Java property set, produced package files (msi, exe, deb, rpm, etc.) will +# be saved in the directory specified with this property. +# +# Usage example: +# # Set directory where to save package files from jtreg jpackage tests +# JTREG_OUTPUT_DIR=/tmp/jpackage_jtreg_packages +# +# # Run tests and fill $JTREG_OUTPUT_DIR directory with package files +# jtreg -Djpackage.test.output=$JTREG_OUTPUT_DIR ... +# +# # Install all packages +# manage_pachages.sh -d $JTREG_OUTPUT_DIR +# +# # Uninstall all packages +# manage_pachages.sh -d $JTREG_OUTPUT_DIR -u +# + +# +# When using with MSI installers, Cygwin shell from which this script is +# executed should be started as administrator. Otherwise silent installation +# won't work. +# + +# Fail fast +set -e; set -o pipefail; + + +help_usage () +{ + echo "Usage: `basename $0` [OPTION]" + echo "Options:" + echo " -h - print this message" + echo " -v - verbose output" + echo " -d - path to directory where to look for package files" + echo " -u - uninstall packages instead of the default install" + echo " -t - dry run, print commands but don't execute them" +} + +error () +{ + echo "$@" > /dev/stderr +} + +fatal () +{ + error "$@" + exit 1 +} + +fatal_with_help_usage () +{ + error "$@" + help_usage + exit 1 +} + +# For macOS +if !(type "tac" &> /dev/null;) then + tac_cmd='tail -r' +else + tac_cmd=tac +fi + +# Directory where to look for package files. +package_dir=$PWD + +# Script debug. +verbose= + +# Operation mode. +mode=install + +dryrun= + +while getopts "vhd:ut" argname; do + case "$argname" in + v) verbose=yes;; + t) dryrun=yes;; + u) mode=uninstall;; + d) package_dir="$OPTARG";; + h) help_usage; exit 0;; + ?) help_usage; exit 1;; + esac +done +shift $(( OPTIND - 1 )) + +[ -d "$package_dir" ] || fatal_with_help_usage "Package directory [$package_dir] is not a directory" + +[ -z "$verbose" ] || set -x + + +function find_packages_of_type () +{ + # sort output alphabetically + find "$package_dir" -maxdepth 1 -type f -name '*.'"$1" | sort +} + +function find_packages () +{ + local package_suffixes=(deb rpm msi exe pkg dmg) + for suffix in "${package_suffixes[@]}"; do + if [ "$mode" == "uninstall" ]; then + packages=$(find_packages_of_type $suffix | $tac_cmd) + else + packages=$(find_packages_of_type $suffix) + fi + if [ -n "$packages" ]; then + package_type=$suffix + break; + fi + done +} + + +# RPM +install_cmd_rpm () +{ + echo sudo rpm --install "$@" +} +uninstall_cmd_rpm () +{ + local package_name=$(rpm -qp --queryformat '%{Name}' "$@") + echo sudo rpm -e "$package_name" +} + +# DEB +install_cmd_deb () +{ + echo sudo dpkg -i "$@" +} +uninstall_cmd_deb () +{ + local package_name=$(dpkg-deb -f "$@" Package) + echo sudo dpkg -r "$package_name" +} + +# MSI +install_cmd_msi () +{ + echo msiexec /qn /norestart /i $(cygpath -w "$@") +} +uninstall_cmd_msi () +{ + echo msiexec /qn /norestart /x $(cygpath -w "$@") +} + +# EXE +install_cmd_exe () +{ + echo "$@" +} +uninstall_cmd_exe () +{ + error No implemented +} + +# PKG +install_cmd_pkg () +{ + echo sudo /usr/sbin/installer -allowUntrusted -pkg "\"$@\"" -target / +} +uninstall_cmd_pkg () +{ + local pname=`basename $@` + local appname="$(cut -d'-' -f1 <<<"$pname")" + if [ "$appname" = "CommonInstallDirTest" ]; then + echo sudo rm -rf "/Applications/jpackage/\"$appname.app\"" + else + echo sudo rm -rf "/Applications/\"$appname.app\"" + fi +} + +# DMG +install_cmd_dmg () +{ + local pname=`basename $@` + local appname="$(cut -d'-' -f1 <<<"$pname")" + local command=() + if [ "$appname" = "CommonLicenseTest" ]; then + command+=("{" yes "|" hdiutil attach "\"$@\"" ">" /dev/null) + else + command+=("{" hdiutil attach "\"$@\"" ">" /dev/null) + fi + + command+=(";" sudo cp -R "\"/Volumes/$appname/$appname.app\"" /Applications ">" /dev/null) + command+=(";" hdiutil detach "\"/Volumes/$appname\"" ">" /dev/null ";}") + + echo "${command[@]}" +} +uninstall_cmd_dmg () +{ + local pname=`basename $@` + local appname="$(cut -d'-' -f1 <<<"$pname")" + echo sudo rm -rf "/Applications/\"$appname.app\"" +} + +# Find packages +packages= +find_packages +if [ -z "$packages" ]; then + echo "No packages found in $package_dir directory" + exit +fi + +# Build list of commands to execute +declare -a commands +IFS=$'\n' +for p in $packages; do + commands[${#commands[@]}]=$(${mode}_cmd_${package_type} "$p") +done + +if [ -z "$dryrun" ]; then + # Run commands + for cmd in "${commands[@]}"; do + echo Running: $cmd + eval $cmd || true; + done +else + # Print commands + for cmd in "${commands[@]}"; do echo $cmd; done +fi Binary files /dev/null and new/test/jdk/tools/jpackage/resources/icon.icns differ Binary files /dev/null and new/test/jdk/tools/jpackage/resources/icon.ico differ Binary files /dev/null and new/test/jdk/tools/jpackage/resources/icon.png differ --- /dev/null 2019-11-18 21:34:20.000000000 -0500 +++ new/test/jdk/tools/jpackage/run_tests.sh 2019-11-18 21:34:17.376699400 -0500 @@ -0,0 +1,275 @@ +#!/bin/bash + +# +# Script to run jpackage tests. +# + + +# Fail fast +set -e; set -o pipefail; + + +# Link obtained from https://openjdk.java.net/jtreg/ page +jtreg_bundle=https://ci.adoptopenjdk.net/view/Dependencies/job/jtreg/lastSuccessfulBuild/artifact/jtreg-4.2.0-tip.tar.gz +workdir=/tmp/jpackage_jtreg_testing +jtreg_jar=$workdir/jtreg/lib/jtreg.jar +jpackage_test_selector=test/jdk/tools/jpackage + + +find_packaging_tests () +{ + (cd "$open_jdk_with_jpackage_jtreg_tests" && \ + find "$jpackage_test_selector/$1" -type f -name '*.java' \ + | xargs grep -E -l '@key[[:space:]]+jpackagePlatformPackage') +} + + +find_all_packaging_tests () +{ + find_packaging_tests share + case "$(uname -s)" in + Darwin) + find_packaging_tests macosx;; + Linux) + find_packaging_tests linux;; + CYGWIN*|MINGW32*|MSYS*) + find_packaging_tests windows;; + *) + fatal Failed to detect OS type;; + esac +} + + +help_usage () +{ + echo "Usage: `basename $0` [options] [test_names]" + echo "Options:" + echo " -h - print this message" + echo " -v - verbose output" + echo " -c - keep jtreg cache" + echo " -a - run all, not only SQE tests" + echo " -d - dry run. Print jtreg command line, but don't execute it" + echo " -t - path to JDK to be tested [ mandatory ]" + echo " -j - path to local copy of openjdk repo with jpackage jtreg tests" + echo " Optional, default is openjdk repo where this script resides" + echo " -o - path to folder where to copy artifacts for testing." + echo " Optional, default is the current directory." + echo ' -r - value for `jpackage.test.runtime-image` property.' + echo " Optional, for jtreg tests debug purposes only." + echo ' -l - value for `jpackage.test.logfile` property.' + echo " Optional, for jtreg tests debug purposes only." + echo " -m - mode to run jtreg tests." + echo ' Should be one of `create`, `update`, `verify-install` or `verify-uninstall`.' + echo ' Optional, default mode is `update`.' + echo ' - `create`' + echo ' Remove all package bundles from the output directory before running jtreg tests.' + echo ' - `update`' + echo ' Run jtreg tests and overrite existing package bundles in the output directory.' + echo ' - `verify-install`' + echo ' Verify installed packages created with the previous run of the script.' + echo ' - `verify-uninstall`' + echo ' Verify packages created with the previous run of the script were uninstalled cleanly.' + echo ' - `print-default-tests`' + echo ' Print default list of packaging tests and exit.' +} + +error () +{ + echo "$@" > /dev/stderr +} + +fatal () +{ + error "$@" + exit 1 +} + +fatal_with_help_usage () +{ + error "$@" + help_usage + exit 1 +} + +if command -v cygpath &> /dev/null; then +to_native_path () +{ + cygpath -m "$@" +} +else +to_native_path () +{ + echo "$@" +} +fi + +exec_command () +{ + if [ -n "$dry_run" ]; then + echo "$@" + else + eval "$@" + fi +} + + +# Path to JDK to be tested. +test_jdk= + +# Path to local copy of open jdk repo with jpackage jtreg tests +# hg clone http://hg.openjdk.java.net/jdk/sandbox +# cd sandbox; hg update -r JDK-8200758-branch +open_jdk_with_jpackage_jtreg_tests=$(dirname $0)/../../../../ + +# Directory where to save artifacts for testing. +output_dir=$PWD + +# Script and jtreg debug. +verbose= +jtreg_verbose="-verbose:fail,error,summary" + +keep_jtreg_cache= + +# Mode in which to run jtreg tests +mode=update + +# jtreg extra arguments +declare -a jtreg_args + +# Run all tests +run_all_tests= + +mapfile -t tests < <(find_all_packaging_tests) + +while getopts "vahdct:j:o:r:m:l:" argname; do + case "$argname" in + v) verbose=yes;; + a) run_all_tests=yes;; + d) dry_run=yes;; + c) keep_jtreg_cache=yes;; + t) test_jdk="$OPTARG";; + j) open_jdk_with_jpackage_jtreg_tests="$OPTARG";; + o) output_dir="$OPTARG";; + r) runtime_dir="$OPTARG";; + l) logfile="$OPTARG";; + m) mode="$OPTARG";; + h) help_usage; exit 0;; + ?) help_usage; exit 1;; + esac +done +shift $(( OPTIND - 1 )) + +[ -z "$verbose" ] || { set -x; jtreg_verbose=-va; } + +if [ -z "$open_jdk_with_jpackage_jtreg_tests" ]; then + fatal_with_help_usage "Path to openjdk repo with jpackage jtreg tests not specified" +fi + +if [ "$mode" = "print-default-tests" ]; then + exec_command for t in ${tests[@]}";" do echo '$t;' done + exit +fi + +if [ -z "$test_jdk" ]; then + fatal_with_help_usage Path to test JDK not specified +fi + +if [ -z "$JAVA_HOME" ]; then + echo JAVA_HOME environment variable not set, will use java from test JDK [$test_jdk] to run jtreg + JAVA_HOME="$test_jdk" +fi +if [ ! -e "$JAVA_HOME/bin/java" ]; then + fatal JAVA_HOME variable is set to [$JAVA_HOME] value, but $JAVA_HOME/bin/java not found. +fi + +if [ -n "$runtime_dir" ]; then + if [ ! -d "$runtime_dir" ]; then + fatal 'Value of `-r` option is set to non-existing directory'. + fi + jtreg_args+=("-Djpackage.test.runtime-image=$(to_native_path "$(cd "$runtime_dir" && pwd)")") +fi + +if [ -n "$logfile" ]; then + if [ ! -d "$(dirname "$logfile")" ]; then + fatal 'Value of `-l` option specified a file in non-existing directory'. + fi + logfile="$(cd "$(dirname "$logfile")" && pwd)/$(basename "$logfile")" + jtreg_args+=("-Djpackage.test.logfile=$(to_native_path "$logfile")") +fi + +if [ "$mode" = create ]; then + true +elif [ "$mode" = update ]; then + true +elif [ "$mode" = verify-install ]; then + jtreg_args+=("-Djpackage.test.action=$mode") +elif [ "$mode" = verify-uninstall ]; then + jtreg_args+=("-Djpackage.test.action=$mode") +else + fatal_with_help_usage 'Invalid value of -m option:' [$mode] +fi + +if [ -z "$run_all_tests" ]; then + jtreg_args+=(-Djpackage.test.SQETest=yes) +fi + +# All remaining command line arguments are tests to run that should override the defaults +[ $# -eq 0 ] || tests=($@) + + +installJtreg () +{ + # Install jtreg if missing + if [ ! -f "$jtreg_jar" ]; then + exec_command mkdir -p "$workdir" + exec_command "(" cd "$workdir" "&&" wget "$jtreg_bundle" "&&" tar -xzf "$(basename $jtreg_bundle)" ";" rm -f "$(basename $jtreg_bundle)" ")" + fi +} + + +preRun () +{ + local xargs_args=(-t --no-run-if-empty rm) + if [ -n "$dry_run" ]; then + xargs_args=(--no-run-if-empty echo rm) + fi + + if [ ! -d "$output_dir" ]; then + exec_command mkdir -p "$output_dir" + fi + [ ! -d "$output_dir" ] || output_dir=$(cd "$output_dir" && pwd) + + # Clean output directory + [ "$mode" != "create" ] || find $output_dir -maxdepth 1 -type f -name '*.exe' -or -name '*.msi' -or -name '*.rpm' -or -name '*.deb' | xargs "${xargs_args[@]}" +} + + +run () +{ + local jtreg_cmdline=(\ + $JAVA_HOME/bin/java -jar $(to_native_path "$jtreg_jar") \ + "-Djpackage.test.output=$(to_native_path "$output_dir")" \ + "${jtreg_args[@]}" \ + -nr \ + "$jtreg_verbose" \ + -retain:all \ + -automatic \ + -ignore:run \ + -testjdk:"$(to_native_path $test_jdk)" \ + -dir:"$(to_native_path $open_jdk_with_jpackage_jtreg_tests)" \ + -reportDir:"$(to_native_path $workdir/run/results)" \ + -workDir:"$(to_native_path $workdir/run/support)" \ + "${tests[@]}" \ + ) + + # Clear previous results + [ -n "$keep_jtreg_cache" ] || exec_command rm -rf "$workdir"/run + + # Run jpackage jtreg tests to create artifacts for testing + exec_command ${jtreg_cmdline[@]} +} + + +installJtreg +preRun +run --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddLauncherBase.java 2019-11-18 21:34:32.417203000 -0500 +++ /dev/null 2019-11-18 21:34:33.000000000 -0500 @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateAppImageAddLauncherBase { - private static final String app = JPackagePath.getApp(); - private static final String app2 = JPackagePath.getAppSL(); - private static final String appOutput = JPackagePath.getAppOutputFile(); - private static final String appWorkingDir = JPackagePath.getAppWorkingDir(); - - // Note: quotes in argument for add launcher is not support by test - private static final String ARGUMENT1 = "argument 1"; - private static final String ARGUMENT2 = "argument 2"; - private static final String ARGUMENT3 = "argument 3"; - - private static final List arguments = new ArrayList<>(); - - private static final String PARAM1 = "-Dparam1=Some Param 1"; - private static final String PARAM2 = "-Dparam2=Some Param 2"; - private static final String PARAM3 = "-Dparam3=Some Param 3"; - - private static final List vmArguments = new ArrayList<>(); - - private static void validateResult(List args, List vmArgs) - throws Exception { - File outfile = new File(appWorkingDir + File.separator + appOutput); - if (!outfile.exists()) { - throw new AssertionError(appOutput + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - String[] result = output.split("\n"); - - if (result.length != (args.size() + vmArgs.size() + 2)) { - throw new AssertionError("Unexpected number of lines: " - + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: " + args.size())) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - - int index = 2; - for (String arg : args) { - if (!result[index].trim().equals(arg)) { - throw new AssertionError("Unexpected result[" + index + "]: " - + result[index]); - } - index++; - } - - for (String vmArg : vmArgs) { - if (!result[index].trim().equals(vmArg)) { - throw new AssertionError("Unexpected result[" + index + "]: " - + result[index]); - } - index++; - } - } - - private static void validate() throws Exception { - int retVal = JPackageHelper.execute(null, app); - if (retVal != 0) { - throw new AssertionError("Test application exited with error: " - + retVal); - } - validateResult(new ArrayList<>(), new ArrayList<>()); - - retVal = JPackageHelper.execute(null, app2); - if (retVal != 0) { - throw new AssertionError("Test application exited with error: " - + retVal); - } - validateResult(arguments, vmArguments); - } - - public static void testCreateAppImage(String [] cmd) throws Exception { - JPackageHelper.executeCLI(true, cmd); - validate(); - } - - public static void testCreateAppImageToolProvider(String [] cmd) throws Exception { - JPackageHelper.executeToolProvider(true, cmd); - validate(); - } - - public static void createSLProperties() throws Exception { - arguments.add(ARGUMENT1); - arguments.add(ARGUMENT2); - arguments.add(ARGUMENT3); - - String argumentsMap = - JPackageHelper.listToArgumentsMap(arguments, true); - - vmArguments.add(PARAM1); - vmArguments.add(PARAM2); - vmArguments.add(PARAM3); - - String vmArgumentsMap = - JPackageHelper.listToArgumentsMap(vmArguments, true); - - try (PrintWriter out = new PrintWriter(new BufferedWriter( - new FileWriter("sl.properties")))) { - out.println("arguments=" + argumentsMap); - out.println("java-options=" + vmArgumentsMap); - } - } - -} --- /dev/null 2019-11-18 21:34:34.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/AddLauncherBase.java 2019-11-18 21:34:28.773229700 -0500 @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +public class AddLauncherBase { + private static final String app = JPackagePath.getApp(); + private static final String appOutput = JPackagePath.getAppOutputFile(); + + // Note: quotes in argument for add launcher is not support by test + private static final String ARGUMENT1 = "argument 1"; + private static final String ARGUMENT2 = "argument 2"; + private static final String ARGUMENT3 = "argument 3"; + + private static final List arguments = new ArrayList<>(); + + private static final String PARAM1 = "-Dparam1=Some Param 1"; + private static final String PARAM2 = "-Dparam2=Some Param 2"; + private static final String PARAM3 = "-Dparam3=Some Param 3"; + + private static final List vmArguments = new ArrayList<>(); + private static final List empty = new ArrayList<>(); + + private static void validateResult(List args, List vmArgs) + throws Exception { + File outfile = new File(appOutput); + if (!outfile.exists()) { + throw new AssertionError(appOutput + " was not created"); + } + + String output = Files.readString(outfile.toPath()); + String[] result = output.split("\n"); + + int expected = 2 + args.size() + vmArgs.size(); + + if (result.length != expected) { + throw new AssertionError("Unexpected number of lines: " + + result.length + " expected: " + expected + " - results: " + output); + } + + if (!result[0].trim().endsWith("jpackage test application")) { + throw new AssertionError("Unexpected result[0]: " + result[0]); + } + + if (!result[1].trim().equals("args.length: " + args.size())) { + throw new AssertionError("Unexpected result[1]: " + result[1]); + } + + int index = 2; + for (String arg : args) { + if (!result[index].trim().equals(arg)) { + throw new AssertionError("Unexpected result[" + + index + "]: " + result[index]); + } + index++; + } + + for (String vmArg : vmArgs) { + if (!result[index].trim().equals(vmArg)) { + throw new AssertionError("Unexpected result[" + + index + "]: " + result[index]); + } + index++; + } + } + + private static void validate(boolean includeArgs, String name) + throws Exception { + int retVal = JPackageHelper.execute(null, app); + if (retVal != 0) { + throw new AssertionError("Test application " + app + + " exited with error: " + retVal); + } + validateResult(new ArrayList<>(), new ArrayList<>()); + + String app2 = JPackagePath.getAppSL(name); + retVal = JPackageHelper.execute(null, app2); + if (retVal != 0) { + throw new AssertionError("Test application " + app2 + + " exited with error: " + retVal); + } + if (includeArgs) { + validateResult(arguments, vmArguments); + } else { + validateResult(empty, empty); + } + } + + public static void testCreateAppImage(String [] cmd) throws Exception { + testCreateAppImage(cmd, true, "test2"); + } + + public static void testCreateAppImage(String [] cmd, + boolean includeArgs, String name) throws Exception { + JPackageHelper.executeCLI(true, cmd); + validate(includeArgs, name); + } + + public static void testCreateAppImageToolProvider(String [] cmd) + throws Exception { + testCreateAppImageToolProvider(cmd, true, "test2"); + } + + public static void testCreateAppImageToolProvider(String [] cmd, + boolean includeArgs, String name) throws Exception { + JPackageHelper.executeToolProvider(true, cmd); + validate(includeArgs, name); + } + + public static void testCreateAppImage(String [] cmd, + ArrayList argList, ArrayList optionList) + throws Exception { + JPackageHelper.executeCLI(true, cmd); + int retVal = JPackageHelper.execute(null, app); + if (retVal != 0) { + throw new AssertionError("Test application " + app + + " exited with error: " + retVal); + } + validateResult(argList, optionList); + String name = "test4"; + + String app2 = JPackagePath.getAppSL(name); + retVal = JPackageHelper.execute(null, app2); + if (retVal != 0) { + throw new AssertionError("Test application " + app2 + + " exited with error: " + retVal); + } + validateResult(arguments, vmArguments); + } + + public static void createSLProperties() throws Exception { + arguments.add(ARGUMENT1); + arguments.add(ARGUMENT2); + arguments.add(ARGUMENT3); + + String argumentsMap = + JPackageHelper.listToArgumentsMap(arguments, true); + + vmArguments.add(PARAM1); + vmArguments.add(PARAM2); + vmArguments.add(PARAM3); + + String vmArgumentsMap = + JPackageHelper.listToArgumentsMap(vmArguments, true); + + try (PrintWriter out = new PrintWriter(new BufferedWriter( + new FileWriter("sl.properties")))) { + out.println("arguments=" + argumentsMap); + out.println("java-options=" + vmArgumentsMap); + } + + try (PrintWriter out = new PrintWriter(new BufferedWriter( + new FileWriter("m1.properties")))) { + out.println("module=com.hello/com.hello.Hello"); + out.println("main-jar="); + } + + try (PrintWriter out = new PrintWriter(new BufferedWriter( + new FileWriter("j1.properties")))) { + out.println("main-jar hello.jar"); + out.println("main-class Hello"); + } + + + } + +} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddLauncherModuleTest.java 2019-11-18 21:34:54.699639200 -0500 +++ /dev/null 2019-11-18 21:34:56.000000000 -0500 @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create image with additional launcher test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageAddLauncherBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageAddLauncherModuleTest - */ -public class JPackageCreateAppImageAddLauncherModuleTest { - private static final String OUTPUT = "output"; - private static final String [] CMD = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input", - "--add-launcher", "test2=sl.properties"}; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloModule(); - JPackageCreateAppImageAddLauncherBase.createSLProperties(); - JPackageCreateAppImageAddLauncherBase.testCreateAppImage(CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageAddLauncherBase.testCreateAppImageToolProvider(CMD); - } - -} --- /dev/null 2019-11-18 21:34:56.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/AddLauncherModuleTest.java 2019-11-18 21:34:50.882938800 -0500 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @summary jpackage create image with additional launcher test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build AddLauncherBase + * @modules jdk.incubator.jpackage + * @run main/othervm -Xmx512m AddLauncherModuleTest + */ +public class AddLauncherModuleTest { + private static final String OUTPUT = "output"; + private static final String [] CMD = { + "--type", "app-image", + "--dest", OUTPUT, + "--name", "test", + "--module", "com.hello/com.hello.Hello", + "--module-path", "input", + "--add-launcher", "test2=sl.properties"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloModule(); + AddLauncherBase.createSLProperties(); + AddLauncherBase.testCreateAppImageToolProvider( + CMD); + } + +} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddLauncherTest.java 2019-11-18 21:35:16.928651900 -0500 +++ /dev/null 2019-11-18 21:35:18.000000000 -0500 @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create image with additional launcher test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageAddLauncherBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageAddLauncherTest - */ -public class JPackageCreateAppImageAddLauncherTest { - private static final String OUTPUT = "output"; - private static final String [] CMD = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--add-launcher", "test2=sl.properties"}; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - JPackageCreateAppImageAddLauncherBase.createSLProperties(); - JPackageCreateAppImageAddLauncherBase.testCreateAppImage(CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageAddLauncherBase.testCreateAppImageToolProvider(CMD); - } - -} --- /dev/null 2019-11-18 21:35:18.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/AddLauncherTest.java 2019-11-18 21:35:13.244693700 -0500 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.ArrayList; + +/* + * @test + * @summary jpackage create image with additional launcher test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build AddLauncherBase + * @modules jdk.incubator.jpackage + * @run main/othervm -Xmx512m AddLauncherTest + */ +public class AddLauncherTest { + private static final String OUTPUT = "output"; + private static final String [] CMD = { + "--type", "app-image", + "--input", "input", + "--dest", OUTPUT, + "--name", "test", + "--main-jar", "hello.jar", + "--main-class", "Hello", + "--add-launcher", "test2=sl.properties"}; + + private final static String OPT1 = "-Dparam1=xxx"; + private final static String OPT2 = "-Dparam2=yyy"; + private final static String OPT3 = "-Dparam3=zzz"; + private final static String ARG1 = "original-argument"; + + private static final String [] CMD1 = { + "--type", "app-image", + "--input", "input", + "--dest", OUTPUT, + "--name", "test", + "--main-jar", "hello.jar", + "--main-class", "Hello", + "--java-options", OPT1, + "--java-options", OPT2, + "--java-options", OPT3, + "--arguments", ARG1, + "--add-launcher", "test4=sl.properties"}; + + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + AddLauncherBase.createSLProperties(); + AddLauncherBase.testCreateAppImage(CMD); + + ArrayList argList = new ArrayList (); + argList.add(ARG1); + + ArrayList optList = new ArrayList (); + optList.add(OPT1); + optList.add(OPT2); + optList.add(OPT3); + + JPackageHelper.deleteOutputFolder(OUTPUT); + AddLauncherBase.testCreateAppImage(CMD1, argList, optList); + } + +} --- /dev/null 2019-11-18 21:35:38.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/AddLaunchersTest.java 2019-11-18 21:35:35.013284300 -0500 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @summary jpackage create image with additional launcher test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build AddLauncherBase + * @modules jdk.incubator.jpackage + * @run main/othervm -Xmx512m AddLaunchersTest + */ +public class AddLaunchersTest { + private static final String OUTPUT = "output"; + private static final String [] CMD1 = { + "--description", "Test non modular app with multiple add-launchers where one is modular app and other is non modular app", + "--type", "app-image", + "--input", "input", + "--dest", OUTPUT, + "--name", "test", + "--main-jar", "hello.jar", + "--main-class", "Hello", + "--module-path", "module", + "--add-modules", "com.hello,java.desktop", + "--add-launcher", "test3=j1.properties", + "--add-launcher", "test4=m1.properties"}; + + private static final String [] CMD2 = { + "--description", "Test modular app with multiple add-launchers where one is modular app and other is non modular app", + "--type", "app-image", + "--input", "input", + "--dest", OUTPUT, + "--name", "test", + "--module", "com.hello/com.hello.Hello", + "--module-path", "module", + "--add-launcher", "test5=jl.properties", + "--add-launcher", "test6=m1.properties"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + JPackageHelper.createHelloModule(); + AddLauncherBase.createSLProperties(); + + JPackageHelper.deleteOutputFolder(OUTPUT); + AddLauncherBase.testCreateAppImageToolProvider( + CMD1, false, "test3"); + + JPackageHelper.deleteOutputFolder(OUTPUT); + AddLauncherBase.testCreateAppImage( + CMD1, false, "test4"); + + JPackageHelper.deleteOutputFolder(OUTPUT); + AddLauncherBase.testCreateAppImage( + CMD2, false, "test5"); + + JPackageHelper.deleteOutputFolder(OUTPUT); + AddLauncherBase.testCreateAppImageToolProvider( + CMD2, false, "test6"); + + } + +} --- /dev/null 2019-11-18 21:35:49.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/AdditionalLaunchersTest.java 2019-11-18 21:35:46.233022000 -0500 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.Optional; +import java.lang.invoke.MethodHandles; +import jdk.jpackage.test.HelloApp; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.FileAssociations; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.TKit; + +/** + * Test --add-launcher parameter. Output of the test should be + * additionallauncherstest*.* installer. The output installer should provide the + * same functionality as the default installer (see description of the default + * installer in SimplePackageTest.java) plus install three extra application + * launchers. + */ + +/* + * @test + * @summary jpackage with --add-launcher + * @key jpackagePlatformPackage + * @library ../helpers + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile AdditionalLaunchersTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=AdditionalLaunchersTest + */ + +public class AdditionalLaunchersTest { + + @Test + public void test() { + // Configure a bunch of additional launchers and also setup + // file association to make sure it will be linked only to the main + // launcher. + + PackageTest packageTest = new PackageTest().configureHelloApp(); + packageTest.addInitializer(cmd -> { + cmd.addArguments("--arguments", "Duke", "--arguments", "is", + "--arguments", "the", "--arguments", "King"); + }); + + new FileAssociations( + MethodHandles.lookup().lookupClass().getSimpleName()).applyTo( + packageTest); + + new AdditionalLauncher("Baz2").setArguments().applyTo(packageTest); + new AdditionalLauncher("foo").setArguments("yep!").applyTo(packageTest); + + AdditionalLauncher barLauncher = new AdditionalLauncher("Bar").setArguments( + "one", "two", "three"); + if (TKit.isLinux()) { + barLauncher.setIcon(TKit.TEST_SRC_ROOT.resolve("apps/dukeplug.png")); + } + barLauncher.applyTo(packageTest); + + packageTest.run(); + } + + private static Path replaceFileName(Path path, String newFileName) { + String fname = path.getFileName().toString(); + int lastDotIndex = fname.lastIndexOf("."); + if (lastDotIndex != -1) { + fname = newFileName + fname.substring(lastDotIndex); + } else { + fname = newFileName; + } + return path.getParent().resolve(fname); + } + + static class AdditionalLauncher { + + AdditionalLauncher(String name) { + this.name = name; + } + + AdditionalLauncher setArguments(String... args) { + arguments = List.of(args); + return this; + } + + AdditionalLauncher setIcon(Path iconPath) { + icon = iconPath; + return this; + } + + void applyTo(PackageTest test) { + final Path propsFile = TKit.workDir().resolve(name + ".properties"); + + test.addInitializer(cmd -> { + cmd.addArguments("--add-launcher", String.format("%s=%s", name, + propsFile)); + + Map properties = new HashMap<>(); + if (arguments != null) { + properties.put("arguments", String.join(" ", + arguments.toArray(String[]::new))); + } + + if (icon != null) { + properties.put("icon", icon.toAbsolutePath().toString()); + } + + TKit.createPropertiesFile(propsFile, properties); + }); + test.addInstallVerifier(cmd -> { + Path launcherPath = replaceFileName(cmd.appLauncherPath(), name); + + TKit.assertExecutableFileExists(launcherPath); + + if (cmd.isFakeRuntime(String.format( + "Not running %s launcher", launcherPath))) { + return; + } + HelloApp.executeAndVerifyOutput(launcherPath, + Optional.ofNullable(arguments).orElse(List.of()).toArray( + String[]::new)); + }); + test.addUninstallVerifier(cmd -> { + Path launcherPath = replaceFileName(cmd.appLauncherPath(), name); + + TKit.assertPathExists(launcherPath, false); + }); + } + + private List arguments; + private Path icon; + private final String name; + } +} --- /dev/null 2019-11-18 21:36:00.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/AppImagePackageTest.java 2019-11-18 21:35:57.393673600 -0500 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.JPackageCommand; + +/** + * Test --app-image parameter. The output installer should provide the same + * functionality as the default installer (see description of the default + * installer in SimplePackageTest.java) + */ + +/* + * @test + * @summary jpackage with --app-image + * @key jpackagePlatformPackage + * @library ../helpers + * @requires (jpackage.test.SQETest == null) + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=540 -Xmx512m AppImagePackageTest + */ +public class AppImagePackageTest { + + public static void main(String[] args) { + TKit.run(args, () -> { + Path appimageOutput = Path.of("appimage"); + + JPackageCommand appImageCmd = JPackageCommand.helloAppImage() + .setArgumentValue("--dest", appimageOutput) + .addArguments("--type", "app-image"); + + PackageTest packageTest = new PackageTest(); + if (packageTest.getAction() == PackageTest.Action.CREATE) { + appImageCmd.execute(); + } + + packageTest.addInitializer(cmd -> { + Path appimageInput = appimageOutput.resolve(appImageCmd.name()); + + if (PackageType.MAC.contains(cmd.packageType())) { + // Why so complicated on macOS? + appimageInput = Path.of(appimageInput.toString() + ".app"); + } + + cmd.addArguments("--app-image", appimageInput); + cmd.removeArgumentWithValue("--input"); + }).addBundleDesktopIntegrationVerifier(false).run(); + }); + } +} --- /dev/null 2019-11-18 21:36:12.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/ArgumentsTest.java 2019-11-18 21:36:08.735176200 -0500 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import java.util.List; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.HelloApp; +import jdk.jpackage.test.Functional.ThrowingConsumer; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.Annotations.*; + + +/* + * Tricky arguments used in the test require a bunch of levels of character + * escaping for proper encoding them in a single string to be used as a value of + * `--arguments` option. String with encoded arguments doesn't go through the + * system to jpackage executable as is because OS is interpreting escape + * characters. This is true for Windows at least. + * + * String mapping performed by the system corrupts the string and jpackage exits + * with error. There is no problem with string corruption when jpackage is used + * as tool provider. This is not jpackage issue, so just always run this test + * with jpackage used as tool provider. + * / + +/* + * @test + * @summary jpackage create image with --arguments test + * @library ../helpers + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile ArgumentsTest.java + * @run main/othervm -Xmx512m jdk.jpackage.test.Main + * --jpt-run=ArgumentsTest + */ +public class ArgumentsTest { + + @BeforeEach + public static void useJPackageToolProvider() { + JPackageCommand.useToolProviderByDefault(); + } + + @Test + @Parameter("Goodbye") + @Parameter("com.hello/com.hello.Hello") + public static void testApp(String javaAppDesc) { + testIt(javaAppDesc, null); + } + + private static void testIt(String javaAppDesc, + ThrowingConsumer initializer) { + + JPackageCommand cmd = JPackageCommand.helloAppImage(javaAppDesc).addArguments( + "--arguments", JPackageCommand.escapeAndJoin(TRICKY_ARGUMENTS)); + if (initializer != null) { + ThrowingConsumer.toConsumer(initializer).accept(cmd); + } + + cmd.executeAndAssertImageCreated(); + + Path launcherPath = cmd.appLauncherPath(); + if (!cmd.isFakeRuntime(String.format( + "Not running [%s] launcher", launcherPath))) { + HelloApp.executeAndVerifyOutput(launcherPath, TRICKY_ARGUMENTS); + } + } + + private final static List TRICKY_ARGUMENTS = List.of( + "argument", + "Some Arguments", + "Value \"with\" quotes" + ); +} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageBase.java 2019-11-18 21:36:23.592534800 -0500 +++ /dev/null 2019-11-18 21:36:25.000000000 -0500 @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; - -public abstract class JPackageCreateAppImageBase { - private static final String app = JPackagePath.getApp(); - private static final String appOutput = JPackagePath.getAppOutputFile(); - private static final String appWorkingDir = JPackagePath.getAppWorkingDir(); - - private static void validateResult(String[] result) throws Exception { - if (result.length != 2) { - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: 0")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - } - - private static void validate() throws Exception { - int retVal = JPackageHelper.execute(null, app); - if (retVal != 0) { - throw new AssertionError( - "Test application exited with error: " + retVal); - } - - File outfile = new File(appWorkingDir + File.separator + appOutput); - if (!outfile.exists()) { - throw new AssertionError(appOutput + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - String[] result = output.split("\n"); - validateResult(result); - } - - public static void testCreateAppImage(String [] cmd) throws Exception { - JPackageHelper.executeCLI(true, cmd); - validate(); - } - - public static void testCreateAppImageToolProvider(String [] cmd) throws Exception { - JPackageHelper.executeToolProvider(true, cmd); - validate(); - } -} --- /dev/null 2019-11-18 21:36:25.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/Base.java 2019-11-18 21:36:20.075765300 -0500 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Files; +import java.nio.file.Path; + +public abstract class Base { + private static final String appOutput = JPackagePath.getAppOutputFile(); + + private static void validateResult(String[] result) throws Exception { + if (result.length != 2) { + throw new AssertionError( + "Unexpected number of lines: " + result.length); + } + + if (!result[0].trim().endsWith("jpackage test application")) { + throw new AssertionError("Unexpected result[0]: " + result[0]); + } + + if (!result[1].trim().equals("args.length: 0")) { + throw new AssertionError("Unexpected result[1]: " + result[1]); + } + } + + public static void validate(String app) throws Exception { + Path outPath = Path.of(appOutput); + int retVal = JPackageHelper.execute(null, app); + + if (outPath.toFile().exists()) { + System.out.println("output contents: "); + System.out.println(Files.readString(outPath) + "\n"); + } else { + System.out.println("no output file: " + outPath + + " from command: " + app); + } + + if (retVal != 0) { + throw new AssertionError( + "Test application (" + app + ") exited with error: " + retVal); + } + + if (!outPath.toFile().exists()) { + throw new AssertionError(appOutput + " was not created"); + } + + String output = Files.readString(outPath); + String[] result = JPackageHelper.splitAndFilter(output); + validateResult(result); + } + + public static void testCreateAppImage(String [] cmd) throws Exception { + JPackageHelper.executeCLI(true, cmd); + validate(JPackagePath.getApp()); + } + + public static void testCreateAppImageToolProvider(String [] cmd) throws Exception { + JPackageHelper.executeToolProvider(true, cmd); + validate(JPackagePath.getApp()); + } +} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageErrorTest.java 2019-11-18 21:36:45.584345100 -0500 +++ /dev/null 2019-11-18 21:36:47.000000000 -0500 @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create app image error test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageErrorTest - */ -import java.util.*; -import java.io.*; -import java.nio.*; -import java.nio.file.*; -import java.nio.file.attribute.*; - -public class JPackageCreateAppImageErrorTest { - - private static final String OUTPUT = "output"; - - private static final String ARG1 = "--no-such-argument"; - private static final String EXPECTED1 = - "Invalid Option: [--no-such-argument]"; - private static final String ARG2 = "--output"; - private static final String EXPECTED2 = "Mode is not specified"; - - private static final String [] CMD1 = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "non-existant.jar", - }; - private static final String EXP1 = "main jar does not exist"; - - private static final String [] CMD2 = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - }; - private static final String EXP2 = "class was not specified nor was"; - - private static void validate(String output, String expected, boolean single) - throws Exception { - String[] result = output.split("\n"); - if (single && result.length != 1) { - System.err.println(output); - throw new AssertionError("Unexpected multiple lines of output: " - + output); - } - - if (!result[0].trim().contains(expected)) { - throw new AssertionError("Unexpected output: " + result[0] - + " - expected output to contain: " + expected); - } - } - - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - - validate(JPackageHelper.executeToolProvider(false, ARG1), EXPECTED1, true); - validate(JPackageHelper.executeToolProvider(false, ARG2), EXPECTED2, true); - - JPackageHelper.deleteOutputFolder(OUTPUT); - validate(JPackageHelper.executeToolProvider(false, CMD1), EXP1, false); - - JPackageHelper.deleteOutputFolder(OUTPUT); - validate(JPackageHelper.executeToolProvider(false, CMD2), EXP2, false); - - } - -} --- /dev/null 2019-11-18 21:36:47.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/ErrorTest.java 2019-11-18 21:36:42.083753800 -0500 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @summary jpackage create app image error test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build Base + * @modules jdk.incubator.jpackage + * @run main/othervm -Xmx512m ErrorTest + */ +import java.util.*; +import java.io.*; +import java.nio.*; +import java.nio.file.*; +import java.nio.file.attribute.*; + +public class ErrorTest { + + private static final String OUTPUT = "output"; + + private static final String ARG1 = "--no-such-argument"; + private static final String EXPECTED1 = + "Invalid Option: [--no-such-argument]"; + private static final String ARG2 = "--dest"; + private static final String EXPECTED2 = "--main-jar or --module"; + + private static final String [] CMD1 = { + "--type", "app-image", + "--input", "input", + "--dest", OUTPUT, + "--name", "test", + "--main-jar", "non-existant.jar", + }; + private static final String EXP1 = "main jar does not exist"; + + private static final String [] CMD2 = { + "--type", "app-image", + "--input", "input", + "--dest", OUTPUT, + "--name", "test", + "--main-jar", "hello.jar", + }; + private static final String EXP2 = "class was not specified nor was"; + + private static void validate(String output, String expected, boolean single) + throws Exception { + String[] result = JPackageHelper.splitAndFilter(output); + if (single && result.length != 1) { + System.err.println(output); + throw new AssertionError("Unexpected multiple lines of output: " + + output); + } + + if (!result[0].trim().contains(expected)) { + throw new AssertionError("Unexpected output: " + result[0] + + " - expected output to contain: " + expected); + } + } + + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + + validate(JPackageHelper.executeToolProvider(false, + "--type", "app-image", ARG1), EXPECTED1, true); + validate(JPackageHelper.executeToolProvider(false, + "--type", "app-image", ARG2), EXPECTED2, true); + + JPackageHelper.deleteOutputFolder(OUTPUT); + validate(JPackageHelper.executeToolProvider(false, CMD1), EXP1, false); + + JPackageHelper.deleteOutputFolder(OUTPUT); + validate(JPackageHelper.executeToolProvider(false, CMD2), EXP2, false); + + } + +} --- /dev/null 2019-11-18 21:37:06.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/FileAssociationsTest.java 2019-11-18 21:37:03.433672600 -0500 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.FileAssociations; +import jdk.jpackage.test.Annotations.Test; + +/** + * Test --file-associations parameter. Output of the test should be + * fileassociationstest*.* installer. The output installer should provide the + * same functionality as the default installer (see description of the default + * installer in SimplePackageTest.java) plus configure file associations. After + * installation files with ".jptest1" and ".jptest2" suffixes should be + * associated with the test app. + * + * Suggested test scenario is to create empty file with ".jptest1" suffix, + * double click on it and make sure that test application was launched in + * response to double click event with the path to test .jptest1 file on the + * commend line. The same applies to ".jptest2" suffix. + * + * On Linux use "echo > foo.jptest1" and not "touch foo.jptest1" to create test + * file as empty files are always interpreted as plain text and will not be + * opened with the test app. This is a known bug. + * + * Icon associated with the main launcher should be associated with files with + * ".jptest1" suffix. Different icon should be associated with files with with + * ".jptest2" suffix. Icon for files with ".jptest1" suffix is platform specific + * and is one of 'icon.*' files in test/jdk/tools/jpackage/resources directory. + */ + +/* + * @test + * @summary jpackage with --file-associations + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile FileAssociationsTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=FileAssociationsTest + */ +public class FileAssociationsTest { + @Test + public static void test() { + PackageTest packageTest = new PackageTest(); + + // Not supported + packageTest.excludeTypes(PackageType.MAC_DMG); + + new FileAssociations("jptest1").applyTo(packageTest); + + Path icon = TKit.TEST_SRC_ROOT.resolve(Path.of("resources", "icon" + + TKit.ICON_SUFFIX)); + + icon = TKit.createRelativePathCopy(icon); + + new FileAssociations("jptest2") + .setFilename("fa2") + .setIcon(icon) + .applyTo(packageTest); + + packageTest.run(); + } +} --- /dev/null 2019-11-18 21:37:17.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/IconTest.java 2019-11-18 21:37:14.538656100 -0500 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import jdk.incubator.jpackage.internal.IOUtils; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.Functional; +import jdk.jpackage.test.Annotations.*; +import jdk.jpackage.test.JPackageCommand; + +/* + * @test + * @summary jpackage create image with custom icon + * @library ../helpers + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile IconTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=IconTest + */ + +public class IconTest { + @Test + public static void testResourceDir() throws IOException { + TKit.withTempDirectory("resources", tempDir -> { + JPackageCommand cmd = JPackageCommand.helloAppImage() + .addArguments("--resource-dir", tempDir); + + Files.copy(GOLDEN_ICON, tempDir.resolve(appIconFileName(cmd)), + StandardCopyOption.REPLACE_EXISTING); + + testIt(cmd); + }); + } + + @Test + @Parameter("true") + @Parameter("false") + public static void testParameter(boolean relativePath) throws IOException { + final Path iconPath; + if (relativePath) { + iconPath = TKit.createRelativePathCopy(GOLDEN_ICON); + } else { + iconPath = GOLDEN_ICON; + } + + testIt(JPackageCommand.helloAppImage().addArguments("--icon", iconPath)); + } + + private static String appIconFileName(JPackageCommand cmd) { + return IOUtils.replaceSuffix(cmd.appLauncherPath().getFileName(), + TKit.ICON_SUFFIX).toString(); + } + + private static void testIt(JPackageCommand cmd) throws IOException { + cmd.executeAndAssertHelloAppImageCreated(); + + Path iconPath = cmd.appLayout().destktopIntegrationDirectory().resolve( + appIconFileName(cmd)); + + TKit.assertFileExists(iconPath); + TKit.assertTrue(-1 == Files.mismatch(GOLDEN_ICON, iconPath), + String.format( + "Check application icon file [%s] is a copy of source icon file [%s]", + iconPath, GOLDEN_ICON)); + } + + private final static Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( + "resources", "icon" + TKit.ICON_SUFFIX)); +} --- /dev/null 2019-11-18 21:37:28.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/InstallDirTest.java 2019-11-18 21:37:25.636840400 -0500 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Functional; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.Annotations.Parameter; + +/** + * Test --install-dir parameter. Output of the test should be + * commoninstalldirtest*.* package bundle. The output package should provide the + * same functionality as the default package but install test application in + * specified directory. + * + * Linux: + * + * Application should be installed in /opt/jpackage/commoninstalldirtest folder. + * + * Mac: + * + * Application should be installed in /Applications/jpackage/commoninstalldirtest.app + * folder. + * + * Windows: + * + * Application should be installed in %ProgramFiles%/TestVendor/InstallDirTest1234 + * folder. + */ + +/* + * @test + * @summary jpackage with --install-dir + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @compile InstallDirTest.java + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=InstallDirTest.testCommon + */ + +/* + * @test + * @summary jpackage with --install-dir + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @compile InstallDirTest.java + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @requires (os.family == "linux") + * @requires (jpackage.test.SQETest == null) + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=InstallDirTest.testLinuxInvalid,testLinuxUnsupported + */ +public class InstallDirTest { + + public static void testCommon() { + final Map INSTALL_DIRS = Functional.identity(() -> { + Map reply = new HashMap<>(); + reply.put(PackageType.WIN_MSI, Path.of("TestVendor\\InstallDirTest1234")); + reply.put(PackageType.WIN_EXE, reply.get(PackageType.WIN_MSI)); + + reply.put(PackageType.LINUX_DEB, Path.of("/opt/jpackage")); + reply.put(PackageType.LINUX_RPM, reply.get(PackageType.LINUX_DEB)); + + reply.put(PackageType.MAC_PKG, Path.of("/Applications/jpackage")); + + return reply; + }).get(); + + new PackageTest().excludeTypes(PackageType.MAC_DMG).configureHelloApp() + .addInitializer(cmd -> { + cmd.addArguments("--install-dir", INSTALL_DIRS.get( + cmd.packageType())); + }).run(); + } + + @Parameter("/") + @Parameter(".") + @Parameter("foo") + @Parameter("/opt/foo/.././.") + public static void testLinuxInvalid(String installDir) { + testLinuxBad(installDir, "Invalid installation directory"); + } + + @Parameter("/usr") + @Parameter("/usr/local") + @Parameter("/usr/foo") + public static void testLinuxUnsupported(String installDir) { + testLinuxBad(installDir, "currently unsupported"); + } + + private static void testLinuxBad(String installDir, + String errorMessageSubstring) { + new PackageTest().configureHelloApp() + .setExpectedExitCode(1) + .forTypes(PackageType.LINUX) + .addInitializer(cmd -> { + cmd.addArguments("--install-dir", installDir); + cmd.saveConsoleOutput(true); + }) + .addBundleVerifier((cmd, result) -> { + String errorMessage = JPackageCommand.filterOutput( + result.getOutput().stream()).filter(line -> line.contains( + errorMessageSubstring)).findFirst().orElse(null); + TKit.assertNotNull(errorMessage, String.format( + "Check output contains [%s] substring", + errorMessageSubstring)); + }) + .run(); + } +} --- old/test/jdk/tools/jpackage/JPackageInvalidArgTest.java 2019-11-18 21:37:40.585925700 -0500 +++ /dev/null 2019-11-18 21:37:42.000000000 -0500 @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage invalid argument test - * @library helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageInvalidArgTest - */ -public class JPackageInvalidArgTest { - - private static final String ARG1 = "--no-such-argument"; - private static final String ARG2 = "--output"; - private static final String RESULT1 = - "Invalid Option: [--no-such-argument]"; - private static final String RESULT2 = "Mode is not specified"; - - private static void validate(String arg, String output) throws Exception { - String[] result = output.split("\n"); - if (result.length != 1) { - System.err.println(output); - throw new AssertionError("Invalid number of lines in output: " - + result.length); - } - - if (arg.equals(ARG1)) { - if (!result[0].trim().contains(RESULT1)) { - System.err.println("Expected: " + RESULT1); - System.err.println("Actual: " + result[0]); - throw new AssertionError("Unexpected output: " + result[0]); - } - } else if (arg.equals(ARG2)) { - if (!result[0].trim().contains(RESULT2)) { - System.err.println("Expected: " + RESULT2); - System.err.println("Actual: " + result[0]); - throw new AssertionError("Unexpected output: " + result[0]); - } - } - } - - private static void testInvalidArg() throws Exception { - String output = JPackageHelper.executeCLI(false, ARG1); - validate(ARG1, output); - output = JPackageHelper.executeCLI(false, ARG2); - validate(ARG2, output); - } - - private static void testInvalidArgToolProvider() throws Exception { - String output = JPackageHelper.executeToolProvider(false, ARG1); - validate(ARG1, output); - output = JPackageHelper.executeToolProvider(false, ARG2); - validate(ARG2, output); - } - - public static void main(String[] args) throws Exception { - testInvalidArg(); - testInvalidArgToolProvider(); - } - -} --- /dev/null 2019-11-18 21:37:42.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/InvalidArgTest.java 2019-11-18 21:37:36.985493200 -0500 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @summary jpackage invalid argument test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @modules jdk.incubator.jpackage + * @run main/othervm -Xmx512m InvalidArgTest + */ +public class InvalidArgTest { + + private static final String ARG1 = "--no-such-argument"; + private static final String ARG2 = "--dest"; + private static final String RESULT1 = + "Invalid Option: [--no-such-argument]"; + private static final String RESULT2 = "--main-jar or --module"; + + private static void validate(String arg, String output) throws Exception { + String[] result = JPackageHelper.splitAndFilter(output); + if (result.length != 1) { + System.err.println(output); + throw new AssertionError("Invalid number of lines in output: " + + result.length); + } + + if (arg.equals(ARG1)) { + if (!result[0].trim().contains(RESULT1)) { + System.err.println("Expected: " + RESULT1); + System.err.println("Actual: " + result[0]); + throw new AssertionError("Unexpected output: " + result[0]); + } + } else if (arg.equals(ARG2)) { + if (!result[0].trim().contains(RESULT2)) { + System.err.println("Expected: " + RESULT2); + System.err.println("Actual: " + result[0]); + throw new AssertionError("Unexpected output: " + result[0]); + } + } + } + + private static void testInvalidArg() throws Exception { + String output = JPackageHelper.executeCLI(false, + "--type", "app-image", ARG1); + validate(ARG1, output); + + output = JPackageHelper.executeCLI(false, + "--type", "app-image", ARG2); + validate(ARG2, output); + } + + private static void testInvalidArgToolProvider() throws Exception { + String output = JPackageHelper.executeToolProvider(false, + "--type", "app-image", ARG1); + validate(ARG1, output); + + output = JPackageHelper.executeToolProvider(false, + "--type", "app-image", ARG2); + validate(ARG2, output); + } + + public static void main(String[] args) throws Exception { + testInvalidArg(); + testInvalidArgToolProvider(); + } + +} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJavaOptionsBase.java 2019-11-18 21:38:02.167059800 -0500 +++ /dev/null 2019-11-18 21:38:03.000000000 -0500 @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateAppImageJavaOptionsBase { - - private static final String app = JPackagePath.getApp(); - private static final String appOutput = JPackagePath.getAppOutputFile(); - private static final String appWorkingDir = JPackagePath.getAppWorkingDir(); - - private static final String ARGUMENT1 = "-Dparam1=Some Param 1"; - private static final String ARGUMENT2 = "-Dparam2=Some \"Param\" 2"; - private static final String ARGUMENT3 = - "-Dparam3=Some \"Param\" with \" 3"; - - private static final List arguments = new ArrayList<>(); - - private static void initArguments(boolean toolProvider, String [] cmd) { - if (arguments.isEmpty()) { - arguments.add(ARGUMENT1); - arguments.add(ARGUMENT2); - arguments.add(ARGUMENT3); - } - - String argumentsMap = JPackageHelper.listToArgumentsMap(arguments, - toolProvider); - cmd[cmd.length - 1] = argumentsMap; - } - - private static void initArguments2(boolean toolProvider, String [] cmd) { - int index = cmd.length - 6; - - cmd[index++] = "--java-options"; - arguments.clear(); - arguments.add(ARGUMENT1); - cmd[index++] = JPackageHelper.listToArgumentsMap(arguments, - toolProvider); - - cmd[index++] = "--java-options"; - arguments.clear(); - arguments.add(ARGUMENT2); - cmd[index++] = JPackageHelper.listToArgumentsMap(arguments, - toolProvider); - - cmd[index++] = "--java-options"; - arguments.clear(); - arguments.add(ARGUMENT3); - cmd[index++] = JPackageHelper.listToArgumentsMap(arguments, - toolProvider); - - arguments.clear(); - arguments.add(ARGUMENT1); - arguments.add(ARGUMENT2); - arguments.add(ARGUMENT3); - } - - private static void validateResult(String[] result, List args) - throws Exception { - if (result.length != (args.size() + 2)) { - for (String r : result) { - System.err.println(r.trim()); - } - throw new AssertionError("Unexpected number of lines: " - + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: 0")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - - int index = 2; - for (String arg : args) { - if (!result[index].trim().equals(arg)) { - throw new AssertionError("Unexpected result[" + index + "]: " - + result[index]); - } - index++; - } - } - - private static void validate(List expectedArgs) throws Exception { - int retVal = JPackageHelper.execute(null, app); - if (retVal != 0) { - throw new AssertionError("Test application exited with error: " - + retVal); - } - - File outfile = new File(appWorkingDir + File.separator + appOutput); - if (!outfile.exists()) { - throw new AssertionError(appOutput + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - String[] result = output.split("\n"); - validateResult(result, expectedArgs); - } - - public static void testCreateAppImageJavaOptions(String [] cmd) throws Exception { - initArguments(false, cmd); - JPackageHelper.executeCLI(true, cmd); - validate(arguments); - } - - public static void testCreateAppImageJavaOptionsToolProvider(String [] cmd) throws Exception { - initArguments(true, cmd); - JPackageHelper.executeToolProvider(true, cmd); - validate(arguments); - } - - public static void testCreateAppImageJavaOptions2(String [] cmd) throws Exception { - initArguments2(false, cmd); - JPackageHelper.executeCLI(true, cmd); - validate(arguments); - } - - public static void testCreateAppImageJavaOptions2ToolProvider(String [] cmd) throws Exception { - initArguments2(true, cmd); - JPackageHelper.executeToolProvider(true, cmd); - validate(arguments); - } -} --- /dev/null 2019-11-18 21:38:04.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/JavaOptionsBase.java 2019-11-18 21:37:58.560578800 -0500 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.File; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +public class JavaOptionsBase { + + private static final String app = JPackagePath.getApp(); + private static final String appOutput = JPackagePath.getAppOutputFile(); + + private static final String ARGUMENT1 = "-Dparam1=Some Param 1"; + private static final String ARGUMENT2 = "-Dparam2=Some \"Param\" 2"; + private static final String ARGUMENT3 = + "-Dparam3=Some \"Param\" with \" 3"; + + private static final List arguments = new ArrayList<>(); + + private static void initArguments(boolean toolProvider, String [] cmd) { + if (arguments.isEmpty()) { + arguments.add(ARGUMENT1); + arguments.add(ARGUMENT2); + arguments.add(ARGUMENT3); + } + + String argumentsMap = JPackageHelper.listToArgumentsMap(arguments, + toolProvider); + cmd[cmd.length - 1] = argumentsMap; + } + + private static void initArguments2(boolean toolProvider, String [] cmd) { + int index = cmd.length - 6; + + cmd[index++] = "--java-options"; + arguments.clear(); + arguments.add(ARGUMENT1); + cmd[index++] = JPackageHelper.listToArgumentsMap(arguments, + toolProvider); + + cmd[index++] = "--java-options"; + arguments.clear(); + arguments.add(ARGUMENT2); + cmd[index++] = JPackageHelper.listToArgumentsMap(arguments, + toolProvider); + + cmd[index++] = "--java-options"; + arguments.clear(); + arguments.add(ARGUMENT3); + cmd[index++] = JPackageHelper.listToArgumentsMap(arguments, + toolProvider); + + arguments.clear(); + arguments.add(ARGUMENT1); + arguments.add(ARGUMENT2); + arguments.add(ARGUMENT3); + } + + private static void validateResult(String[] result, List args) + throws Exception { + if (result.length != (args.size() + 2)) { + for (String r : result) { + System.err.println(r.trim()); + } + throw new AssertionError("Unexpected number of lines: " + + result.length); + } + + if (!result[0].trim().equals("jpackage test application")) { + throw new AssertionError("Unexpected result[0]: " + result[0]); + } + + if (!result[1].trim().equals("args.length: 0")) { + throw new AssertionError("Unexpected result[1]: " + result[1]); + } + + int index = 2; + for (String arg : args) { + if (!result[index].trim().equals(arg)) { + throw new AssertionError("Unexpected result[" + index + "]: " + + result[index]); + } + index++; + } + } + + private static void validate(List expectedArgs) throws Exception { + int retVal = JPackageHelper.execute(null, app); + if (retVal != 0) { + throw new AssertionError("Test application exited with error: " + + retVal); + } + + File outfile = new File(appOutput); + if (!outfile.exists()) { + throw new AssertionError(appOutput + " was not created"); + } + + String output = Files.readString(outfile.toPath()); + String[] result = JPackageHelper.splitAndFilter(output); + validateResult(result, expectedArgs); + } + + public static void testCreateAppImageJavaOptions(String [] cmd) throws Exception { + initArguments(false, cmd); + JPackageHelper.executeCLI(true, cmd); + validate(arguments); + } + + public static void testCreateAppImageJavaOptionsToolProvider(String [] cmd) throws Exception { + initArguments(true, cmd); + JPackageHelper.executeToolProvider(true, cmd); + validate(arguments); + } + + public static void testCreateAppImageJavaOptions2(String [] cmd) throws Exception { + initArguments2(false, cmd); + JPackageHelper.executeCLI(true, cmd); + validate(arguments); + } + + public static void testCreateAppImageJavaOptions2ToolProvider(String [] cmd) throws Exception { + initArguments2(true, cmd); + JPackageHelper.executeToolProvider(true, cmd); + validate(arguments); + } +} --- /dev/null 2019-11-18 21:38:23.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/JavaOptionsEqualsTest.java 2019-11-18 21:38:20.625496500 -0500 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.File; +import java.nio.file.Files; + +/* + * @test + * @summary jpackage create image with --java-options test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build JavaOptionsBase + * @modules jdk.incubator.jpackage + * @run main/othervm -Xmx512m JavaOptionsEqualsTest + */ +public class JavaOptionsEqualsTest { + + private static final String app = JPackagePath.getApp(); + + private static final String OUTPUT = "output"; + + private static final String WARNING_1 + = "WARNING: Unknown module: me.mymodule.foo"; + + private static final String WARNING_2 + = "WARNING: Unknown module: other.mod.bar"; + + private static final String[] CMD = { + "--type", "app-image", + "--input", "input", + "--description", "the two options below should cause two app execution " + + "Warnings with two lines output saying: " + + "WARNING: Unknown module: ", + "--dest", OUTPUT, + "--name", "test", + "--main-jar", "hello.jar", + "--main-class", "Hello", + "--java-options", + "--add-exports=java.base/sun.util=me.mymodule.foo,ALL-UNNAMED", + "--java-options", + "--add-exports=java.base/sun.security.util=other.mod.bar,ALL-UNNAMED", + }; + + private static void validate() throws Exception { + File outfile = new File("app.out"); + + int retVal = JPackageHelper.execute(outfile, app); + if (retVal != 0) { + throw new AssertionError( + "Test application exited with error: " + retVal); + } + + if (!outfile.exists()) { + throw new AssertionError( + "outfile: " + outfile + " was not created"); + } + + String output = Files.readString(outfile.toPath()); + System.out.println("App output:"); + System.out.print(output); + + String[] result = JPackageHelper.splitAndFilter(output); + if (result.length != 4) { + throw new AssertionError( + "Unexpected number of lines: " + result.length + + " - output: " + output); + } + + String nextWarning = WARNING_1; + if (!result[0].startsWith(nextWarning)){ + nextWarning = WARNING_2; + if (!result[0].startsWith(WARNING_2)){ + throw new AssertionError("Unexpected result[0]: " + result[0]); + } else { + nextWarning = WARNING_1; + } + } + + if (!result[1].startsWith(nextWarning)) { + throw new AssertionError("Unexpected result[1]: " + result[1]); + } + + if (!result[2].trim().endsWith("jpackage test application")) { + throw new AssertionError("Unexpected result[2]: " + result[2]); + } + + if (!result[3].trim().equals("args.length: 0")) { + throw new AssertionError("Unexpected result[3]: " + result[3]); + } + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + String output = JPackageHelper.executeCLI(true, CMD); + validate(); + + JPackageHelper.deleteOutputFolder(OUTPUT); + output = JPackageHelper.executeToolProvider(true, CMD); + validate(); + } + +} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJavaOptionsModuleTest.java 2019-11-18 21:38:35.618132200 -0500 +++ /dev/null 2019-11-18 21:38:37.000000000 -0500 @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create image with --java-options test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageJavaOptionsBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageJavaOptionsModuleTest - */ -public class JPackageCreateAppImageJavaOptionsModuleTest { - private static final String OUTPUT = "output"; - - private static final String[] CMD = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input", - "--java-options", "TBD"}; - - private static final String[] CMD2 = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input", - "--java-options", "TBD", - "--java-options", "TBD", - "--java-options", "TBD"}; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloModule(); - - JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions(CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptionsToolProvider(CMD); - - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions2(CMD2); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions2ToolProvider(CMD2); - } - -} --- /dev/null 2019-11-18 21:38:37.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/JavaOptionsModuleTest.java 2019-11-18 21:38:31.821778400 -0500 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary jpackage create image with --java-options test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build JavaOptionsBase + * @modules jdk.incubator.jpackage + * @run main/othervm -Xmx512m JavaOptionsModuleTest + */ +public class JavaOptionsModuleTest { + private static final String OUTPUT = "output"; + + private static final String[] CMD = { + "--type", "app-image", + "--dest", OUTPUT, + "--name", "test", + "--module", "com.hello/com.hello.Hello", + "--module-path", "input", + "--java-options", "TBD"}; + + private static final String[] CMD2 = { + "--type", "app-image", + "--dest", OUTPUT, + "--name", "test", + "--module", "com.hello/com.hello.Hello", + "--module-path", "input", + "--java-options", "TBD", + "--java-options", "TBD", + "--java-options", "TBD"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloModule(); + + JavaOptionsBase.testCreateAppImageJavaOptions(CMD); + JPackageHelper.deleteOutputFolder(OUTPUT); + JavaOptionsBase.testCreateAppImageJavaOptionsToolProvider(CMD); + + JPackageHelper.deleteOutputFolder(OUTPUT); + JavaOptionsBase.testCreateAppImageJavaOptions2(CMD2); + JPackageHelper.deleteOutputFolder(OUTPUT); + JavaOptionsBase.testCreateAppImageJavaOptions2ToolProvider(CMD2); + } + +} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJavaOptionsTest.java 2019-11-18 21:38:58.320207900 -0500 +++ /dev/null 2019-11-18 21:38:59.000000000 -0500 @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create image with --java-options test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageJavaOptionsBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageJavaOptionsTest - */ -public class JPackageCreateAppImageJavaOptionsTest { - private static final String OUTPUT = "output"; - - private static final String[] CMD = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--java-options", "TBD"}; - - private static final String[] CMD2 = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--java-options", "TBD", - "--java-options", "TBD", - "--java-options", "TBD"}; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions(CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptionsToolProvider(CMD); - - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions2(CMD2); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions2ToolProvider(CMD2); - } - -} --- /dev/null 2019-11-18 21:39:00.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/JavaOptionsTest.java 2019-11-18 21:38:54.857717100 -0500 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary jpackage create image with --java-options test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build JavaOptionsBase + * @modules jdk.incubator.jpackage + * @run main/othervm -Xmx512m JavaOptionsTest + */ +public class JavaOptionsTest { + private static final String OUTPUT = "output"; + + private static final String[] CMD = { + "--type", "app-image", + "--input", "input", + "--dest", OUTPUT, + "--name", "test", + "--main-jar", "hello.jar", + "--main-class", "Hello", + "--java-options", "TBD"}; + + private static final String[] CMD2 = { + "--type", "app-image", + "--input", "input", + "--dest", OUTPUT, + "--name", "test", + "--main-jar", "hello.jar", + "--main-class", "Hello", + "--java-options", "TBD", + "--java-options", "TBD", + "--java-options", "TBD"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + JavaOptionsBase.testCreateAppImageJavaOptions(CMD); + JPackageHelper.deleteOutputFolder(OUTPUT); + JavaOptionsBase.testCreateAppImageJavaOptionsToolProvider(CMD); + + JPackageHelper.deleteOutputFolder(OUTPUT); + JavaOptionsBase.testCreateAppImageJavaOptions2(CMD2); + JPackageHelper.deleteOutputFolder(OUTPUT); + JavaOptionsBase.testCreateAppImageJavaOptions2ToolProvider(CMD2); + } + +} --- /dev/null 2019-11-18 21:39:19.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/LicenseTest.java 2019-11-18 21:39:16.763263500 -0500 @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.Function; +import java.util.stream.Collectors; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.LinuxHelper; +import jdk.jpackage.test.Executor; +import jdk.jpackage.test.TKit; + +/** + * Test --license-file parameter. Output of the test should be commonlicensetest*.* + * package bundle. The output package should provide the same functionality as + * the default package and also incorporate license information from + * test/jdk/tools/jpackage/resources/license.txt file from OpenJDK repo. + * + * deb: + * + * Package should install license file /opt/commonlicensetest/share/doc/copyright + * file. + * + * rpm: + * + * Package should install license file in + * %{_defaultlicensedir}/licensetest-1.0/license.txt file. + * + * Mac: + * + * Windows + * + * Installer should display license text matching contents of the license file + * during installation. + */ + +/* + * @test + * @summary jpackage with --license-file + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @compile LicenseTest.java + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=LicenseTest.testCommon + */ + +/* + * @test + * @summary jpackage with --license-file + * @library ../helpers + * @key jpackagePlatformPackage + * @compile LicenseTest.java + * @requires (os.family == "linux") + * @requires (jpackage.test.SQETest == null) + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=LicenseTest.testCustomDebianCopyright + * --jpt-run=LicenseTest.testCustomDebianCopyrightSubst + */ + +public class LicenseTest { + public static void testCommon() { + new PackageTest().configureHelloApp() + .addInitializer(cmd -> { + cmd.addArguments("--license-file", TKit.createRelativePathCopy( + LICENSE_FILE)); + }) + .forTypes(PackageType.LINUX) + .addBundleVerifier(cmd -> { + verifyLicenseFileInLinuxPackage(cmd, linuxLicenseFile(cmd)); + }) + .addInstallVerifier(cmd -> { + TKit.assertReadableFileExists(linuxLicenseFile(cmd)); + }) + .addUninstallVerifier(cmd -> { + verifyLicenseFileNotInstalledLinux(linuxLicenseFile(cmd)); + }) + .forTypes(PackageType.LINUX_DEB) + .addInstallVerifier(cmd -> { + verifyLicenseFileInstalledDebian(debLicenseFile(cmd)); + }) + .forTypes(PackageType.LINUX_RPM) + .addInstallVerifier(cmd -> { + verifyLicenseFileInstalledRpm(rpmLicenseFile(cmd)); + }) + .run(); + } + + public static void testCustomDebianCopyright() { + new CustomDebianCopyrightTest().run(); + } + + public static void testCustomDebianCopyrightSubst() { + new CustomDebianCopyrightTest().withSubstitution(true).run(); + } + + private static Path rpmLicenseFile(JPackageCommand cmd) { + final Path licenseRoot = Path.of( + new Executor() + .setExecutable("rpm") + .addArguments("--eval", "%{_defaultlicensedir}") + .executeAndGetFirstLineOfOutput()); + final Path licensePath = licenseRoot.resolve(String.format("%s-%s", + LinuxHelper.getPackageName(cmd), cmd.version())).resolve( + LICENSE_FILE.getFileName()); + return licensePath; + } + + private static Path linuxLicenseFile(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.LINUX); + switch (cmd.packageType()) { + case LINUX_DEB: + return debLicenseFile(cmd); + + case LINUX_RPM: + return rpmLicenseFile(cmd); + + default: + return null; + } + } + + private static Path debLicenseFile(JPackageCommand cmd) { + return cmd.appInstallationDirectory().resolve("share/doc/copyright"); + } + + private static void verifyLicenseFileInLinuxPackage(JPackageCommand cmd, + Path expectedLicensePath) { + TKit.assertTrue(LinuxHelper.getPackageFiles(cmd).filter(path -> path.equals( + expectedLicensePath)).findFirst().orElse(null) != null, + String.format("Check license file [%s] is in %s package", + expectedLicensePath, LinuxHelper.getPackageName(cmd))); + } + + private static void verifyLicenseFileInstalledRpm(Path licenseFile) throws + IOException { + TKit.assertStringListEquals(Files.readAllLines(LICENSE_FILE), + Files.readAllLines(licenseFile), String.format( + "Check contents of package license file [%s] are the same as contents of source license file [%s]", + licenseFile, LICENSE_FILE)); + } + + private static void verifyLicenseFileInstalledDebian(Path licenseFile) + throws IOException { + + List actualLines = Files.readAllLines(licenseFile).stream().dropWhile( + line -> !line.startsWith("License:")).collect( + Collectors.toList()); + // Remove leading `License:` followed by the whitespace from the first text line. + actualLines.set(0, actualLines.get(0).split("\\s+", 2)[1]); + + actualLines = DEBIAN_COPYRIGT_FILE_STRIPPER.apply(actualLines); + + TKit.assertNotEquals(0, String.join("\n", actualLines).length(), + "Check stripped license text is not empty"); + + TKit.assertStringListEquals(DEBIAN_COPYRIGT_FILE_STRIPPER.apply( + Files.readAllLines(LICENSE_FILE)), actualLines, String.format( + "Check subset of package license file [%s] is a match of the source license file [%s]", + licenseFile, LICENSE_FILE)); + } + + private static void verifyLicenseFileNotInstalledLinux(Path licenseFile) { + TKit.assertPathExists(licenseFile.getParent(), false); + } + + private static class CustomDebianCopyrightTest { + CustomDebianCopyrightTest() { + withSubstitution(false); + } + + private List licenseFileText(String copyright, String licenseText) { + List lines = new ArrayList(List.of( + String.format("Copyright=%s", copyright), + "Foo", + "Bar", + "Buz")); + lines.addAll(List.of(licenseText.split("\\R", -1))); + return lines; + } + + private List licenseFileText() { + if (withSubstitution) { + return licenseFileText("APPLICATION_COPYRIGHT", + "APPLICATION_LICENSE_TEXT"); + } else { + return expetedLicenseFileText(); + } + } + + private List expetedLicenseFileText() { + return licenseFileText(copyright, licenseText); + } + + CustomDebianCopyrightTest withSubstitution(boolean v) { + withSubstitution = v; + // Different values just to make easy to figure out from the test log which test was executed. + if (v) { + copyright = "Duke (C)"; + licenseText = "The quick brown fox\n jumps over the lazy dog"; + } else { + copyright = "Java (C)"; + licenseText = "How vexingly quick daft zebras jump!"; + } + return this; + } + + void run() { + final Path srcLicenseFile = TKit.workDir().resolve("license"); + new PackageTest().configureHelloApp().forTypes(PackageType.LINUX_DEB) + .addInitializer(cmd -> { + // Create source license file. + Files.write(srcLicenseFile, List.of( + licenseText.split("\\R", -1))); + + cmd.setFakeRuntime(); + cmd.setArgumentValue("--name", String.format("%s%s", + withSubstitution ? "CustomDebianCopyrightWithSubst" : "CustomDebianCopyright", + cmd.name())); + cmd.addArguments("--license-file", srcLicenseFile); + cmd.addArguments("--copyright", copyright); + cmd.addArguments("--resource-dir", RESOURCE_DIR); + + // Create copyright template file in a resource dir. + Files.createDirectories(RESOURCE_DIR); + Files.write(RESOURCE_DIR.resolve("copyright"), + licenseFileText()); + }) + .addInstallVerifier(cmd -> { + Path installedLicenseFile = debLicenseFile(cmd); + TKit.assertStringListEquals(expetedLicenseFileText(), + DEBIAN_COPYRIGT_FILE_STRIPPER.apply(Files.readAllLines( + installedLicenseFile)), String.format( + "Check contents of package license file [%s] are the same as contents of source license file [%s]", + installedLicenseFile, srcLicenseFile)); + }) + .run(); + } + + private boolean withSubstitution; + private String copyright; + private String licenseText; + + private final Path RESOURCE_DIR = TKit.workDir().resolve("resources"); + } + + private static final Path LICENSE_FILE = TKit.TEST_SRC_ROOT.resolve( + Path.of("resources", "license.txt")); + + private static final Function, List> DEBIAN_COPYRIGT_FILE_STRIPPER = (lines) -> Arrays.asList( + String.join("\n", lines).stripTrailing().split("\n")); +} --- old/test/jdk/tools/jpackage/JPackageMissingArgumentsTest.java 2019-11-18 21:39:31.700042000 -0500 +++ /dev/null 2019-11-18 21:39:33.000000000 -0500 @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create image missing arguments test - * @library helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageMissingArgumentsTest - */ - -public class JPackageMissingArgumentsTest { - private static final String [] RESULT_1 = {"--output"}; - private static final String [] CMD_1 = { - "create-app-image", - "--input", "input", - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - }; - - private static final String [] RESULT_2 = {"--input"}; - private static final String [] CMD_2 = { - "create-app-image", - "--output", "output", - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - }; - - private static final String [] RESULT_3 = {"--input", "--app-image"}; - private static final String [] CMD_3 = { - "create-installer", - "--output", "output", - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - }; - - private static final String [] RESULT_4 = {"main class was not specified"}; - private static final String [] CMD_4 = { - "create-app-image", - "--input", "input", - "--output", "output", - "--name", "test", - "--main-jar", "hello.jar", - }; - - private static final String [] RESULT_5 = {"--main-jar"}; - private static final String [] CMD_5 = { - "create-app-image", - "--input", "input", - "--output", "output", - "--name", "test", - "--main-class", "Hello", - }; - - private static final String [] RESULT_6 = {"--module-path", "--runtime-image"}; - private static final String [] CMD_6 = { - "create-app-image", - "--output", "output", - "--name", "test", - "--module", "com.hello/com.hello.Hello", - }; - - private static final String [] RESULT_7 = {"--module-path", "--runtime-image", - "--app-image"}; - private static final String [] CMD_7 = { - "create-installer", - "--output", "output", - "--name", "test", - "--module", "com.hello/com.hello.Hello", - }; - - private static void validate(String output, String [] expected, - boolean single) throws Exception { - String[] result = output.split("\n"); - if (single && result.length != 1) { - System.err.println(output); - throw new AssertionError("Invalid number of lines in output: " - + result.length); - } - - for (String s : expected) { - if (!result[0].contains(s)) { - System.err.println("Expected to contain: " + s); - System.err.println("Actual: " + result[0]); - throw new AssertionError("Unexpected error message"); - } - } - } - - private static void testMissingArg() throws Exception { - String output = JPackageHelper.executeCLI(false, CMD_1); - validate(output, RESULT_1, true); - - output = JPackageHelper.executeCLI(false, CMD_2); - validate(output, RESULT_2, true); - - output = JPackageHelper.executeCLI(false, CMD_3); - validate(output, RESULT_3, true); - - output = JPackageHelper.executeCLI(false, CMD_4); - validate(output, RESULT_4, false); - - output = JPackageHelper.executeCLI(false, CMD_5); - validate(output, RESULT_5, true); - - output = JPackageHelper.executeCLI(false, CMD_6); - validate(output, RESULT_6, true); - - output = JPackageHelper.executeCLI(false, CMD_7); - validate(output, RESULT_7, true); - } - - private static void testMissingArgToolProvider() throws Exception { - String output = JPackageHelper.executeToolProvider(false, CMD_1); - validate(output, RESULT_1, true); - - output = JPackageHelper.executeToolProvider(false, CMD_2); - validate(output, RESULT_2, true); - - output = JPackageHelper.executeToolProvider(false, CMD_3); - validate(output, RESULT_3, true); - - output = JPackageHelper.executeToolProvider(false, CMD_4); - validate(output, RESULT_4, false); - - output = JPackageHelper.executeToolProvider(false, CMD_5); - validate(output, RESULT_5, true); - - output = JPackageHelper.executeToolProvider(false, CMD_6); - validate(output, RESULT_6, true); - - output = JPackageHelper.executeToolProvider(false, CMD_7); - validate(output, RESULT_7, true); - } - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - testMissingArg(); - testMissingArgToolProvider(); - } - -} --- /dev/null 2019-11-18 21:39:33.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/MissingArgumentsTest.java 2019-11-18 21:39:28.227952400 -0500 @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @summary jpackage create image missing arguments test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @modules jdk.incubator.jpackage + * @run main/othervm -Xmx512m MissingArgumentsTest + */ + +public class MissingArgumentsTest { + private static final String [] RESULT_1 = {"--input"}; + private static final String [] CMD_1 = { + "--type", "app-image", + "--dest", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--main-class", "Hello", + }; + + private static final String [] RESULT_2 = {"--input", "--app-image"}; + private static final String [] CMD_2 = { + "--type", "app-image", + "--type", "invalid-type", + "--dest", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--main-class", "Hello", + }; + + private static final String [] RESULT_3 = {"main class was not specified"}; + private static final String [] CMD_3 = { + "--type", "app-image", + "--input", "input", + "--dest", "output", + "--name", "test", + "--main-jar", "hello.jar", + }; + + private static final String [] RESULT_4 = {"--main-jar"}; + private static final String [] CMD_4 = { + "--type", "app-image", + "--input", "input", + "--dest", "output", + "--name", "test", + "--main-class", "Hello", + }; + + private static final String [] RESULT_5 = {"--module-path", "--runtime-image"}; + private static final String [] CMD_5 = { + "--type", "app-image", + "--dest", "output", + "--name", "test", + "--module", "com.hello/com.hello.Hello", + }; + + private static final String [] RESULT_6 = {"--module-path", "--runtime-image", + "--app-image"}; + private static final String [] CMD_6 = { + "--type", "invalid-type", + "--dest", "output", + "--name", "test", + "--module", "com.hello/com.hello.Hello", + }; + + private static void validate(String output, String [] expected, + boolean single) throws Exception { + String[] result = JPackageHelper.splitAndFilter(output); + if (single && result.length != 1) { + System.err.println(output); + throw new AssertionError("Invalid number of lines in output: " + + result.length); + } + + for (String s : expected) { + if (!result[0].contains(s)) { + System.err.println("Expected to contain: " + s); + System.err.println("Actual: " + result[0]); + throw new AssertionError("Unexpected error message"); + } + } + } + + private static void testMissingArg() throws Exception { + String output = JPackageHelper.executeCLI(false, CMD_1); + validate(output, RESULT_1, true); + + output = JPackageHelper.executeCLI(false, CMD_2); + validate(output, RESULT_2, true); + + output = JPackageHelper.executeCLI(false, CMD_3); + validate(output, RESULT_3, false); + + output = JPackageHelper.executeCLI(false, CMD_4); + validate(output, RESULT_4, true); + + output = JPackageHelper.executeCLI(false, CMD_5); + validate(output, RESULT_5, true); + + output = JPackageHelper.executeCLI(false, CMD_6); + validate(output, RESULT_6, true); + + } + + private static void testMissingArgToolProvider() throws Exception { + String output = JPackageHelper.executeToolProvider(false, CMD_1); + validate(output, RESULT_1, true); + + output = JPackageHelper.executeToolProvider(false, CMD_2); + validate(output, RESULT_2, true); + + output = JPackageHelper.executeToolProvider(false, CMD_3); + validate(output, RESULT_3, false); + + output = JPackageHelper.executeToolProvider(false, CMD_4); + validate(output, RESULT_4, true); + + output = JPackageHelper.executeToolProvider(false, CMD_5); + validate(output, RESULT_5, true); + + output = JPackageHelper.executeToolProvider(false, CMD_6); + validate(output, RESULT_6, true); + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + testMissingArg(); + testMissingArgToolProvider(); + } + +} --- /dev/null 2019-11-18 21:39:53.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/RuntimePackageTest.java 2019-11-18 21:39:49.943024500 -0500 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import jdk.jpackage.test.*; +import jdk.jpackage.test.Annotations.Test; + +/** + * Test --runtime-image parameter. + * Output of the test should be RuntimePackageTest*.* installer. + * The installer should install Java Runtime without an application. + * Installation directory should not have "app" subfolder and should not have + * an application launcher. + * + * + * Windows: + * + * Java runtime should be installed in %ProgramFiles%\RuntimePackageTest directory. + */ + +/* + * @test + * @summary jpackage with --runtime-image + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @comment Temporary disable for Linux and OSX until functionality implemented + * @requires (os.family != "mac") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile RuntimePackageTest.java + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=RuntimePackageTest + */ +public class RuntimePackageTest { + + @Test + public static void test() { + new PackageTest() + .addInitializer(cmd -> { + cmd.addArguments("--runtime-image", Optional.ofNullable( + JPackageCommand.DEFAULT_RUNTIME_IMAGE).orElse(Path.of( + System.getProperty("java.home")))); + // Remove --input parameter from jpackage command line as we don't + // create input directory in the test and jpackage fails + // if --input references non existant directory. + cmd.removeArgumentWithValue("--input"); + }) + .addInstallVerifier(cmd -> { + Set srcRuntime = listFiles(Path.of(cmd.getArgumentValue("--runtime-image"))); + Set dstRuntime = listFiles(cmd.appRuntimeDirectory()); + + Set intersection = new HashSet<>(srcRuntime); + intersection.retainAll(dstRuntime); + + srcRuntime.removeAll(intersection); + dstRuntime.removeAll(intersection); + + assertFileListEmpty(srcRuntime, "Missing"); + assertFileListEmpty(dstRuntime, "Unexpected"); + }) + .run(); + } + + private static Set listFiles(Path root) throws IOException { + try (var files = Files.walk(root)) { + return files.map(root::relativize).collect(Collectors.toSet()); + } + } + + private static void assertFileListEmpty(Set paths, String msg) { + TKit.assertTrue(paths.isEmpty(), String.format( + "Check there are no %s files in installed image", + msg.toLowerCase()), () -> { + String msg2 = String.format("%s %d files", msg, paths.size()); + TKit.trace(msg2 + ":"); + paths.stream().map(Path::toString).sorted().forEachOrdered( + TKit::trace); + TKit.trace("Done"); + }); + } +} --- /dev/null 2019-11-18 21:40:04.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/SimplePackageTest.java 2019-11-18 21:40:01.047244100 -0500 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.Annotations.Test; + +/** + * Simple platform specific packaging test. Output of the test should be + * simplepackagetest*.* package bundle. + * + * Windows: + * + * The installer should not have license text. It should not have an option + * to change the default installation directory. + * Test application should be installed in %ProgramFiles%\SimplePackageTest directory. + * Installer should install test app for all users (machine wide). + * Installer should not create any shortcuts. + */ + +/* + * @test + * @summary Simple jpackage command run + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile SimplePackageTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=SimplePackageTest + */ +public class SimplePackageTest { + + @Test + public static void test() { + new PackageTest() + .configureHelloApp() + .addBundleDesktopIntegrationVerifier(false) + .run(); + } +} --- /dev/null 2019-11-18 21:40:15.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/jdk/jpackage/tests/AppVersionTest.java 2019-11-18 21:40:12.218783600 -0500 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.tests; + +import java.util.Collection; +import java.util.List; +import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; + +/* + * @test + * @summary jpackage application version testing + * @library ../../../../helpers + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile AppVersionTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=jdk.jpackage.tests.AppVersionTest + */ + +public final class AppVersionTest { + + @Parameters + public static Collection input() { + return List.of(new Object[][]{ + // Default jpackage version + {"1.0", "Hello", null}, + {"1.0", "com.other/com.other.Hello", null}, + // Version should be picked from --app-version + {"3.1", "Hello", new String[]{"--app-version", "3.1"}}, + {"3.2", "com.other/com.other.Hello", new String[]{"--app-version", + "3.2"}}, + // Version should be picked from the last --app-version + {"3.3", "Hello", new String[]{"--app-version", "4", "--app-version", + "3.3"}}, + {"7.8", "com.other/com.other.Hello", new String[]{"--app-version", + "4", "--app-version", "7.8"}}, + // Pick version from jar + {"3.10.17", "com.other/com.other.Hello@3.10.17", null}, + // Ignore version in jar if --app-version given + {"7.5.81", "com.other/com.other.Hello@3.10.17", new String[]{ + "--app-version", "7.5.81"}} + }); + } + + public AppVersionTest(String expectedVersion, String javaAppDesc, + String[] jpackageArgs) { + this.expectedVersion = expectedVersion; + + cmd = JPackageCommand.helloAppImage(javaAppDesc); + if (jpackageArgs != null) { + cmd.addArguments(jpackageArgs); + } + } + + @Test + public void test() { + cmd.executeAndAssertHelloAppImageCreated(); + String actualVersion = cmd.readLaunherCfgFile().getValue("Application", + "app.version"); + TKit.assertEquals(expectedVersion, actualVersion, + "Check application version"); + } + + private final String expectedVersion; + private final JPackageCommand cmd; +} --- /dev/null 2019-11-18 21:40:26.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java 2019-11-18 21:40:23.331803600 -0500 @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.tests; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.ArrayList; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.*; +import jdk.jpackage.test.Functional.ThrowingConsumer; +import jdk.jpackage.test.Annotations.*; + +/* + * @test + * @summary jpackage basic testing + * @library ../../../../helpers + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile BasicTest.java + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=jdk.jpackage.tests.BasicTest + */ + +public final class BasicTest { + @Test + public void testNoArgs() { + List output = + getJPackageToolProvider().executeAndGetOutput(); + TKit.assertStringListEquals(List.of("Usage: jpackage ", + "Use jpackage --help (or -h) for a list of possible options"), + output, "Check jpackage output"); + } + + @Test + public void testVersion() { + List output = + getJPackageToolProvider() + .addArgument("--version") + .executeAndGetOutput(); + TKit.assertStringListEquals(List.of(System.getProperty("java.version")), + output, "Check jpackage output"); + } + + @Test + public void testHelp() { + List hOutput = getJPackageToolProvider() + .addArgument("-h").executeAndGetOutput(); + List helpOutput = getJPackageToolProvider() + .addArgument("--help").executeAndGetOutput(); + + TKit.assertStringListEquals(hOutput, helpOutput, + "Check -h and --help parameters produce the same output"); + + final String windowsPrefix = "--win-"; + final String linuxPrefix = "--linux-"; + final String osxPrefix = "--mac-"; + + final String expectedPrefix; + final List unexpectedPrefixes; + + if (TKit.isWindows()) { + expectedPrefix = windowsPrefix; + unexpectedPrefixes = List.of(osxPrefix, linuxPrefix); + } else if (TKit.isLinux()) { + expectedPrefix = linuxPrefix; + unexpectedPrefixes = List.of(windowsPrefix, osxPrefix); + } else if (TKit.isOSX()) { + expectedPrefix = osxPrefix; + unexpectedPrefixes = List.of(linuxPrefix, windowsPrefix); + } else { + throw TKit.throwUnknownPlatformError(); + } + + Function> createPattern = (prefix) -> { + return Pattern.compile("^ " + prefix).asPredicate(); + }; + + Function, Long> countStrings = (prefixes) -> { + return hOutput.stream().filter( + prefixes.stream().map(createPattern).reduce(x -> false, + Predicate::or)).peek(TKit::trace).count(); + }; + + TKit.trace("Check parameters in help text"); + TKit.assertNotEquals(0, countStrings.apply(List.of(expectedPrefix)), + "Check help text contains plaform specific parameters"); + TKit.assertEquals(0, countStrings.apply(unexpectedPrefixes), + "Check help text doesn't contain unexpected parameters"); + } + + @Test + @SuppressWarnings("unchecked") + public void testVerbose() { + JPackageCommand cmd = JPackageCommand.helloAppImage() + .setFakeRuntime().executePrerequisiteActions(); + + List expectedVerboseOutputStrings = new ArrayList<>(); + expectedVerboseOutputStrings.add("Creating app package:"); + if (TKit.isWindows()) { + expectedVerboseOutputStrings.add("Result application bundle:"); + expectedVerboseOutputStrings.add( + "Succeeded in building Windows Application Image package"); + } else if (TKit.isLinux()) { + expectedVerboseOutputStrings.add( + "Succeeded in building Linux Application Image package"); + } else if (TKit.isOSX()) { + expectedVerboseOutputStrings.add("Preparing Info.plist:"); + expectedVerboseOutputStrings.add( + "Succeeded in building Mac Application Image package"); + } else { + TKit.throwUnknownPlatformError(); + } + + TKit.deleteDirectoryContentsRecursive(cmd.outputDir()); + List nonVerboseOutput = cmd.createExecutor().executeAndGetOutput(); + List[] verboseOutput = (List[])new List[1]; + + // Directory clean up is not 100% reliable on Windows because of + // antivirus software that can lock .exe files. Setup + // diffreent output directory instead of cleaning the default one for + // verbose jpackage run. + TKit.withTempDirectory("verbose-output", tempDir -> { + cmd.setArgumentValue("--dest", tempDir); + verboseOutput[0] = cmd.createExecutor().addArgument( + "--verbose").executeAndGetOutput(); + }); + + TKit.assertTrue(nonVerboseOutput.size() < verboseOutput[0].size(), + "Check verbose output is longer than regular"); + + expectedVerboseOutputStrings.forEach(str -> { + TKit.assertTextStream(str).label("regular output") + .predicate(String::contains).negate() + .apply(nonVerboseOutput.stream()); + }); + + expectedVerboseOutputStrings.forEach(str -> { + TKit.assertTextStream(str).label("verbose output") + .apply(verboseOutput[0].stream()); + }); + } + + @Test + public void testNoName() { + final String mainClassName = "Greetings"; + + JPackageCommand cmd = JPackageCommand.helloAppImage(mainClassName) + .removeArgumentWithValue("--name"); + + Path expectedImageDir = cmd.outputDir().resolve(mainClassName); + if (TKit.isOSX()) { + expectedImageDir = expectedImageDir.getParent().resolve( + expectedImageDir.getFileName().toString() + ".app"); + } + + cmd.executeAndAssertHelloAppImageCreated(); + TKit.assertEquals(expectedImageDir.toAbsolutePath().normalize().toString(), + cmd.outputBundle().toAbsolutePath().normalize().toString(), + String.format( + "Check [%s] directory is filled with application image data", + expectedImageDir)); + } + + @Test + // Regular app + @Parameter("Hello") + // Modular app + @Parameter("com.other/com.other.Hello") + public void testApp(String javaAppDesc) { + JPackageCommand.helloAppImage(javaAppDesc) + .executeAndAssertHelloAppImageCreated(); + } + + @Test + public void testWhitespaceInPaths() { + JPackageCommand.helloAppImage("a/b c.jar:Hello") + .setArgumentValue("--input", TKit.workDir().resolve("The quick brown fox")) + .setArgumentValue("--dest", TKit.workDir().resolve("jumps over the lazy dog")) + .executeAndAssertHelloAppImageCreated(); + } + + @Test + @Parameter("ALL-MODULE-PATH") + @Parameter("ALL-DEFAULT") + @Parameter("java.desktop") + @Parameter("java.desktop,jdk.jartool") + @Parameter({ "java.desktop", "jdk.jartool" }) + public void testAddModules(String... addModulesArg) { + JPackageCommand cmd = JPackageCommand + .helloAppImage("goodbye.jar:com.other/com.other.Hello"); + Stream.of(addModulesArg).map(v -> Stream.of("--add-modules", v)).flatMap( + s -> s).forEachOrdered(cmd::addArgument); + cmd.executeAndAssertHelloAppImageCreated(); + } + + /** + * Test --temp option. Doesn't make much sense for app image as temporary + * directory is used only on Windows. Test it in packaging mode. + * @throws IOException + */ + @Test + public void testTemp() throws IOException { + TKit.withTempDirectory("temp-root", tempRoot -> { + Function getTempDir = cmd -> { + return tempRoot.resolve(cmd.outputBundle().getFileName()); + }; + + ThrowingConsumer addTempDir = cmd -> { + Path tempDir = getTempDir.apply(cmd); + Files.createDirectories(tempDir); + cmd.addArguments("--temp", tempDir); + }; + + new PackageTest().configureHelloApp().addInitializer(addTempDir) + .addBundleVerifier(cmd -> { + // Check jpackage actually used the supplied directory. + Path tempDir = getTempDir.apply(cmd); + TKit.assertNotEquals(0, tempDir.toFile().list().length, + String.format( + "Check jpackage wrote some data in the supplied temporary directory [%s]", + tempDir)); + }) + .run(); + + new PackageTest().configureHelloApp().addInitializer(addTempDir) + .addInitializer(cmd -> { + // Clean output from the previus jpackage run. + Files.delete(cmd.outputBundle()); + }) + // Temporary directory should not be empty, + // jpackage should exit with error. + .setExpectedExitCode(1) + .run(); + }); + } + + @Test + public void testAtFile() throws IOException { + JPackageCommand cmd = JPackageCommand.helloAppImage(); + + // Init options file with the list of options configured + // for JPackageCommand instance. + final Path optionsFile = TKit.workDir().resolve("options"); + Files.write(optionsFile, + List.of(String.join(" ", cmd.getAllArguments()))); + + // Build app jar file. + cmd.executePrerequisiteActions(); + + // Make sure output directory is empty. Normally JPackageCommand would + // do this automatically. + TKit.deleteDirectoryContentsRecursive(cmd.outputDir()); + + // Instead of running jpackage command through configured + // JPackageCommand instance, run vanilla jpackage command with @ file. + getJPackageToolProvider() + .addArgument(String.format("@%s", optionsFile)) + .execute().assertExitCodeIsZero(); + + // Verify output of jpackage command. + cmd.assertImageCreated(); + HelloApp.executeLauncherAndVerifyOutput(cmd); + } + + @Parameter("Hello") + @Parameter("com.foo/com.foo.main.Aloha") + @Test + public void testJLinkRuntime(String javaAppDesc) { + JPackageCommand cmd = JPackageCommand.helloAppImage(javaAppDesc); + + // If `--module` parameter was set on jpackage command line, get its + // value and extract module name. + // E.g.: foo.bar2/foo.bar.Buz -> foo.bar2 + // Note: HelloApp class manages `--module` parameter on jpackage command line + final String moduleName = cmd.getArgumentValue("--module", () -> null, + (v) -> v.split("/", 2)[0]); + + if (moduleName != null) { + // Build module jar. + cmd.executePrerequisiteActions(); + } + + TKit.withTempDirectory("runtime", tempDir -> { + final Path runtimeDir = tempDir.resolve("data"); + + // List of modules required for test app. + final var modules = new String[] { + "java.base", + "java.desktop" + }; + + Executor jlink = getToolProvider(JavaTool.JLINK) + .saveOutput(false) + .addArguments( + "--add-modules", String.join(",", modules), + "--output", runtimeDir.toString(), + "--strip-debug", + "--no-header-files", + "--no-man-pages"); + + if (moduleName != null) { + jlink.addArguments("--add-modules", moduleName, "--module-path", + Path.of(cmd.getArgumentValue("--module-path")).resolve( + "hello.jar").toString()); + } + + jlink.execute().assertExitCodeIsZero(); + + cmd.addArguments("--runtime-image", runtimeDir); + cmd.executeAndAssertHelloAppImageCreated(); + }); + } + + private static Executor getJPackageToolProvider() { + return getToolProvider(JavaTool.JPACKAGE); + } + + private static Executor getToolProvider(JavaTool tool) { + return new Executor().dumpOutput().saveOutput().setToolProvider(tool); + } +} --- /dev/null 2019-11-18 21:40:37.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java 2019-11-18 21:40:34.747750100 -0500 @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.tests; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.Collection; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.jar.JarFile; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.nio.file.Path; +import java.util.function.Predicate; +import java.util.jar.JarEntry; +import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.*; +import jdk.jpackage.test.Functional.ThrowingConsumer; +import static jdk.jpackage.tests.MainClassTest.Script.MainClassType.*; + + +/* + * @test + * @summary test different settings of main class name for jpackage + * @library ../../../../helpers + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile MainClassTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=jdk.jpackage.tests.MainClassTest + */ + +public final class MainClassTest { + + static final class Script { + Script() { + appDesc = JavaAppDesc.parse("test.Hello"); + } + + Script modular(boolean v) { + appDesc.setModuleName(v ? "com.other" : null); + return this; + } + + Script withJLink(boolean v) { + withJLink = v; + return this; + } + + Script withMainClass(MainClassType v) { + mainClass = v; + return this; + } + + Script withJarMainClass(MainClassType v) { + appDesc.setJarWithMainClass(v != NotSet); + jarMainClass = v; + return this; + } + + Script expectedErrorMessage(String v) { + expectedErrorMessage = v; + return this; + } + + @Override + public String toString() { + return Stream.of( + format("modular", appDesc.moduleName() != null ? 'y' : 'n'), + format("main-class", mainClass), + format("jar-main-class", jarMainClass), + format("jlink", withJLink ? 'y' : 'n'), + format("error", expectedErrorMessage) + ).filter(Objects::nonNull).collect(Collectors.joining("; ")); + } + + private static String format(String key, Object value) { + if (value == null) { + return null; + } + return String.join("=", key, value.toString()); + } + + enum MainClassType { + NotSet("n"), + SetWrong("b"), + SetRight("y"); + + MainClassType(String label) { + this.label = label; + } + + @Override + public String toString() { + return label; + } + + private final String label; + }; + + private JavaAppDesc appDesc; + private boolean withJLink; + private MainClassType mainClass; + private MainClassType jarMainClass; + private String expectedErrorMessage; + } + + public MainClassTest(Script script) { + this.script = script; + + nonExistingMainClass = Stream.of( + script.appDesc.packageName(), "ThereIsNoSuchClass").filter( + Objects::nonNull).collect(Collectors.joining(".")); + + cmd = JPackageCommand + .helloAppImage(script.appDesc) + .ignoreDefaultRuntime(true); + if (!script.withJLink) { + cmd.addArguments("--runtime-image", Path.of(System.getProperty( + "java.home"))); + } + + final String moduleName = script.appDesc.moduleName(); + switch (script.mainClass) { + case NotSet: + if (moduleName != null) { + // Don't specify class name, only module name. + cmd.setArgumentValue("--module", moduleName); + } else { + cmd.removeArgumentWithValue("--main-class"); + } + break; + + case SetWrong: + if (moduleName != null) { + cmd.setArgumentValue("--module", + String.join("/", moduleName, nonExistingMainClass)); + } else { + cmd.setArgumentValue("--main-class", nonExistingMainClass); + } + } + } + + @Parameters + public static Collection scripts() { + final var withMainClass = Set.of(SetWrong, SetRight); + + List scripts = new ArrayList<>(); + for (var withJLink : List.of(true, false)) { + for (var modular : List.of(true, false)) { + for (var mainClass : Script.MainClassType.values()) { + for (var jarMainClass : Script.MainClassType.values()) { + Script script = new Script() + .modular(modular) + .withJLink(withJLink) + .withMainClass(mainClass) + .withJarMainClass(jarMainClass); + + if (withMainClass.contains(jarMainClass) + || withMainClass.contains(mainClass)) { + } else if (modular) { + script.expectedErrorMessage( + "Error: Main application class is missing"); + } else { + script.expectedErrorMessage( + "A main class was not specified nor was one found in the jar"); + } + + scripts.add(new Script[]{script}); + } + } + } + } + return scripts; + } + + @Test + public void test() throws IOException { + if (script.jarMainClass == SetWrong) { + initJarWithWrongMainClass(); + } + + if (script.expectedErrorMessage != null) { + // This is the case when main class is not found nor in jar + // file nor on command line. + List output = cmd + .saveConsoleOutput(true) + .execute() + .assertExitCodeIs(1) + .getOutput(); + TKit.assertTextStream(script.expectedErrorMessage).apply(output.stream()); + return; + } + + // Get here only if main class is specified. + boolean appShouldSucceed = false; + + // Should succeed if valid main class is set on the command line. + appShouldSucceed |= (script.mainClass == SetRight); + + // Should succeed if main class is not set on the command line but set + // to valid value in the jar. + appShouldSucceed |= (script.mainClass == NotSet && script.jarMainClass == SetRight); + + if (appShouldSucceed) { + cmd.executeAndAssertHelloAppImageCreated(); + } else { + cmd.executeAndAssertImageCreated(); + if (!cmd.isFakeRuntime(String.format("Not running [%s]", + cmd.appLauncherPath()))) { + List output = new Executor() + .setDirectory(cmd.outputDir()) + .setExecutable(cmd.appLauncherPath()) + .dumpOutput().saveOutput() + .execute().assertExitCodeIs(1).getOutput(); + TKit.assertTextStream(String.format( + "Error: Could not find or load main class %s", + nonExistingMainClass)).apply(output.stream()); + } + } + } + + private void initJarWithWrongMainClass() throws IOException { + // Call JPackageCommand.executePrerequisiteActions() to build app's jar. + // executePrerequisiteActions() is called by JPackageCommand instance + // only once. + cmd.executePrerequisiteActions(); + + final Path jarFile; + if (script.appDesc.moduleName() != null) { + jarFile = Path.of(cmd.getArgumentValue("--module-path"), + script.appDesc.jarFileName()); + } else { + jarFile = cmd.inputDir().resolve(cmd.getArgumentValue("--main-jar")); + } + + // Create new jar file filtering out main class from the old jar file. + TKit.withTempDirectory("repack-jar", workDir -> { + // Extract app's class from the old jar. + explodeJar(jarFile, workDir, + jarEntry -> Path.of(jarEntry.getName()).equals( + script.appDesc.classFilePath())); + + // Create app's jar file with different main class. + var badAppDesc = JavaAppDesc.parse(script.appDesc.toString()).setClassName( + nonExistingMainClass); + JPackageCommand.helloAppImage(badAppDesc).executePrerequisiteActions(); + + // Extract new jar but skip app's class. + explodeJar(jarFile, workDir, + jarEntry -> !Path.of(jarEntry.getName()).equals( + badAppDesc.classFilePath())); + + // At this point we should have: + // 1. Manifest from the new jar referencing non-existing class + // as the main class. + // 2. Module descriptor referencing non-existing class as the main + // class in case of modular app. + // 3. App's class from the old jar. We need it to let jlink find some + // classes in the package declared in module descriptor + // in case of modular app. + + Files.delete(jarFile); + new Executor().setToolProvider(JavaTool.JAR) + .addArguments("-v", "-c", "-M", "-f", jarFile.toString()) + .addArguments("-C", workDir.toString(), ".") + .dumpOutput() + .execute().assertExitCodeIsZero(); + }); + } + + private static void explodeJar(Path jarFile, Path workDir, + Predicate filter) throws IOException { + try (var jar = new JarFile(jarFile.toFile())) { + jar.stream() + .filter(Predicate.not(JarEntry::isDirectory)) + .filter(filter) + .sequential().forEachOrdered(ThrowingConsumer.toConsumer( + jarEntry -> { + try (var in = jar.getInputStream(jarEntry)) { + Path fileName = workDir.resolve(jarEntry.getName()); + Files.createDirectories(fileName.getParent()); + Files.copy(in, fileName); + } + })); + } + } + + private final JPackageCommand cmd; + private final Script script; + private final String nonExistingMainClass; +} --- /dev/null 2019-11-18 21:40:49.000000000 -0500 +++ new/test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest.java 2019-11-18 21:40:45.732965100 -0500 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.tests; + +import java.io.File; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; + + +/* + * @test + * @summary jpackage with --module-path testing + * @library ../../../../helpers + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile ModulePathTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=jdk.jpackage.tests.ModulePathTest + */ + +public final class ModulePathTest { + + @Parameters + public static Collection data() { + return List.of(new String[][]{ + {GOOD_PATH, EMPTY_DIR, NON_EXISTING_DIR}, + {EMPTY_DIR, NON_EXISTING_DIR, GOOD_PATH}, + {GOOD_PATH + "/a/b/c/d", GOOD_PATH}, + {String.join(File.pathSeparator, EMPTY_DIR, NON_EXISTING_DIR, + GOOD_PATH)}, + {String.join(File.pathSeparator, EMPTY_DIR, NON_EXISTING_DIR), + String.join(File.pathSeparator, EMPTY_DIR, NON_EXISTING_DIR, + GOOD_PATH)}, + {}, + {EMPTY_DIR} + }); + } + + public ModulePathTest(String... modulePathArgs) { + this.modulePathArgs = List.of(modulePathArgs); + } + + @Test + public void test() { + final String moduleName = "com.foo"; + JPackageCommand cmd = JPackageCommand.helloAppImage( + "benvenuto.jar:" + moduleName + "/com.foo.Hello"); + // Build app jar file. + cmd.executePrerequisiteActions(); + + // Ignore runtime that can be set for all tests. Usually if default + // runtime is set, it is fake one to save time on running jlink and + // copying megabytes of data from Java home to application image. + // We need proper runtime for this test. + cmd.ignoreDefaultRuntime(true); + + // --module-path should be set in JPackageCommand.helloAppImage call + String goodModulePath = Objects.requireNonNull(cmd.getArgumentValue( + "--module-path")); + cmd.removeArgumentWithValue("--module-path"); + TKit.withTempDirectory("empty-dir", emptyDir -> { + Path nonExistingDir = TKit.withTempDirectory("non-existing-dir", + unused -> { + }); + + Function substitute = str -> { + String v = str; + v = v.replace(GOOD_PATH, goodModulePath); + v = v.replace(EMPTY_DIR, emptyDir.toString()); + v = v.replace(NON_EXISTING_DIR, nonExistingDir.toString()); + return v; + }; + + boolean withGoodPath = modulePathArgs.stream().anyMatch( + s -> s.contains(GOOD_PATH)); + + cmd.addArguments(modulePathArgs.stream().map(arg -> Stream.of( + "--module-path", substitute.apply(arg))).flatMap(s -> s).collect( + Collectors.toList())); + + if (withGoodPath) { + cmd.executeAndAssertHelloAppImageCreated(); + } else { + final String expectedErrorMessage; + if (modulePathArgs.isEmpty()) { + expectedErrorMessage = "Error: Missing argument: --runtime-image or --module-path"; + } else { + expectedErrorMessage = String.format( + "Error: Module %s not found", moduleName); + } + + List output = cmd + .saveConsoleOutput(true) + .execute() + .assertExitCodeIs(1) + .getOutput(); + TKit.assertTextStream(expectedErrorMessage).apply(output.stream()); + } + }); + } + + private final List modulePathArgs; + + private final static String GOOD_PATH = "@GoodPath@"; + private final static String EMPTY_DIR = "@EmptyDir@"; + private final static String NON_EXISTING_DIR = "@NonExistingDir@"; +} --- /dev/null 2019-11-18 21:41:00.000000000 -0500 +++ new/test/jdk/tools/jpackage/test_jpackage.sh 2019-11-18 21:40:56.707541100 -0500 @@ -0,0 +1,68 @@ +#!/bin/bash + +# +# Complete testing of jpackage platform-specific packaging. +# +# The script does the following: +# 1. Create packages. +# 2. Install created packages. +# 3. Verifies packages are installed. +# 4. Uninstall created packages. +# 5. Verifies packages are uninstalled. +# +# For the list of accepted command line arguments see `run_tests.sh` script. +# + +# Fail fast +set -e; set -o pipefail; + +# Script debug +dry_run=${JPACKAGE_TEST_DRY_RUN} + +# Default directory where jpackage should write bundle files +output_dir=~/jpackage_bundles + + +set_args () +{ + args=() + local arg_is_output_dir= + local arg_is_mode= + local output_dir_set= + for arg in "$@"; do + if [ "$arg" == "-o" ]; then + arg_is_output_dir=yes + output_dir_set=yes + elif [ "$arg" == "-m" ]; then + arg_is_mode=yes + continue + elif [ -n "$arg_is_output_dir" ]; then + arg_is_output_dir= + output_dir="$arg" + elif [ -n "$arg_is_mode" ]; then + arg_is_mode= + continue + fi + + args+=( "$arg" ) + done + [ -n "$output_dir_set" ] || args=( -o "$output_dir" "${args[@]}" ) +} + + +exec_command () +{ + if [ -n "$dry_run" ]; then + echo "$@" + else + eval "$@" + fi +} + +set_args "$@" +basedir="$(dirname $0)" +exec_command "$basedir/run_tests.sh" -m create "${args[@]}" +exec_command "$basedir/manage_packages.sh" -d "$output_dir" +exec_command "$basedir/run_tests.sh" -m verify-install "${args[@]}" +exec_command "$basedir/manage_packages.sh" -d "$output_dir" -u +exec_command "$basedir/run_tests.sh" -m verify-uninstall "${args[@]}" --- /dev/null 2019-11-18 21:41:11.000000000 -0500 +++ new/test/jdk/tools/jpackage/windows/WinConsoleTest.java 2019-11-18 21:41:08.098206200 -0500 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.IOException; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Annotations.Parameter; + +/* + * @test + * @summary jpackage with --win-console + * @library ../helpers + * @build jdk.jpackage.test.* + * @requires (os.family == "windows") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile WinConsoleTest.java + * + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useToolProviderByDefault + * --jpt-run=WinConsoleTest + * + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinConsoleTest + */ +public class WinConsoleTest { + + @Test + @Parameter("true") + @Parameter("false") + public static void test(boolean withWinConsole) throws IOException { + JPackageCommand cmd = JPackageCommand.helloAppImage(); + if (!withWinConsole) { + cmd.removeArgument("--win-console"); + } + cmd.executeAndAssertHelloAppImageCreated(); + checkSubsystem(cmd.appLauncherPath(), withWinConsole); + } + + private static void checkSubsystem(Path path, boolean isConsole) throws + IOException { + final int subsystemGui = 2; + final int subsystemConsole = 3; + final int bufferSize = 512; + + final int expectedSubsystem = isConsole ? subsystemConsole : subsystemGui; + + try (InputStream inputStream = new FileInputStream(path.toString())) { + byte[] bytes = new byte[bufferSize]; + TKit.assertEquals(bufferSize, inputStream.read(bytes), + String.format("Check %d bytes were read from %s file", + bufferSize, path)); + + // Check PE header for console or Win GUI app. + // https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_image_nt_headers + for (int i = 0; i < (bytes.length - 4); i++) { + if (bytes[i] == 0x50 && bytes[i + 1] == 0x45 + && bytes[i + 2] == 0x0 && bytes[i + 3] == 0x0) { + + // Signature, File Header and subsystem offset. + i = i + 4 + 20 + 68; + byte subsystem = bytes[i]; + TKit.assertEquals(expectedSubsystem, subsystem, + String.format("Check subsystem of PE [%s] file", + path)); + return; + } + } + } + + TKit.assertUnexpected(String.format( + "Subsystem not found in PE header of [%s] file", path)); + } +} --- /dev/null 2019-11-18 21:41:23.000000000 -0500 +++ new/test/jdk/tools/jpackage/windows/WinDirChooserTest.java 2019-11-18 21:41:19.573931300 -0500 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; + +/** + * Test --win-dir-chooser parameter. Output of the test should be + * WinDirChooserTest-1.0.exe installer. The output installer should provide the + * same functionality as the default installer (see description of the default + * installer in SimplePackageTest.java) plus provide an option for user to + * change the default installation directory. + */ + +/* + * @test + * @summary jpackage with --win-dir-chooser + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "windows") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m WinDirChooserTest + */ + +public class WinDirChooserTest { + public static void main(String[] args) { + TKit.run(args, () -> { + new PackageTest() + .forTypes(PackageType.WINDOWS) + .configureHelloApp() + .addInitializer(cmd -> cmd.addArgument("--win-dir-chooser")).run(); + }); + } +} --- /dev/null 2019-11-18 21:41:34.000000000 -0500 +++ new/test/jdk/tools/jpackage/windows/WinMenuGroupTest.java 2019-11-18 21:41:31.320019100 -0500 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Annotations.Test; + +/** + * Test --win-menu and --win-menu-group parameters. + * Output of the test should be WinMenuGroupTest-1.0.exe installer. + * The output installer should provide the + * same functionality as the default installer (see description of the default + * installer in SimplePackageTest.java) plus + * it should create a shortcut for application launcher in Windows Menu in + * "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\WinMenuGroupTest_MenuGroup" folder. + */ + +/* + * @test + * @summary jpackage with --win-menu and --win-menu-group + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "windows") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile WinMenuGroupTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinMenuGroupTest + */ + +public class WinMenuGroupTest { + @Test + public static void test() { + new PackageTest() + .forTypes(PackageType.WINDOWS) + .configureHelloApp() + .addInitializer(cmd -> cmd.addArguments( + "--win-menu", "--win-menu-group", "WinMenuGroupTest_MenuGroup")) + .run(); + } +} --- /dev/null 2019-11-18 21:41:46.000000000 -0500 +++ new/test/jdk/tools/jpackage/windows/WinMenuTest.java 2019-11-18 21:41:43.049639800 -0500 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Annotations.Test; + +/** + * Test --win-menu parameter. Output of the test should be WinMenuTest-1.0.exe + * installer. The output installer should provide the same functionality as the + * default installer (see description of the default installer in + * SimplePackageTest.java), except it should create a menu shortcut. + */ + +/* + * @test + * @summary jpackage with --win-menu + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "windows") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile WinMenuTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinMenuTest + */ + +public class WinMenuTest { + @Test + public static void test() { + new PackageTest() + .forTypes(PackageType.WINDOWS) + .configureHelloApp() + .addInitializer(cmd -> cmd.addArgument("--win-menu")).run(); + } +} --- /dev/null 2019-11-18 21:41:57.000000000 -0500 +++ new/test/jdk/tools/jpackage/windows/WinPerUserInstallTest.java 2019-11-18 21:41:54.004170500 -0500 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Annotations.Test; + +/** + * Test --win-per-user-install, --win-menu, --win-menu-group parameters. + * Output of the test should be WinPerUserInstallTest-1.0.exe installer. The + * output installer should provide the same functionality as the default + * installer (see description of the default installer in + * SimplePackageTest.java) plus it should create application menu in Windows + * Menu and installation should be per user and not machine wide. + */ + +/* + * @test + * @summary jpackage with --win-per-user-install, --win-menu, --win-menu-group + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "windows") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile WinPerUserInstallTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinPerUserInstallTest + */ + +public class WinPerUserInstallTest { + @Test + public static void test() { + new PackageTest() + .forTypes(PackageType.WINDOWS) + .configureHelloApp() + .addInitializer(cmd -> cmd.addArguments( + "--win-menu", + "--win-menu-group", "WinPerUserInstallTest_MenuGroup", + "--win-per-user-install")) + .run(); + } +} --- /dev/null 2019-11-18 21:42:08.000000000 -0500 +++ new/test/jdk/tools/jpackage/windows/WinResourceTest.java 2019-11-18 21:42:05.092726000 -0500 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.nio.file.Path; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Annotations.Parameters; +import java.util.List; + +/** + * Test --resource-dir option. The test should set --resource-dir to point to + * a dir with an empty "main.wxs" file. As a result, jpackage should try to + * use the customized resource and fail. + */ + +/* + * @test + * @summary jpackage with --resource-dir + * @library ../helpers + * @build jdk.jpackage.test.* + * @requires (os.family == "windows") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile WinResourceTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinResourceTest + */ + +public class WinResourceTest { + + public WinResourceTest(String wixSource, String expectedLogMessage) { + this.wixSource = wixSource; + this.expectedLogMessage = expectedLogMessage; + } + + @Parameters + public static List data() { + return List.of(new Object[][]{ + {"main.wxs", "Using custom package resource [Main WiX project file]"}, + {"overrides.wxi", "Using custom package resource [Overrides WiX project file]"}, + }); + } + + @Test + public void test() throws IOException { + new PackageTest() + .forTypes(PackageType.WINDOWS) + .configureHelloApp() + .addInitializer(cmd -> { + Path resourceDir = TKit.createTempDirectory("resources"); + + // 1. Set fake run time to save time by skipping jlink step of jpackage. + // 2. Instruct test to save jpackage output. + cmd.setFakeRuntime().saveConsoleOutput(true); + + cmd.addArguments("--resource-dir", resourceDir); + // Create invalid WiX source file in a resource dir. + TKit.createTextFile(resourceDir.resolve(wixSource), List.of( + "any string that is an invalid WiX source file")); + }) + .addBundleVerifier((cmd, result) -> { + // Assert jpackage picked custom main.wxs and failed as expected by + // examining its output + TKit.assertTextStream(expectedLogMessage) + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream("error CNDL0104 : Not a valid source file") + .apply(result.getOutput().stream()); + }) + .setExpectedExitCode(1) + .run(); + } + + final String wixSource; + final String expectedLogMessage; +} --- /dev/null 2019-11-18 21:42:19.000000000 -0500 +++ new/test/jdk/tools/jpackage/windows/WinScriptTest.java 2019-11-18 21:42:16.576279600 -0500 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.ArrayList; +import jdk.incubator.jpackage.internal.IOUtils; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.JPackageCommand; + +/* + * @test usage of scripts from resource dir + * @summary jpackage with + * @library ../helpers + * @build jdk.jpackage.test.* + * @requires (os.family == "windows") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile WinScriptTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinScriptTest + */ + +public class WinScriptTest { + + public WinScriptTest(PackageType type) { + this.packageType = type; + + test = new PackageTest() + .forTypes(type) + .configureHelloApp() + .addInitializer(cmd -> { + cmd.setFakeRuntime().saveConsoleOutput(true); + }); + } + + @Parameters + public static List data() { + return List.of(new Object[][]{ + {PackageType.WIN_MSI}, + {PackageType.WIN_EXE} + }); + } + + @Test + @Parameter("0") + @Parameter("10") + public void test(int wsfExitCode) { + final ScriptData appImageScriptData; + if (wsfExitCode != 0 && packageType == PackageType.WIN_EXE) { + appImageScriptData = new ScriptData(PackageType.WIN_MSI, 0); + } else { + appImageScriptData = new ScriptData(PackageType.WIN_MSI, wsfExitCode); + } + + final ScriptData msiScriptData = new ScriptData(PackageType.WIN_EXE, wsfExitCode); + + test.setExpectedExitCode(wsfExitCode == 0 ? 0 : 1); + TKit.withTempDirectory("resources", tempDir -> { + test.addInitializer(cmd -> { + cmd.addArguments("--resource-dir", tempDir); + + appImageScriptData.createScript(cmd); + msiScriptData.createScript(cmd); + }); + + if (packageType == PackageType.WIN_MSI) { + test.addBundleVerifier((cmd, result) -> { + appImageScriptData.assertJPackageOutput(result.getOutput()); + }); + } + + if (packageType == PackageType.WIN_EXE) { + test.addBundleVerifier((cmd, result) -> { + appImageScriptData.assertJPackageOutput(result.getOutput()); + msiScriptData.assertJPackageOutput(result.getOutput()); + }); + } + + test.run(); + }); + } + + private static class ScriptData { + ScriptData(PackageType scriptType, int wsfExitCode) { + if (scriptType == PackageType.WIN_MSI) { + echoText = "post app image wsf"; + envVarName = "JpAppImageDir"; + scriptSuffixName = "post-image"; + } else { + echoText = "post msi wsf"; + envVarName = "JpMsiFile"; + scriptSuffixName = "post-msi"; + } + this.wsfExitCode = wsfExitCode; + } + + void assertJPackageOutput(List output) { + TKit.assertTextStream(String.format("jp: %s", echoText)) + .predicate(String::equals) + .apply(output.stream()); + + String cwdPattern = String.format("jp: CWD(%s)=", envVarName); + TKit.assertTextStream(cwdPattern) + .predicate(String::startsWith) + .apply(output.stream()); + String cwd = output.stream().filter(line -> line.startsWith( + cwdPattern)).findFirst().get().substring(cwdPattern.length()); + + String envVarPattern = String.format("jp: %s=", envVarName); + TKit.assertTextStream(envVarPattern) + .predicate(String::startsWith) + .apply(output.stream()); + String envVar = output.stream().filter(line -> line.startsWith( + envVarPattern)).findFirst().get().substring(envVarPattern.length()); + + TKit.assertTrue(envVar.startsWith(cwd), String.format( + "Check value of %s environment variable [%s] starts with the current directory [%s] set for %s script", + envVarName, envVar, cwd, echoText)); + } + + void createScript(JPackageCommand cmd) throws IOException { + IOUtils.createXml(Path.of(cmd.getArgumentValue("--resource-dir"), + String.format("%s-%s.wsf", cmd.name(), scriptSuffixName)), xml -> { + xml.writeStartElement("job"); + xml.writeAttribute("id", "main"); + xml.writeStartElement("script"); + xml.writeAttribute("language", "JScript"); + xml.writeCData(String.join("\n", List.of( + "var shell = new ActiveXObject('WScript.Shell')", + "WScript.Echo('jp: " + envVarName + "=' + shell.ExpandEnvironmentStrings('%" + envVarName + "%'))", + "WScript.Echo('jp: CWD(" + envVarName + ")=' + shell.CurrentDirectory)", + String.format("WScript.Echo('jp: %s')", echoText), + String.format("WScript.Quit(%d)", wsfExitCode) + ))); + xml.writeEndElement(); + xml.writeEndElement(); + }); + } + + private final int wsfExitCode; + private final String scriptSuffixName; + private final String echoText; + private final String envVarName; + } + + private final PackageType packageType; + private PackageTest test; +} --- /dev/null 2019-11-18 21:42:31.000000000 -0500 +++ new/test/jdk/tools/jpackage/windows/WinShortcutTest.java 2019-11-18 21:42:27.890095400 -0500 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Annotations.Test; + +/** + * Test --win-shortcut parameter. Output of the test should be + * WinShortcutTest-1.0.exe installer. The output installer should provide the + * same functionality as the default installer (see description of the default + * installer in SimplePackageTest.java) plus install application shortcut on the + * desktop. + */ + +/* + * @test + * @summary jpackage with --win-shortcut + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "windows") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile WinShortcutTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinShortcutTest + */ + +public class WinShortcutTest { + @Test + public static void test() { + new PackageTest() + .forTypes(PackageType.WINDOWS) + .configureHelloApp() + .addInitializer(cmd -> cmd.addArgument("--win-shortcut")) + .run(); + } +} --- /dev/null 2019-11-18 21:42:42.000000000 -0500 +++ new/test/jdk/tools/jpackage/windows/WinUpgradeUUIDTest.java 2019-11-18 21:42:39.118895300 -0500 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; + +/** + * Test both --win-upgrade-uuid and --app-version parameters. Output of the test + * should be WinUpgradeUUIDTest-1.0.exe and WinUpgradeUUIDTest-2.0.exe + * installers. Both output installers should provide the same functionality as + * the default installer (see description of the default installer in + * SimplePackageTest.java) but have the same product code and different + * versions. Running WinUpgradeUUIDTest-2.0.exe installer should automatically + * uninstall older version of the test application previously installed with + * WinUpgradeUUIDTest-1.0.exe installer. + */ + +/* + * @test + * @summary jpackage with --win-upgrade-uuid and --app-version + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "windows") + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m WinUpgradeUUIDTest + */ + +public class WinUpgradeUUIDTest { + public static void main(String[] args) { + TKit.run(args, () -> { + PackageTest test = init(); + if (test.getAction() != PackageTest.Action.VERIFY_INSTALL) { + test.run(); + } + + test = init(); + test.addInitializer(cmd -> { + cmd.setArgumentValue("--app-version", "2.0"); + cmd.setArgumentValue("--arguments", "bar"); + }); + test.run(); + }); + } + + private static PackageTest init() { + return new PackageTest() + .forTypes(PackageType.WINDOWS) + .configureHelloApp() + .addInitializer(cmd -> cmd.addArguments("--win-upgrade-uuid", + "F0B18E75-52AD-41A2-BC86-6BE4FCD50BEB")); + } +} --- old/make/launcher/Launcher-jdk.jpackage.gmk 2019-11-18 21:42:51.996680400 -0500 +++ /dev/null 2019-11-18 21:42:53.000000000 -0500 @@ -1,78 +0,0 @@ -# -# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -include LauncherCommon.gmk - - -################################################################################ - -$(eval $(call SetupBuildLauncher, jpackage, \ - MAIN_CLASS := jdk.jpackage.main.Main, \ -)) - -################################################################################ - -JPACKAGE_APPLAUNCHEREXE_SRC := \ - $(TOPDIR)/src/jdk.jpackage/$(OPENJDK_TARGET_OS)/native/jpackageapplauncher - -# Output app launcher executable in resources dir, and symbols in the object dir -$(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_APPLAUNCHEREXE, \ - NAME := jpackageapplauncher, \ - OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \ - SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jpackageapplauncher, \ - SRC := $(JPACKAGE_APPLAUNCHEREXE_SRC), \ - TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ - OPTIMIZATION := LOW, \ - CFLAGS := $(CXXFLAGS_JDKEXE), \ - CFLAGS_windows := -EHsc -DLAUNCHERC -DUNICODE -D_UNICODE, \ - LDFLAGS := $(LDFLAGS_JDKEXE), \ - LIBS_macosx := -framework Cocoa, \ - LIBS := $(LIBCXX), \ - LIBS_linux := -ldl, \ - LIBS_windows := user32.lib shell32.lib advapi32.lib, \ -)) - -TARGETS += $(BUILD_JPACKAGE_APPLAUNCHEREXE) - -# Build non-console version of launcher -ifeq ($(OPENJDK_TARGET_OS), windows) - - $(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_APPLAUNCHERWEXE, \ - NAME := jpackageapplauncherw, \ - OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \ - SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jpackageapplauncherw, \ - SRC := $(JPACKAGE_APPLAUNCHEREXE_SRC), \ - TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ - OPTIMIZATION := LOW, \ - CFLAGS := $(CXXFLAGS_JDKEXE), \ - CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \ - LDFLAGS := $(LDFLAGS_JDKEXE), \ - LIBS := $(LIBCXX), \ - LIBS_windows := user32.lib shell32.lib advapi32.lib, \ - )) - - TARGETS += $(BUILD_JPACKAGE_APPLAUNCHERWEXE) -endif - --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java 2019-11-18 21:43:01.146272200 -0500 +++ /dev/null 2019-11-18 21:43:02.000000000 -0500 @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.UncheckedIOException; -import java.io.Writer; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.PosixFilePermission; -import java.text.MessageFormat; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.ResourceBundle; -import java.util.Set; - -import static jdk.jpackage.internal.StandardBundlerParam.*; - -public class LinuxAppImageBuilder extends AbstractAppImageBuilder { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.LinuxResources"); - - private static final String LIBRARY_NAME = "libapplauncher.so"; - - private final Path root; - private final Path appDir; - private final Path appModsDir; - private final Path runtimeDir; - private final Path resourcesDir; - private final Path mdir; - - private final Map params; - - public static final BundlerParamInfo ICON_PNG = - new StandardBundlerParam<>( - "icon.png", - File.class, - params -> { - File f = ICON.fetchFrom(params); - if (f != null && !f.getName().toLowerCase().endsWith(".png")) { - Log.error(MessageFormat.format(I18N.getString( - "message.icon-not-png"), f)); - return null; - } - return f; - }, - (s, p) -> new File(s)); - - public LinuxAppImageBuilder(Map config, Path imageOutDir) - throws IOException { - super(config, - imageOutDir.resolve(APP_NAME.fetchFrom(config) + "/runtime")); - - Objects.requireNonNull(imageOutDir); - - this.root = imageOutDir.resolve(APP_NAME.fetchFrom(config)); - this.appDir = root.resolve("app"); - this.appModsDir = appDir.resolve("mods"); - this.runtimeDir = root.resolve("runtime"); - this.resourcesDir = root.resolve("resources"); - this.mdir = runtimeDir.resolve("lib"); - this.params = new HashMap<>(); - config.entrySet().stream().forEach(e -> params.put( - e.getKey().toString(), e.getValue())); - Files.createDirectories(appDir); - Files.createDirectories(runtimeDir); - Files.createDirectories(resourcesDir); - } - - public LinuxAppImageBuilder(String appName, Path imageOutDir) - throws IOException { - super(null, imageOutDir.resolve(appName)); - - Objects.requireNonNull(imageOutDir); - - this.root = imageOutDir.resolve(appName); - this.appDir = null; - this.appModsDir = null; - this.runtimeDir = null; - this.resourcesDir = null; - this.mdir = null; - this.params = new HashMap<>(); - } - - private Path destFile(String dir, String filename) { - return runtimeDir.resolve(dir).resolve(filename); - } - - private void writeEntry(InputStream in, Path dstFile) throws IOException { - Files.createDirectories(dstFile.getParent()); - Files.copy(in, dstFile); - } - - private void writeSymEntry(Path dstFile, Path target) throws IOException { - Files.createDirectories(dstFile.getParent()); - Files.createLink(dstFile, target); - } - - /** - * chmod ugo+x file - */ - private void setExecutable(Path file) { - try { - Set perms = - Files.getPosixFilePermissions(file); - perms.add(PosixFilePermission.OWNER_EXECUTE); - perms.add(PosixFilePermission.GROUP_EXECUTE); - perms.add(PosixFilePermission.OTHERS_EXECUTE); - Files.setPosixFilePermissions(file, perms); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - } - - private static void createUtf8File(File file, String content) - throws IOException { - try (OutputStream fout = new FileOutputStream(file); - Writer output = new OutputStreamWriter(fout, "UTF-8")) { - output.write(content); - } - } - - - // it is static for the sake of sharing with "installer" bundlers - // that may skip calls to validate/bundle in this class! - public static File getRootDir(File outDir, Map p) { - return new File(outDir, APP_NAME.fetchFrom(p)); - } - - public static String getLauncherName(Map p) { - return APP_NAME.fetchFrom(p); - } - - public static String getLauncherCfgName(Map p) { - return "app/" + APP_NAME.fetchFrom(p) + ".cfg"; - } - - @Override - public Path getAppDir() { - return appDir; - } - - @Override - public Path getAppModsDir() { - return appModsDir; - } - - @Override - public void prepareApplicationFiles() throws IOException { - Map originalParams = new HashMap<>(params); - - // create the primary launcher - createLauncherForEntryPoint(params); - - // Copy library to the launcher folder - try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { - writeEntry(is_lib, root.resolve(LIBRARY_NAME)); - } - - // create the additional launchers, if any - List> entryPoints - = StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); - for (Map entryPoint : entryPoints) { - createLauncherForEntryPoint( - AddLauncherArguments.merge(originalParams, entryPoint)); - } - - // Copy class path entries to Java folder - copyApplication(); - - // Copy icon to Resources folder - copyIcon(); - } - - @Override - public void prepareJreFiles() throws IOException {} - - private void createLauncherForEntryPoint(Map p) - throws IOException { - // Copy executable to Linux folder - Path executableFile = root.resolve(getLauncherName(p)); - try (InputStream is_launcher = - getResourceAsStream("jpackageapplauncher")) { - writeEntry(is_launcher, executableFile); - } - - executableFile.toFile().setExecutable(true, false); - executableFile.toFile().setWritable(true, true); - - writeCfgFile(p, root.resolve(getLauncherCfgName(p)).toFile(), - "$APPDIR/runtime"); - } - - private void copyIcon() throws IOException { - File icon = ICON_PNG.fetchFrom(params); - if (icon != null) { - File iconTarget = new File(resourcesDir.toFile(), - APP_NAME.fetchFrom(params) + ".png"); - IOUtils.copyFile(icon, iconTarget); - } - } - - private void copyApplication() throws IOException { - for (RelativeFileSet appResources : - APP_RESOURCES_LIST.fetchFrom(params)) { - if (appResources == null) { - throw new RuntimeException("Null app resources?"); - } - File srcdir = appResources.getBaseDirectory(); - for (String fname : appResources.getIncludedFiles()) { - copyEntry(appDir, srcdir, fname); - } - } - } - -} --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java 2019-11-18 21:43:10.292089600 -0500 +++ /dev/null 2019-11-18 21:43:11.000000000 -0500 @@ -1,880 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.*; -import java.nio.file.Files; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.PosixFilePermissions; -import java.text.MessageFormat; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; - -import static jdk.jpackage.internal.StandardBundlerParam.*; -import static jdk.jpackage.internal.LinuxAppBundler.ICON_PNG; -import static jdk.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR; -import static jdk.jpackage.internal.LinuxAppBundler.LINUX_PACKAGE_DEPENDENCIES; - -public class LinuxDebBundler extends AbstractBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.LinuxResources"); - - public static final BundlerParamInfo APP_BUNDLER = - new StandardBundlerParam<>( - "linux.app.bundler", - LinuxAppBundler.class, - params -> new LinuxAppBundler(), - (s, p) -> null); - - // Debian rules for package naming are used here - // https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Source - // - // Package names must consist only of lower case letters (a-z), - // digits (0-9), plus (+) and minus (-) signs, and periods (.). - // They must be at least two characters long and - // must start with an alphanumeric character. - // - private static final Pattern DEB_BUNDLE_NAME_PATTERN = - Pattern.compile("^[a-z][a-z\\d\\+\\-\\.]+"); - - public static final BundlerParamInfo BUNDLE_NAME = - new StandardBundlerParam<> ( - Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId(), - String.class, - params -> { - String nm = APP_NAME.fetchFrom(params); - - if (nm == null) return null; - - // make sure to lower case and spaces/underscores become dashes - nm = nm.toLowerCase().replaceAll("[ _]", "-"); - return nm; - }, - (s, p) -> { - if (!DEB_BUNDLE_NAME_PATTERN.matcher(s).matches()) { - throw new IllegalArgumentException(new ConfigException( - MessageFormat.format(I18N.getString( - "error.invalid-value-for-package-name"), s), - I18N.getString( - "error.invalid-value-for-package-name.advice"))); - } - - return s; - }); - - public static final BundlerParamInfo FULL_PACKAGE_NAME = - new StandardBundlerParam<> ( - "linux.deb.fullPackageName", - String.class, - params -> BUNDLE_NAME.fetchFrom(params) + "-" - + VERSION.fetchFrom(params), - (s, p) -> s); - - public static final BundlerParamInfo DEB_IMAGE_DIR = - new StandardBundlerParam<>( - "linux.deb.imageDir", - File.class, - params -> { - File imagesRoot = IMAGES_ROOT.fetchFrom(params); - if (!imagesRoot.exists()) imagesRoot.mkdirs(); - return new File(new File(imagesRoot, "linux-deb.image"), - FULL_PACKAGE_NAME.fetchFrom(params)); - }, - (s, p) -> new File(s)); - - public static final BundlerParamInfo APP_IMAGE_ROOT = - new StandardBundlerParam<>( - "linux.deb.imageRoot", - File.class, - params -> { - File imageDir = DEB_IMAGE_DIR.fetchFrom(params); - return new File(imageDir, LINUX_INSTALL_DIR.fetchFrom(params)); - }, - (s, p) -> new File(s)); - - public static final BundlerParamInfo CONFIG_DIR = - new StandardBundlerParam<>( - "linux.deb.configDir", - File.class, - params -> new File(DEB_IMAGE_DIR.fetchFrom(params), "DEBIAN"), - (s, p) -> new File(s)); - - public static final BundlerParamInfo EMAIL = - new StandardBundlerParam<> ( - Arguments.CLIOptions.LINUX_DEB_MAINTAINER.getId(), - String.class, - params -> "Unknown", - (s, p) -> s); - - public static final BundlerParamInfo MAINTAINER = - new StandardBundlerParam<> ( - BundleParams.PARAM_MAINTAINER, - String.class, - params -> VENDOR.fetchFrom(params) + " <" - + EMAIL.fetchFrom(params) + ">", - (s, p) -> s); - - public static final BundlerParamInfo LICENSE_TEXT = - new StandardBundlerParam<> ( - "linux.deb.licenseText", - String.class, - params -> { - try { - String licenseFile = LICENSE_FILE.fetchFrom(params); - if (licenseFile != null) { - return Files.readString(new File(licenseFile).toPath()); - } - } catch (Exception e) { - Log.verbose(e); - } - return "Unknown"; - }, - (s, p) -> s); - - public static final BundlerParamInfo XDG_FILE_PREFIX = - new StandardBundlerParam<> ( - "linux.xdg-prefix", - String.class, - params -> { - try { - String vendor; - if (params.containsKey(VENDOR.getID())) { - vendor = VENDOR.fetchFrom(params); - } else { - vendor = "jpackage"; - } - String appName = APP_NAME.fetchFrom(params); - - return (appName + "-" + vendor).replaceAll("\\s", ""); - } catch (Exception e) { - Log.verbose(e); - } - return "unknown-MimeInfo.xml"; - }, - (s, p) -> s); - - public static final BundlerParamInfo MENU_GROUP = - new StandardBundlerParam<>( - Arguments.CLIOptions.LINUX_MENU_GROUP.getId(), - String.class, - params -> I18N.getString("param.menu-group.default"), - (s, p) -> s - ); - - private final static String DEFAULT_ICON = "javalogo_white_32.png"; - private final static String DEFAULT_CONTROL_TEMPLATE = "template.control"; - private final static String DEFAULT_PRERM_TEMPLATE = "template.prerm"; - private final static String DEFAULT_PREINSTALL_TEMPLATE = - "template.preinst"; - private final static String DEFAULT_POSTRM_TEMPLATE = "template.postrm"; - private final static String DEFAULT_POSTINSTALL_TEMPLATE = - "template.postinst"; - private final static String DEFAULT_COPYRIGHT_TEMPLATE = - "template.copyright"; - private final static String DEFAULT_DESKTOP_FILE_TEMPLATE = - "template.desktop"; - - public final static String TOOL_DPKG = "dpkg-deb"; - - public static boolean testTool(String toolName, String minVersion) { - try { - ProcessBuilder pb = new ProcessBuilder( - toolName, - "--version"); - // not interested in the output - IOUtils.exec(pb, Log.isDebug(), true); - } catch (Exception e) { - Log.verbose(MessageFormat.format(I18N.getString( - "message.test-for-tool"), toolName, e.getMessage())); - return false; - } - return true; - } - - @Override - public boolean validate(Map p) - throws UnsupportedPlatformException, ConfigException { - try { - if (p == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - - //run basic validation to ensure requirements are met - //we are not interested in return code, only possible exception - APP_BUNDLER.fetchFrom(p).validate(p); - - // NOTE: Can we validate that the required tools are available - // before we start? - if (!testTool(TOOL_DPKG, "1")){ - throw new ConfigException(MessageFormat.format( - I18N.getString("error.tool-not-found"), TOOL_DPKG), - I18N.getString("error.tool-not-found.advice")); - } - - - // Show warning is license file is missing - String licenseFile = LICENSE_FILE.fetchFrom(p); - if (licenseFile == null) { - Log.verbose(I18N.getString("message.debs-like-licenses")); - } - - // only one mime type per association, at least one file extention - List> associations = - FILE_ASSOCIATIONS.fetchFrom(p); - if (associations != null) { - for (int i = 0; i < associations.size(); i++) { - Map assoc = associations.get(i); - List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); - if (mimes == null || mimes.isEmpty()) { - String msgKey = - "error.no-content-types-for-file-association"; - throw new ConfigException( - MessageFormat.format(I18N.getString(msgKey), i), - I18N.getString(msgKey + ".advise")); - - } else if (mimes.size() > 1) { - String msgKey = - "error.too-many-content-types-for-file-association"; - throw new ConfigException( - MessageFormat.format(I18N.getString(msgKey), i), - I18N.getString(msgKey + ".advise")); - } - } - } - - // bundle name has some restrictions - // the string converter will throw an exception if invalid - BUNDLE_NAME.getStringConverter().apply(BUNDLE_NAME.fetchFrom(p), p); - - return true; - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - private boolean prepareProto(Map p) - throws PackagerException, IOException { - File appImage = StandardBundlerParam.getPredefinedAppImage(p); - File appDir = null; - - // we either have an application image or need to build one - if (appImage != null) { - appDir = new File(APP_IMAGE_ROOT.fetchFrom(p), - APP_NAME.fetchFrom(p)); - // copy everything from appImage dir into appDir/name - IOUtils.copyRecursive(appImage.toPath(), appDir.toPath()); - } else { - appDir = APP_BUNDLER.fetchFrom(p).doBundle(p, - APP_IMAGE_ROOT.fetchFrom(p), true); - } - return appDir != null; - } - - //@Override - public File bundle(Map p, - File outdir) throws PackagerException { - if (!outdir.isDirectory() && !outdir.mkdirs()) { - throw new PackagerException ("error.cannot-create-output-dir", - outdir.getAbsolutePath()); - } - if (!outdir.canWrite()) { - throw new PackagerException("error.cannot-write-to-output-dir", - outdir.getAbsolutePath()); - } - - // we want to create following structure - // - // DEBIAN - // control (file with main package details) - // menu (request to create menu) - // ... other control files if needed .... - // opt (by default) - // AppFolder (this is where app image goes) - // launcher executable - // app - // runtime - - File imageDir = DEB_IMAGE_DIR.fetchFrom(p); - File configDir = CONFIG_DIR.fetchFrom(p); - - try { - - imageDir.mkdirs(); - configDir.mkdirs(); - if (prepareProto(p) && prepareProjectConfig(p)) { - return buildDeb(p, outdir); - } - return null; - } catch (IOException ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - /* - * set permissions with a string like "rwxr-xr-x" - * - * This cannot be directly backport to 22u which is built with 1.6 - */ - private void setPermissions(File file, String permissions) { - Set filePermissions = - PosixFilePermissions.fromString(permissions); - try { - if (file.exists()) { - Files.setPosixFilePermissions(file.toPath(), filePermissions); - } - } catch (IOException ex) { - Logger.getLogger(LinuxDebBundler.class.getName()).log( - Level.SEVERE, null, ex); - } - - } - - private String getArch() { - String arch = System.getProperty("os.arch"); - if ("i386".equals(arch)) - return "i386"; - else - return "amd64"; - } - - private long getInstalledSizeKB(Map params) { - return getInstalledSizeKB(APP_IMAGE_ROOT.fetchFrom(params)) >> 10; - } - - private long getInstalledSizeKB(File dir) { - long count = 0; - File[] children = dir.listFiles(); - if (children != null) { - for (File file : children) { - if (file.isFile()) { - count += file.length(); - } - else if (file.isDirectory()) { - count += getInstalledSizeKB(file); - } - } - } - return count; - } - - private boolean prepareProjectConfig(Map params) - throws IOException { - Map data = createReplacementData(params); - File rootDir = LinuxAppBundler.getRootDir(APP_IMAGE_ROOT.fetchFrom( - params), params); - - File iconTarget = getConfig_IconFile(rootDir, params); - File icon = ICON_PNG.fetchFrom(params); - if (!StandardBundlerParam.isRuntimeInstaller(params)) { - // prepare installer icon - if (icon == null || !icon.exists()) { - fetchResource(iconTarget.getName(), - I18N.getString("resource.menu-icon"), - DEFAULT_ICON, - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } else { - fetchResource(iconTarget.getName(), - I18N.getString("resource.menu-icon"), - icon, - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } - } - - StringBuilder installScripts = new StringBuilder(); - StringBuilder removeScripts = new StringBuilder(); - for (Map addLauncher : - ADD_LAUNCHERS.fetchFrom(params)) { - Map addLauncherData = - createReplacementData(addLauncher); - addLauncherData.put("APPLICATION_FS_NAME", - data.get("APPLICATION_FS_NAME")); - addLauncherData.put("DESKTOP_MIMES", ""); - - if (!StandardBundlerParam.isRuntimeInstaller(params)) { - // prepare desktop shortcut - Writer w = new BufferedWriter(new FileWriter( - getConfig_DesktopShortcutFile( - rootDir, addLauncher))); - String content = preprocessTextResource( - getConfig_DesktopShortcutFile(rootDir, - addLauncher).getName(), - I18N.getString("resource.menu-shortcut-descriptor"), - DEFAULT_DESKTOP_FILE_TEMPLATE, - addLauncherData, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - } - - // prepare installer icon - iconTarget = getConfig_IconFile(rootDir, addLauncher); - icon = ICON_PNG.fetchFrom(addLauncher); - if (icon == null || !icon.exists()) { - fetchResource(iconTarget.getName(), - I18N.getString("resource.menu-icon"), - DEFAULT_ICON, - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } else { - fetchResource(iconTarget.getName(), - I18N.getString("resource.menu-icon"), - icon, - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } - - // postinst copying of desktop icon - installScripts.append( - " xdg-desktop-menu install --novendor "); - installScripts.append(LINUX_INSTALL_DIR.fetchFrom(params)); - installScripts.append("/"); - installScripts.append(data.get("APPLICATION_FS_NAME")); - installScripts.append("/"); - installScripts.append( - addLauncherData.get("APPLICATION_LAUNCHER_FILENAME")); - installScripts.append(".desktop\n"); - - // postrm cleanup of desktop icon - removeScripts.append( - " xdg-desktop-menu uninstall --novendor "); - removeScripts.append(LINUX_INSTALL_DIR.fetchFrom(params)); - removeScripts.append("/"); - removeScripts.append(data.get("APPLICATION_FS_NAME")); - removeScripts.append("/"); - removeScripts.append( - addLauncherData.get("APPLICATION_LAUNCHER_FILENAME")); - removeScripts.append(".desktop\n"); - } - data.put("ADD_LAUNCHERS_INSTALL", installScripts.toString()); - data.put("ADD_LAUNCHERS_REMOVE", removeScripts.toString()); - - List> associations = - FILE_ASSOCIATIONS.fetchFrom(params); - data.put("FILE_ASSOCIATION_INSTALL", ""); - data.put("FILE_ASSOCIATION_REMOVE", ""); - data.put("DESKTOP_MIMES", ""); - if (associations != null) { - String mimeInfoFile = XDG_FILE_PREFIX.fetchFrom(params) - + "-MimeInfo.xml"; - StringBuilder mimeInfo = new StringBuilder( - "\n\n"); - StringBuilder registrations = new StringBuilder(); - StringBuilder deregistrations = new StringBuilder(); - StringBuilder desktopMimes = new StringBuilder("MimeType="); - boolean addedEntry = false; - - for (Map assoc : associations) { - // - // Awesome document - // - // - // - - if (assoc == null) { - continue; - } - - String description = FA_DESCRIPTION.fetchFrom(assoc); - File faIcon = FA_ICON.fetchFrom(assoc); - List extensions = FA_EXTENSIONS.fetchFrom(assoc); - if (extensions == null) { - Log.error(I18N.getString( - "message.creating-association-with-null-extension")); - } - - List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); - if (mimes == null || mimes.isEmpty()) { - continue; - } - String thisMime = mimes.get(0); - String dashMime = thisMime.replace('/', '-'); - - mimeInfo.append(" \n"); - if (description != null && !description.isEmpty()) { - mimeInfo.append(" ") - .append(description) - .append("\n"); - } - - if (extensions != null) { - for (String ext : extensions) { - mimeInfo.append(" \n"); - } - } - - mimeInfo.append(" \n"); - if (!addedEntry) { - registrations.append(" xdg-mime install ") - .append(LINUX_INSTALL_DIR.fetchFrom(params)) - .append("/") - .append(data.get("APPLICATION_FS_NAME")) - .append("/") - .append(mimeInfoFile) - .append("\n"); - - deregistrations.append(" xdg-mime uninstall ") - .append(LINUX_INSTALL_DIR.fetchFrom(params)) - .append("/") - .append(data.get("APPLICATION_FS_NAME")) - .append("/") - .append(mimeInfoFile) - .append("\n"); - addedEntry = true; - } else { - desktopMimes.append(";"); - } - desktopMimes.append(thisMime); - - if (faIcon != null && faIcon.exists()) { - int size = getSquareSizeOfImage(faIcon); - - if (size > 0) { - File target = new File(rootDir, - APP_NAME.fetchFrom(params) - + "_fa_" + faIcon.getName()); - IOUtils.copyFile(faIcon, target); - - // xdg-icon-resource install --context mimetypes - // --size 64 awesomeapp_fa_1.png - // application-x.vnd-awesome - registrations.append( - " xdg-icon-resource install " - + "--context mimetypes --size ") - .append(size) - .append(" ") - .append(LINUX_INSTALL_DIR.fetchFrom(params)) - .append("/") - .append(data.get("APPLICATION_FS_NAME")) - .append("/") - .append(target.getName()) - .append(" ") - .append(dashMime) - .append("\n"); - - // x dg-icon-resource uninstall --context mimetypes - // --size 64 awesomeapp_fa_1.png - // application-x.vnd-awesome - deregistrations.append( - " xdg-icon-resource uninstall " - + "--context mimetypes --size ") - .append(size) - .append(" ") - .append(LINUX_INSTALL_DIR.fetchFrom(params)) - .append("/") - .append(data.get("APPLICATION_FS_NAME")) - .append("/") - .append(target.getName()) - .append(" ") - .append(dashMime) - .append("\n"); - } - } - } - mimeInfo.append(""); - - if (addedEntry) { - Writer w = new BufferedWriter(new FileWriter( - new File(rootDir, mimeInfoFile))); - w.write(mimeInfo.toString()); - w.close(); - data.put("FILE_ASSOCIATION_INSTALL", registrations.toString()); - data.put("FILE_ASSOCIATION_REMOVE", deregistrations.toString()); - data.put("DESKTOP_MIMES", desktopMimes.toString()); - } - } - - if (!StandardBundlerParam.isRuntimeInstaller(params)) { - //prepare desktop shortcut - Writer w = new BufferedWriter(new FileWriter( - getConfig_DesktopShortcutFile(rootDir, params))); - String content = preprocessTextResource( - getConfig_DesktopShortcutFile( - rootDir, params).getName(), - I18N.getString("resource.menu-shortcut-descriptor"), - DEFAULT_DESKTOP_FILE_TEMPLATE, - data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - } - // prepare control file - Writer w = new BufferedWriter(new FileWriter( - getConfig_ControlFile(params))); - String content = preprocessTextResource( - getConfig_ControlFile(params).getName(), - I18N.getString("resource.deb-control-file"), - DEFAULT_CONTROL_TEMPLATE, - data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - - w = new BufferedWriter(new FileWriter( - getConfig_PreinstallFile(params))); - content = preprocessTextResource( - getConfig_PreinstallFile(params).getName(), - I18N.getString("resource.deb-preinstall-script"), - DEFAULT_PREINSTALL_TEMPLATE, - data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - setPermissions(getConfig_PreinstallFile(params), "rwxr-xr-x"); - - w = new BufferedWriter(new FileWriter(getConfig_PrermFile(params))); - content = preprocessTextResource( - getConfig_PrermFile(params).getName(), - I18N.getString("resource.deb-prerm-script"), - DEFAULT_PRERM_TEMPLATE, - data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - setPermissions(getConfig_PrermFile(params), "rwxr-xr-x"); - - w = new BufferedWriter(new FileWriter( - getConfig_PostinstallFile(params))); - content = preprocessTextResource( - getConfig_PostinstallFile(params).getName(), - I18N.getString("resource.deb-postinstall-script"), - DEFAULT_POSTINSTALL_TEMPLATE, - data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - setPermissions(getConfig_PostinstallFile(params), "rwxr-xr-x"); - - w = new BufferedWriter(new FileWriter(getConfig_PostrmFile(params))); - content = preprocessTextResource( - getConfig_PostrmFile(params).getName(), - I18N.getString("resource.deb-postrm-script"), - DEFAULT_POSTRM_TEMPLATE, - data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - setPermissions(getConfig_PostrmFile(params), "rwxr-xr-x"); - - w = new BufferedWriter(new FileWriter(getConfig_CopyrightFile(params))); - content = preprocessTextResource( - getConfig_CopyrightFile(params).getName(), - I18N.getString("resource.deb-copyright-file"), - DEFAULT_COPYRIGHT_TEMPLATE, - data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - - return true; - } - - private Map createReplacementData( - Map params) { - Map data = new HashMap<>(); - - data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params)); - data.put("APPLICATION_FS_NAME", APP_NAME.fetchFrom(params)); - data.put("APPLICATION_PACKAGE", BUNDLE_NAME.fetchFrom(params)); - data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params)); - data.put("APPLICATION_MAINTAINER", MAINTAINER.fetchFrom(params)); - data.put("APPLICATION_VERSION", VERSION.fetchFrom(params)); - data.put("APPLICATION_LAUNCHER_FILENAME", APP_NAME.fetchFrom(params)); - data.put("INSTALLATION_DIRECTORY", LINUX_INSTALL_DIR.fetchFrom(params)); - data.put("XDG_PREFIX", XDG_FILE_PREFIX.fetchFrom(params)); - data.put("DEPLOY_BUNDLE_CATEGORY", MENU_GROUP.fetchFrom(params)); - data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params)); - data.put("APPLICATION_COPYRIGHT", COPYRIGHT.fetchFrom(params)); - data.put("APPLICATION_LICENSE_TEXT", LICENSE_TEXT.fetchFrom(params)); - data.put("APPLICATION_ARCH", getArch()); - data.put("APPLICATION_INSTALLED_SIZE", - Long.toString(getInstalledSizeKB(params))); - String deps = LINUX_PACKAGE_DEPENDENCIES.fetchFrom(params); - data.put("PACKAGE_DEPENDENCIES", - deps.isEmpty() ? "" : "Depends: " + deps); - data.put("RUNTIME_INSTALLER", "" + - StandardBundlerParam.isRuntimeInstaller(params)); - - return data; - } - - private File getConfig_DesktopShortcutFile(File rootDir, - Map params) { - return new File(rootDir, APP_NAME.fetchFrom(params) + ".desktop"); - } - - private File getConfig_IconFile(File rootDir, - Map params) { - return new File(rootDir, APP_NAME.fetchFrom(params) + ".png"); - } - - private File getConfig_InitScriptFile(Map params) { - return new File(LinuxAppBundler.getRootDir( - APP_IMAGE_ROOT.fetchFrom(params), params), - BUNDLE_NAME.fetchFrom(params) + ".init"); - } - - private File getConfig_ControlFile(Map params) { - return new File(CONFIG_DIR.fetchFrom(params), "control"); - } - - private File getConfig_PreinstallFile(Map params) { - return new File(CONFIG_DIR.fetchFrom(params), "preinst"); - } - - private File getConfig_PrermFile(Map params) { - return new File(CONFIG_DIR.fetchFrom(params), "prerm"); - } - - private File getConfig_PostinstallFile(Map params) { - return new File(CONFIG_DIR.fetchFrom(params), "postinst"); - } - - private File getConfig_PostrmFile(Map params) { - return new File(CONFIG_DIR.fetchFrom(params), "postrm"); - } - - private File getConfig_CopyrightFile(Map params) { - return new File(CONFIG_DIR.fetchFrom(params), "copyright"); - } - - private File buildDeb(Map params, - File outdir) throws IOException { - File outFile = new File(outdir, - FULL_PACKAGE_NAME.fetchFrom(params)+".deb"); - Log.verbose(MessageFormat.format(I18N.getString( - "message.outputting-to-location"), outFile.getAbsolutePath())); - - outFile.getParentFile().mkdirs(); - - // run dpkg - ProcessBuilder pb = new ProcessBuilder( - "fakeroot", TOOL_DPKG, "-b", - FULL_PACKAGE_NAME.fetchFrom(params), - outFile.getAbsolutePath()); - pb = pb.directory(DEB_IMAGE_DIR.fetchFrom(params).getParentFile()); - IOUtils.exec(pb, false); - - Log.verbose(MessageFormat.format(I18N.getString( - "message.output-to-location"), outFile.getAbsolutePath())); - - return outFile; - } - - @Override - public String getName() { - return I18N.getString("deb.bundler.name"); - } - - @Override - public String getDescription() { - return I18N.getString("deb.bundler.description"); - } - - @Override - public String getID() { - return "deb"; - } - - @Override - public String getBundleType() { - return "INSTALLER"; - } - - @Override - public Collection> getBundleParameters() { - Collection> results = new LinkedHashSet<>(); - results.addAll(LinuxAppBundler.getAppBundleParameters()); - results.addAll(getDebBundleParameters()); - return results; - } - - public static Collection> getDebBundleParameters() { - return Arrays.asList( - BUNDLE_NAME, - COPYRIGHT, - MENU_GROUP, - DESCRIPTION, - EMAIL, - ICON_PNG, - LICENSE_FILE, - VENDOR - ); - } - - @Override - public File execute(Map params, - File outputParentDir) throws PackagerException { - return bundle(params, outputParentDir); - } - - @Override - public boolean supported(boolean runtimeInstaller) { - return (Platform.getPlatform() == Platform.LINUX); - } - - public int getSquareSizeOfImage(File f) { - try { - BufferedImage bi = ImageIO.read(f); - if (bi.getWidth() == bi.getHeight()) { - return bi.getWidth(); - } else { - return 0; - } - } catch (Exception e) { - Log.verbose(e); - return 0; - } - } -} --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java 2019-11-18 21:43:19.254359500 -0500 +++ /dev/null 2019-11-18 21:43:20.000000000 -0500 @@ -1,725 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.*; -import java.nio.file.Files; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.PosixFilePermissions; -import java.text.MessageFormat; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static jdk.jpackage.internal.StandardBundlerParam.*; -import static jdk.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR; -import static jdk.jpackage.internal.LinuxAppBundler.LINUX_PACKAGE_DEPENDENCIES; - -public class LinuxRpmBundler extends AbstractBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.LinuxResources"); - - public static final BundlerParamInfo APP_BUNDLER = - new StandardBundlerParam<>( - "linux.app.bundler", - LinuxAppBundler.class, - params -> new LinuxAppBundler(), - null); - - public static final BundlerParamInfo RPM_IMAGE_DIR = - new StandardBundlerParam<>( - "linux.rpm.imageDir", - File.class, - params -> { - File imagesRoot = IMAGES_ROOT.fetchFrom(params); - if (!imagesRoot.exists()) imagesRoot.mkdirs(); - return new File(imagesRoot, "linux-rpm.image"); - }, - (s, p) -> new File(s)); - - // Fedora rules for package naming are used here - // https://fedoraproject.org/wiki/Packaging:NamingGuidelines?rd=Packaging/NamingGuidelines - // - // all Fedora packages must be named using only the following ASCII - // characters. These characters are displayed here: - // - // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+ - // - private static final Pattern RPM_BUNDLE_NAME_PATTERN = - Pattern.compile("[a-z\\d\\+\\-\\.\\_]+", Pattern.CASE_INSENSITIVE); - - public static final BundlerParamInfo BUNDLE_NAME = - new StandardBundlerParam<> ( - Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId(), - String.class, - params -> { - String nm = APP_NAME.fetchFrom(params); - if (nm == null) return null; - - // make sure to lower case and spaces become dashes - nm = nm.toLowerCase().replaceAll("[ ]", "-"); - - return nm; - }, - (s, p) -> { - if (!RPM_BUNDLE_NAME_PATTERN.matcher(s).matches()) { - String msgKey = "error.invalid-value-for-package-name"; - throw new IllegalArgumentException( - new ConfigException(MessageFormat.format( - I18N.getString(msgKey), s), - I18N.getString(msgKey + ".advice"))); - } - - return s; - } - ); - - public static final BundlerParamInfo MENU_GROUP = - new StandardBundlerParam<>( - Arguments.CLIOptions.LINUX_MENU_GROUP.getId(), - String.class, - params -> I18N.getString("param.menu-group.default"), - (s, p) -> s - ); - - public static final BundlerParamInfo LICENSE_TYPE = - new StandardBundlerParam<>( - Arguments.CLIOptions.LINUX_RPM_LICENSE_TYPE.getId(), - String.class, - params -> I18N.getString("param.license-type.default"), - (s, p) -> s - ); - - public static final BundlerParamInfo XDG_FILE_PREFIX = - new StandardBundlerParam<> ( - "linux.xdg-prefix", - String.class, - params -> { - try { - String vendor; - if (params.containsKey(VENDOR.getID())) { - vendor = VENDOR.fetchFrom(params); - } else { - vendor = "jpackage"; - } - String appName = APP_NAME.fetchFrom(params); - - return (vendor + "-" + appName).replaceAll("\\s", ""); - } catch (Exception e) { - if (Log.isDebug()) { - e.printStackTrace(); - } - } - return "unknown-MimeInfo.xml"; - }, - (s, p) -> s); - - private final static String DEFAULT_ICON = "javalogo_white_32.png"; - private final static String DEFAULT_SPEC_TEMPLATE = "template.spec"; - private final static String DEFAULT_DESKTOP_FILE_TEMPLATE = - "template.desktop"; - - public final static String TOOL_RPMBUILD = "rpmbuild"; - public final static double TOOL_RPMBUILD_MIN_VERSION = 4.0d; - - public static boolean testTool(String toolName, double minVersion) { - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintStream ps = new PrintStream(baos)) { - ProcessBuilder pb = new ProcessBuilder(toolName, "--version"); - IOUtils.exec(pb, Log.isDebug(), false, ps); - //not interested in the above's output - String content = new String(baos.toByteArray()); - Pattern pattern = Pattern.compile(" (\\d+\\.\\d+)"); - Matcher matcher = pattern.matcher(content); - - if (matcher.find()) { - String v = matcher.group(1); - double version = Double.parseDouble(v); - return minVersion <= version; - } else { - return false; - } - } catch (Exception e) { - Log.verbose(MessageFormat.format(I18N.getString( - "message.test-for-tool"), toolName, e.getMessage())); - return false; - } - } - - @Override - public boolean validate(Map p) - throws UnsupportedPlatformException, ConfigException { - try { - if (p == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - - // run basic validation to ensure requirements are met - // we are not interested in return code, only possible exception - APP_BUNDLER.fetchFrom(p).validate(p); - - // validate presense of required tools - if (!testTool(TOOL_RPMBUILD, TOOL_RPMBUILD_MIN_VERSION)){ - throw new ConfigException( - MessageFormat.format( - I18N.getString("error.cannot-find-rpmbuild"), - TOOL_RPMBUILD_MIN_VERSION), - MessageFormat.format( - I18N.getString("error.cannot-find-rpmbuild.advice"), - TOOL_RPMBUILD_MIN_VERSION)); - } - - // only one mime type per association, at least one file extension - List> associations = - FILE_ASSOCIATIONS.fetchFrom(p); - if (associations != null) { - for (int i = 0; i < associations.size(); i++) { - Map assoc = associations.get(i); - List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); - if (mimes == null || mimes.isEmpty()) { - String msgKey = - "error.no-content-types-for-file-association"; - throw new ConfigException( - MessageFormat.format(I18N.getString(msgKey), i), - I18N.getString(msgKey + ".advice")); - } else if (mimes.size() > 1) { - String msgKey = - "error.no-content-types-for-file-association"; - throw new ConfigException( - MessageFormat.format(I18N.getString(msgKey), i), - I18N.getString(msgKey + ".advice")); - } - } - } - - // bundle name has some restrictions - // the string converter will throw an exception if invalid - BUNDLE_NAME.getStringConverter().apply(BUNDLE_NAME.fetchFrom(p), p); - - return true; - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - private boolean prepareProto(Map p) - throws PackagerException, IOException { - File appImage = StandardBundlerParam.getPredefinedAppImage(p); - File appDir = null; - - // we either have an application image or need to build one - if (appImage != null) { - appDir = new File(RPM_IMAGE_DIR.fetchFrom(p), - APP_NAME.fetchFrom(p)); - // copy everything from appImage dir into appDir/name - IOUtils.copyRecursive(appImage.toPath(), appDir.toPath()); - } else { - appDir = APP_BUNDLER.fetchFrom(p).doBundle(p, - RPM_IMAGE_DIR.fetchFrom(p), true); - } - return appDir != null; - } - - public File bundle(Map p, - File outdir) throws PackagerException { - if (!outdir.isDirectory() && !outdir.mkdirs()) { - throw new PackagerException( - "error.cannot-create-output-dir", - outdir.getAbsolutePath()); - } - if (!outdir.canWrite()) { - throw new PackagerException( - "error.cannot-write-to-output-dir", - outdir.getAbsolutePath()); - } - - File imageDir = RPM_IMAGE_DIR.fetchFrom(p); - try { - - imageDir.mkdirs(); - - if (prepareProto(p) && prepareProjectConfig(p)) { - return buildRPM(p, outdir); - } - return null; - } catch (IOException ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - private String getLicenseFileString(Map params) - throws IOException { - StringBuilder sb = new StringBuilder(); - - String licenseStr = LICENSE_FILE.fetchFrom(params); - if (licenseStr != null) { - File licenseFile = new File(licenseStr); - File rootDir = - LinuxAppBundler.getRootDir(RPM_IMAGE_DIR.fetchFrom(params), - params); - File target = new File(rootDir + File.separator + "app" - + File.separator + licenseFile.getName()); - Files.copy(licenseFile.toPath(), target.toPath()); - - sb.append("%license "); - sb.append(LINUX_INSTALL_DIR.fetchFrom(params)); - sb.append("/"); - sb.append(APP_NAME.fetchFrom(params)); - sb.append("/app/"); - sb.append(licenseFile.getName()); - } - - return sb.toString(); - } - - private boolean prepareProjectConfig(Map params) - throws IOException { - Map data = createReplacementData(params); - File rootDir = - LinuxAppBundler.getRootDir(RPM_IMAGE_DIR.fetchFrom(params), params); - - // prepare installer icon - File iconTarget = getConfig_IconFile(rootDir, params); - File icon = LinuxAppBundler.ICON_PNG.fetchFrom(params); - if (!StandardBundlerParam.isRuntimeInstaller(params)) { - if (icon == null || !icon.exists()) { - fetchResource(iconTarget.getName(), - I18N.getString("resource.menu-icon"), - DEFAULT_ICON, - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } else { - fetchResource(iconTarget.getName(), - I18N.getString("resource.menu-icon"), - icon, - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } - } - - StringBuilder installScripts = new StringBuilder(); - StringBuilder removeScripts = new StringBuilder(); - for (Map addLauncher : - ADD_LAUNCHERS.fetchFrom(params)) { - Map addLauncherData = - createReplacementData(addLauncher); - addLauncherData.put("APPLICATION_FS_NAME", - data.get("APPLICATION_FS_NAME")); - addLauncherData.put("DESKTOP_MIMES", ""); - - // prepare desktop shortcut - Writer w = new BufferedWriter(new FileWriter( - getConfig_DesktopShortcutFile(rootDir, addLauncher))); - String content = preprocessTextResource( - getConfig_DesktopShortcutFile(rootDir, - addLauncher).getName(), - I18N.getString("resource.menu-shortcut-descriptor"), - DEFAULT_DESKTOP_FILE_TEMPLATE, addLauncherData, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - - // prepare installer icon - iconTarget = getConfig_IconFile(rootDir, addLauncher); - icon = LinuxAppBundler.ICON_PNG.fetchFrom(addLauncher); - if (icon == null || !icon.exists()) { - fetchResource(iconTarget.getName(), - I18N.getString("resource.menu-icon"), - DEFAULT_ICON, - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } else { - fetchResource(iconTarget.getName(), - I18N.getString("resource.menu-icon"), - icon, - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } - - // post copying of desktop icon - installScripts.append("xdg-desktop-menu install --novendor "); - installScripts.append(LINUX_INSTALL_DIR.fetchFrom(params)); - installScripts.append("/"); - installScripts.append(data.get("APPLICATION_FS_NAME")); - installScripts.append("/"); - installScripts.append(addLauncherData.get( - "APPLICATION_LAUNCHER_FILENAME")); - installScripts.append(".desktop\n"); - - // preun cleanup of desktop icon - removeScripts.append("xdg-desktop-menu uninstall --novendor "); - removeScripts.append(LINUX_INSTALL_DIR.fetchFrom(params)); - removeScripts.append("/"); - removeScripts.append(data.get("APPLICATION_FS_NAME")); - removeScripts.append("/"); - removeScripts.append(addLauncherData.get( - "APPLICATION_LAUNCHER_FILENAME")); - removeScripts.append(".desktop\n"); - - } - data.put("ADD_LAUNCHERS_INSTALL", installScripts.toString()); - data.put("ADD_LAUNCHERS_REMOVE", removeScripts.toString()); - - StringBuilder cdsScript = new StringBuilder(); - - data.put("APP_CDS_CACHE", cdsScript.toString()); - - List> associations = - FILE_ASSOCIATIONS.fetchFrom(params); - data.put("FILE_ASSOCIATION_INSTALL", ""); - data.put("FILE_ASSOCIATION_REMOVE", ""); - data.put("DESKTOP_MIMES", ""); - if (associations != null) { - String mimeInfoFile = XDG_FILE_PREFIX.fetchFrom(params) - + "-MimeInfo.xml"; - StringBuilder mimeInfo = new StringBuilder( - "\n\n"); - StringBuilder registrations = new StringBuilder(); - StringBuilder deregistrations = new StringBuilder(); - StringBuilder desktopMimes = new StringBuilder("MimeType="); - boolean addedEntry = false; - - for (Map assoc : associations) { - // - // Awesome document - // - // - // - - if (assoc == null) { - continue; - } - - String description = FA_DESCRIPTION.fetchFrom(assoc); - File faIcon = FA_ICON.fetchFrom(assoc); //TODO FA_ICON_PNG - List extensions = FA_EXTENSIONS.fetchFrom(assoc); - if (extensions == null) { - Log.verbose(I18N.getString( - "message.creating-association-with-null-extension")); - } - - List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); - if (mimes == null || mimes.isEmpty()) { - continue; - } - String thisMime = mimes.get(0); - String dashMime = thisMime.replace('/', '-'); - - mimeInfo.append(" \n"); - if (description != null && !description.isEmpty()) { - mimeInfo.append(" ") - .append(description) - .append("\n"); - } - - if (extensions != null) { - for (String ext : extensions) { - mimeInfo.append(" \n"); - } - } - - mimeInfo.append(" \n"); - if (!addedEntry) { - registrations.append("xdg-mime install ") - .append(LINUX_INSTALL_DIR.fetchFrom(params)) - .append("/") - .append(data.get("APPLICATION_FS_NAME")) - .append("/") - .append(mimeInfoFile) - .append("\n"); - - deregistrations.append("xdg-mime uninstall ") - .append(LINUX_INSTALL_DIR.fetchFrom(params)) - .append("/") - .append(data.get("APPLICATION_FS_NAME")) - .append("/") - .append(mimeInfoFile) - .append("\n"); - addedEntry = true; - } else { - desktopMimes.append(";"); - } - desktopMimes.append(thisMime); - - if (faIcon != null && faIcon.exists()) { - int size = getSquareSizeOfImage(faIcon); - - if (size > 0) { - File target = new File(rootDir, - APP_NAME.fetchFrom(params) - + "_fa_" + faIcon.getName()); - IOUtils.copyFile(faIcon, target); - - // xdg-icon-resource install --context mimetypes - // --size 64 awesomeapp_fa_1.png - // application-x.vnd-awesome - registrations.append( - "xdg-icon-resource install " - + "--context mimetypes --size ") - .append(size) - .append(" ") - .append(LINUX_INSTALL_DIR.fetchFrom(params)) - .append("/") - .append(data.get("APPLICATION_FS_NAME")) - .append("/") - .append(target.getName()) - .append(" ") - .append(dashMime) - .append("\n"); - - // xdg-icon-resource uninstall --context mimetypes - // --size 64 awesomeapp_fa_1.png - // application-x.vnd-awesome - deregistrations.append( - "xdg-icon-resource uninstall " - + "--context mimetypes --size ") - .append(size) - .append(" ") - .append(LINUX_INSTALL_DIR.fetchFrom(params)) - .append("/") - .append(data.get("APPLICATION_FS_NAME")) - .append("/") - .append(target.getName()) - .append(" ") - .append(dashMime) - .append("\n"); - } - } - } - mimeInfo.append(""); - - if (addedEntry) { - Writer w = new BufferedWriter(new FileWriter( - new File(rootDir, mimeInfoFile))); - w.write(mimeInfo.toString()); - w.close(); - data.put("FILE_ASSOCIATION_INSTALL", registrations.toString()); - data.put("FILE_ASSOCIATION_REMOVE", deregistrations.toString()); - data.put("DESKTOP_MIMES", desktopMimes.toString()); - } - } - - if (!StandardBundlerParam.isRuntimeInstaller(params)) { - //prepare desktop shortcut - Writer w = new BufferedWriter(new FileWriter( - getConfig_DesktopShortcutFile(rootDir, params))); - String content = preprocessTextResource( - getConfig_DesktopShortcutFile(rootDir, params).getName(), - I18N.getString("resource.menu-shortcut-descriptor"), - DEFAULT_DESKTOP_FILE_TEMPLATE, data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - } - - // prepare spec file - Writer w = new BufferedWriter( - new FileWriter(getConfig_SpecFile(params))); - String content = preprocessTextResource( - getConfig_SpecFile(params).getName(), - I18N.getString("resource.rpm-spec-file"), - DEFAULT_SPEC_TEMPLATE, data, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - - return true; - } - - private Map createReplacementData( - Map params) throws IOException { - Map data = new HashMap<>(); - - data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params)); - data.put("APPLICATION_FS_NAME", APP_NAME.fetchFrom(params)); - data.put("APPLICATION_PACKAGE", BUNDLE_NAME.fetchFrom(params)); - data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params)); - data.put("APPLICATION_VERSION", VERSION.fetchFrom(params)); - data.put("APPLICATION_LAUNCHER_FILENAME", APP_NAME.fetchFrom(params)); - data.put("INSTALLATION_DIRECTORY", LINUX_INSTALL_DIR.fetchFrom(params)); - data.put("XDG_PREFIX", XDG_FILE_PREFIX.fetchFrom(params)); - data.put("DEPLOY_BUNDLE_CATEGORY", MENU_GROUP.fetchFrom(params)); - // TODO rpm categories - data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params)); - data.put("APPLICATION_SUMMARY", APP_NAME.fetchFrom(params)); - data.put("APPLICATION_LICENSE_TYPE", LICENSE_TYPE.fetchFrom(params)); - data.put("APPLICATION_LICENSE_FILE", getLicenseFileString(params)); - String deps = LINUX_PACKAGE_DEPENDENCIES.fetchFrom(params); - data.put("PACKAGE_DEPENDENCIES", - deps.isEmpty() ? "" : "Requires: " + deps); - data.put("RUNTIME_INSTALLER", "" + - StandardBundlerParam.isRuntimeInstaller(params)); - return data; - } - - private File getConfig_DesktopShortcutFile(File rootDir, - Map params) { - return new File(rootDir, APP_NAME.fetchFrom(params) + ".desktop"); - } - - private File getConfig_IconFile(File rootDir, - Map params) { - return new File(rootDir, APP_NAME.fetchFrom(params) + ".png"); - } - - private File getConfig_SpecFile(Map params) { - return new File(RPM_IMAGE_DIR.fetchFrom(params), - APP_NAME.fetchFrom(params) + ".spec"); - } - - private File buildRPM(Map params, - File outdir) throws IOException { - Log.verbose(MessageFormat.format(I18N.getString( - "message.outputting-bundle-location"), - outdir.getAbsolutePath())); - - File broot = new File(TEMP_ROOT.fetchFrom(params), "rmpbuildroot"); - - outdir.mkdirs(); - - //run rpmbuild - ProcessBuilder pb = new ProcessBuilder( - TOOL_RPMBUILD, - "-bb", getConfig_SpecFile(params).getAbsolutePath(), - "--define", "%_sourcedir " - + RPM_IMAGE_DIR.fetchFrom(params).getAbsolutePath(), - // save result to output dir - "--define", "%_rpmdir " + outdir.getAbsolutePath(), - // do not use other system directories to build as current user - "--define", "%_topdir " + broot.getAbsolutePath() - ); - pb = pb.directory(RPM_IMAGE_DIR.fetchFrom(params)); - IOUtils.exec(pb, false); - - Log.verbose(MessageFormat.format( - I18N.getString("message.output-bundle-location"), - outdir.getAbsolutePath())); - - // presume the result is the ".rpm" file with the newest modified time - // not the best solution, but it is the most reliable - File result = null; - long lastModified = 0; - File[] list = outdir.listFiles(); - if (list != null) { - for (File f : list) { - if (f.getName().endsWith(".rpm") && - f.lastModified() > lastModified) { - result = f; - lastModified = f.lastModified(); - } - } - } - - return result; - } - - @Override - public String getName() { - return I18N.getString("rpm.bundler.name"); - } - - @Override - public String getDescription() { - return I18N.getString("rpm.bundler.description"); - } - - @Override - public String getID() { - return "rpm"; - } - - @Override - public String getBundleType() { - return "INSTALLER"; - } - - @Override - public Collection> getBundleParameters() { - Collection> results = new LinkedHashSet<>(); - results.addAll(LinuxAppBundler.getAppBundleParameters()); - results.addAll(getRpmBundleParameters()); - return results; - } - - public static Collection> getRpmBundleParameters() { - return Arrays.asList( - BUNDLE_NAME, - MENU_GROUP, - DESCRIPTION, - LinuxAppBundler.ICON_PNG, - LICENSE_FILE, - LICENSE_TYPE, - VENDOR - ); - } - - @Override - public File execute(Map params, - File outputParentDir) throws PackagerException { - return bundle(params, outputParentDir); - } - - @Override - public boolean supported(boolean runtimeInstaller) { - return (Platform.getPlatform() == Platform.LINUX); - } - - public int getSquareSizeOfImage(File f) { - try { - BufferedImage bi = ImageIO.read(f); - if (bi.getWidth() == bi.getHeight()) { - return bi.getWidth(); - } else { - return 0; - } - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } -} --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties 2019-11-18 21:43:28.828165200 -0500 +++ /dev/null 2019-11-18 21:43:30.000000000 -0500 @@ -1,78 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# -app.bundler.name=Linux Application Image -app.bundler.description=A Directory based image of a linux Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers. -deb.bundler.name=DEB Installer -deb.bundler.description=Linux Debian Bundle. -rpm.bundler.name=RPM Bundle -rpm.bundler.description=Redhat Package Manager (RPM) bundler. - -param.license-type.default=Unknown -param.menu-group.default=Unknown - -resource.deb-control-file=DEB control file -resource.deb-preinstall-script=DEB preinstall script -resource.deb-prerm-script=DEB prerm script -resource.deb-postinstall-script=DEB postinstall script -resource.deb-postrm-script=DEB postrm script -resource.deb-copyright-file=DEB copyright file -resource.deb-init-script=DEB init script -resource.menu-shortcut-descriptor=Menu shortcut descriptor -resource.menu-icon=menu icon -resource.rpm-spec-file=RPM spec file -resource.rpm-init-script=RPM init script - -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.tool-not-found=Can not find {0}. -error.tool-not-found.advice=Please install required packages. -error.launcher-name-too-long=The bundle name "{0}" is too long for a daemon. -error.launcher-name-too-long.advice=Set a bundler argument "{0}" to a bundle name that is shorter than 16 characters. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}. -error.no-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. -error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. -error.too-many-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. -error.no-support-for-peruser-daemons=Bundler doesn't support per-user daemons. -error.no-support-for-peruser-daemons.advice=Make sure that the system wide hint is set to true. -error.invalid-value-for-package-name=Invalid value "{0}" for the package name. -error.invalid-value-for-package-name.advice=Set the "linux.bundleName" parameter to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character. -error.cannot-find-rpmbuild=Can not find rpmbuild {0} or newer. -error.cannot-find-rpmbuild.advice=\ Install packages needed to build RPM, version {0} or newer. - - -message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place. -message.test-for-tool=Test for [{0}]. Result: {1} -message.outputting-to-location=Generating DEB for installer to: {0}. -message.output-to-location=Package (.deb) saved to: {0}. -message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application. -message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. -message.outputting-bundle-location=Generating RPM for installer to: {0}. -message.output-bundle-location=Package (.rpm) saved to: {0}. -message.creating-association-with-null-extension=Creating association with null extension. --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties 2019-11-18 21:43:37.655002000 -0500 +++ /dev/null 2019-11-18 21:43:39.000000000 -0500 @@ -1,78 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# -app.bundler.name=Linux Application Image -app.bundler.description=A Directory based image of a linux Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers. -deb.bundler.name=DEB Installer -deb.bundler.description=Linux Debian Bundle. -rpm.bundler.name=RPM Bundle -rpm.bundler.description=Redhat Package Manager (RPM) bundler. - -param.license-type.default=Unknown -param.menu-group.default=Unknown - -resource.deb-control-file=DEB control file -resource.deb-preinstall-script=DEB preinstall script -resource.deb-prerm-script=DEB prerm script -resource.deb-postinstall-script=DEB postinstall script -resource.deb-postrm-script=DEB postrm script -resource.deb-copyright-file=DEB copyright file -resource.deb-init-script=DEB init script -resource.menu-shortcut-descriptor=Menu shortcut descriptor -resource.menu-icon=menu icon -resource.rpm-spec-file=RPM spec file -resource.rpm-init-script=RPM init script - -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.tool-not-found=Can not find {0}. -error.tool-not-found.advice=Please install required packages. -error.launcher-name-too-long=The bundle name "{0}" is too long for a daemon. -error.launcher-name-too-long.advice=Set a bundler argument "{0}" to a bundle name that is shorter than 16 characters. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}. -error.no-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. -error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. -error.too-many-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. -error.no-support-for-peruser-daemons=Bundler doesn't support per-user daemons. -error.no-support-for-peruser-daemons.advice=Make sure that the system wide hint is set to true. -error.invalid-value-for-package-name=Invalid value "{0}" for the package name. -error.invalid-value-for-package-name.advice=Set the "linux.bundleName" parameter to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character. -error.cannot-find-rpmbuild=Can not find rpmbuild {0} or newer. -error.cannot-find-rpmbuild.advice=\ Install packages needed to build RPM, version {0} or newer. - - -message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place. -message.test-for-tool=Test for [{0}]. Result: {1} -message.outputting-to-location=Generating DEB for installer to: {0}. -message.output-to-location=Package (.deb) saved to: {0}. -message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application. -message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. -message.outputting-bundle-location=Generating RPM for installer to: {0}. -message.output-bundle-location=Package (.rpm) saved to: {0}. -message.creating-association-with-null-extension=Creating association with null extension. --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties 2019-11-18 21:43:46.477758300 -0500 +++ /dev/null 2019-11-18 21:43:47.000000000 -0500 @@ -1,78 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# -app.bundler.name=Linux Application Image -app.bundler.description=A Directory based image of a linux Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers. -deb.bundler.name=DEB Installer -deb.bundler.description=Linux Debian Bundle. -rpm.bundler.name=RPM Bundle -rpm.bundler.description=Redhat Package Manager (RPM) bundler. - -param.license-type.default=Unknown -param.menu-group.default=Unknown - -resource.deb-control-file=DEB control file -resource.deb-preinstall-script=DEB preinstall script -resource.deb-prerm-script=DEB prerm script -resource.deb-postinstall-script=DEB postinstall script -resource.deb-postrm-script=DEB postrm script -resource.deb-copyright-file=DEB copyright file -resource.deb-init-script=DEB init script -resource.menu-shortcut-descriptor=Menu shortcut descriptor -resource.menu-icon=menu icon -resource.rpm-spec-file=RPM spec file -resource.rpm-init-script=RPM init script - -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.tool-not-found=Can not find {0}. -error.tool-not-found.advice=Please install required packages. -error.launcher-name-too-long=The bundle name "{0}" is too long for a daemon. -error.launcher-name-too-long.advice=Set a bundler argument "{0}" to a bundle name that is shorter than 16 characters. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}. -error.no-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. -error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. -error.too-many-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. -error.no-support-for-peruser-daemons=Bundler doesn't support per-user daemons. -error.no-support-for-peruser-daemons.advice=Make sure that the system wide hint is set to true. -error.invalid-value-for-package-name=Invalid value "{0}" for the package name. -error.invalid-value-for-package-name.advice=Set the "linux.bundleName" parameter to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character. -error.cannot-find-rpmbuild=Can not find rpmbuild {0} or newer. -error.cannot-find-rpmbuild.advice=\ Install packages needed to build RPM, version {0} or newer. - - -message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place. -message.test-for-tool=Test for [{0}]. Result: {1} -message.outputting-to-location=Generating DEB for installer to: {0}. -message.output-to-location=Package (.deb) saved to: {0}. -message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application. -message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. -message.outputting-bundle-location=Generating RPM for installer to: {0}. -message.output-bundle-location=Package (.rpm) saved to: {0}. -message.creating-association-with-null-extension=Creating association with null extension. --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.copyright 2019-11-18 21:43:55.108601000 -0500 +++ /dev/null 2019-11-18 21:43:56.000000000 -0500 @@ -1,8 +0,0 @@ - -Copyright: - - APPLICATION_COPYRIGHT - -License: - - APPLICATION_LICENSE_TEXT --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop 2019-11-18 21:44:03.702389300 -0500 +++ /dev/null 2019-11-18 21:44:05.000000000 -0500 @@ -1,9 +0,0 @@ -[Desktop Entry] -Name=APPLICATION_NAME -Comment=APPLICATION_DESCRIPTION -Exec=INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME -Icon=INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.png -Terminal=false -Type=Application -Categories=DEPLOY_BUNDLE_CATEGORY -DESKTOP_MIMES --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postinst 2019-11-18 21:44:12.705041900 -0500 +++ /dev/null 2019-11-18 21:44:14.000000000 -0500 @@ -1,61 +0,0 @@ -#!/bin/sh -# postinst script for APPLICATION_NAME -# -# see: dh_installdeb(1) - -set -e - -# summary of how this script can be called: -# * `configure' -# * `abort-upgrade' -# * `abort-remove' `in-favour' -# -# * `abort-remove' -# * `abort-deconfigure' `in-favour' -# `removing' -# -# for details, see http://www.debian.org/doc/debian-policy/ or -# the debian-policy package - -case "$1" in - configure) - if [ "RUNTIME_INSTALLER" != "true" ]; then - echo Adding shortcut to the menu -ADD_LAUNCHERS_INSTALL - xdg-desktop-menu install --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop -FILE_ASSOCIATION_INSTALL - fi - if [ "SERVICE_HINT" = "true" ]; then - echo Installing daemon - cp INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_PACKAGE.init /etc/init.d/APPLICATION_PACKAGE - - if [ -x "/etc/init.d/APPLICATION_PACKAGE" ]; then - update-rc.d APPLICATION_PACKAGE defaults - - if [ "START_ON_INSTALL" = "true" ]; then - if which invoke-rc.d >/dev/null 2>&1; then - invoke-rc.d APPLICATION_PACKAGE start - else - /etc/init.d/APPLICATION_PACKAGE start - fi - fi - fi - - fi - ;; - - abort-upgrade|abort-remove|abort-deconfigure) - ;; - - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 --- old/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec 2019-11-18 21:44:21.792661600 -0500 +++ /dev/null 2019-11-18 21:44:23.000000000 -0500 @@ -1,60 +0,0 @@ -Summary: APPLICATION_SUMMARY -Name: APPLICATION_PACKAGE -Version: APPLICATION_VERSION -Release: 1 -License: APPLICATION_LICENSE_TYPE -Vendor: APPLICATION_VENDOR -Prefix: INSTALLATION_DIRECTORY -Provides: APPLICATION_PACKAGE -Autoprov: 0 -Autoreq: 0 -PACKAGE_DEPENDENCIES - -#avoid ARCH subfolder -%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm - -#comment line below to enable effective jar compression -#it could easily get your package size from 40 to 15Mb but -#build time will substantially increase and it may require unpack200/system java to install -%define __jar_repack %{nil} - -%description -APPLICATION_DESCRIPTION - -%prep - -%build - -%install -rm -rf %{buildroot} -mkdir -p %{buildroot}INSTALLATION_DIRECTORY -cp -r %{_sourcedir}/APPLICATION_FS_NAME %{buildroot}INSTALLATION_DIRECTORY - -%files -APPLICATION_LICENSE_FILE -INSTALLATION_DIRECTORY/APPLICATION_FS_NAME - -%post -if [ "RUNTIME_INSTALLER" != "true" ]; then -ADD_LAUNCHERS_INSTALL - xdg-desktop-menu install --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop -FILE_ASSOCIATION_INSTALL -fi - -%preun -if [ "RUNTIME_INSTALLER" != "true" ]; then -ADD_LAUNCHERS_REMOVE - xdg-desktop-menu uninstall --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop -FILE_ASSOCIATION_REMOVE -fi -if [ "SERVICE_HINT" = "true" ]; then - if [ -x "/etc/init.d/APPLICATION_PACKAGE" ]; then - if [ "STOP_ON_UNINSTALL" = "true" ]; then - /etc/init.d/APPLICATION_PACKAGE stop - fi - /sbin/chkconfig --del APPLICATION_PACKAGE - rm -f /etc/init.d/APPLICATION_PACKAGE - fi -fi - -%clean --- old/src/jdk.jpackage/linux/classes/module-info.java.extra 2019-11-18 21:44:30.470196100 -0500 +++ /dev/null 2019-11-18 21:44:31.000000000 -0500 @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -provides jdk.jpackage.internal.Bundler with - jdk.jpackage.internal.LinuxAppBundler, - jdk.jpackage.internal.LinuxDebBundler, - jdk.jpackage.internal.LinuxRpmBundler; - --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java 2019-11-18 21:44:39.372014000 -0500 +++ /dev/null 2019-11-18 21:44:40.000000000 -0500 @@ -1,368 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.io.IOException; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; - -import static jdk.jpackage.internal.StandardBundlerParam.*; -import static jdk.jpackage.internal.MacAppBundler.*; - -public class MacAppStoreBundler extends MacBaseInstallerBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MacResources"); - - private static final String TEMPLATE_BUNDLE_ICON_HIDPI = - "GenericAppHiDPI.icns"; - private final static String DEFAULT_ENTITLEMENTS = - "MacAppStore.entitlements"; - private final static String DEFAULT_INHERIT_ENTITLEMENTS = - "MacAppStore_Inherit.entitlements"; - - public static final BundlerParamInfo MAC_APP_STORE_APP_SIGNING_KEY = - new StandardBundlerParam<>( - "mac.signing-key-app", - String.class, - params -> { - String result = MacBaseInstallerBundler.findKey( - "3rd Party Mac Developer Application: " + - SIGNING_KEY_USER.fetchFrom(params), - SIGNING_KEYCHAIN.fetchFrom(params), - VERBOSE.fetchFrom(params)); - if (result != null) { - MacCertificate certificate = new MacCertificate(result, - VERBOSE.fetchFrom(params)); - - if (!certificate.isValid()) { - Log.error(MessageFormat.format( - I18N.getString("error.certificate.expired"), - result)); - } - } - - return result; - }, - (s, p) -> s); - - public static final BundlerParamInfo MAC_APP_STORE_PKG_SIGNING_KEY = - new StandardBundlerParam<>( - "mac.signing-key-pkg", - String.class, - params -> { - String result = MacBaseInstallerBundler.findKey( - "3rd Party Mac Developer Installer: " + - SIGNING_KEY_USER.fetchFrom(params), - SIGNING_KEYCHAIN.fetchFrom(params), - VERBOSE.fetchFrom(params)); - - if (result != null) { - MacCertificate certificate = new MacCertificate( - result, VERBOSE.fetchFrom(params)); - - if (!certificate.isValid()) { - Log.error(MessageFormat.format( - I18N.getString("error.certificate.expired"), - result)); - } - } - - return result; - }, - (s, p) -> s); - - public static final StandardBundlerParam MAC_APP_STORE_ENTITLEMENTS = - new StandardBundlerParam<>( - Arguments.CLIOptions.MAC_APP_STORE_ENTITLEMENTS.getId(), - File.class, - params -> null, - (s, p) -> new File(s)); - - public static final BundlerParamInfo INSTALLER_SUFFIX = - new StandardBundlerParam<> ( - "mac.app-store.installerName.suffix", - String.class, - params -> "-MacAppStore", - (s, p) -> s); - - //@Override - public File bundle(Map p, - File outdir) throws PackagerException { - Log.verbose(MessageFormat.format(I18N.getString( - "message.building-bundle"), APP_NAME.fetchFrom(p))); - if (!outdir.isDirectory() && !outdir.mkdirs()) { - throw new PackagerException( - "error.cannot-create-output-dir", - outdir.getAbsolutePath()); - } - if (!outdir.canWrite()) { - throw new PackagerException( - "error.cannot-write-to-output-dir", - outdir.getAbsolutePath()); - } - - // first, load in some overrides - // icns needs @2 versions, so load in the @2 default - p.put(DEFAULT_ICNS_ICON.getID(), TEMPLATE_BUNDLE_ICON_HIDPI); - - // now we create the app - File appImageDir = APP_IMAGE_TEMP_ROOT.fetchFrom(p); - try { - appImageDir.mkdirs(); - - try { - MacAppImageBuilder.addNewKeychain(p); - } catch (InterruptedException e) { - Log.error(e.getMessage()); - } - // first, make sure we don't use the local signing key - p.put(DEVELOPER_ID_APP_SIGNING_KEY.getID(), null); - File appLocation = prepareAppBundle(p, false); - - prepareEntitlements(p); - - String signingIdentity = MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(p); - String identifierPrefix = BUNDLE_ID_SIGNING_PREFIX.fetchFrom(p); - String entitlementsFile = getConfig_Entitlements(p).toString(); - String inheritEntitlements = - getConfig_Inherit_Entitlements(p).toString(); - - MacAppImageBuilder.signAppBundle(p, appLocation.toPath(), - signingIdentity, identifierPrefix, - entitlementsFile, inheritEntitlements); - MacAppImageBuilder.restoreKeychainList(p); - - ProcessBuilder pb; - - // create the final pkg file - File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(p) - + INSTALLER_SUFFIX.fetchFrom(p) - + ".pkg"); - outdir.mkdirs(); - - String installIdentify = - MAC_APP_STORE_PKG_SIGNING_KEY.fetchFrom(p); - - List buildOptions = new ArrayList<>(); - buildOptions.add("productbuild"); - buildOptions.add("--component"); - buildOptions.add(appLocation.toString()); - buildOptions.add("/Applications"); - buildOptions.add("--sign"); - buildOptions.add(installIdentify); - buildOptions.add("--product"); - buildOptions.add(appLocation + "/Contents/Info.plist"); - String keychainName = SIGNING_KEYCHAIN.fetchFrom(p); - if (keychainName != null && !keychainName.isEmpty()) { - buildOptions.add("--keychain"); - buildOptions.add(keychainName); - } - buildOptions.add(finalPKG.getAbsolutePath()); - - pb = new ProcessBuilder(buildOptions); - - IOUtils.exec(pb, false); - return finalPKG; - } catch (PackagerException pe) { - throw pe; - } catch (Exception ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - private File getConfig_Entitlements(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + ".entitlements"); - } - - private File getConfig_Inherit_Entitlements( - Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "_Inherit.entitlements"); - } - - private void prepareEntitlements(Map params) - throws IOException { - File entitlements = MAC_APP_STORE_ENTITLEMENTS.fetchFrom(params); - if (entitlements == null || !entitlements.exists()) { - fetchResource(getEntitlementsFileName(params), - I18N.getString("resource.mac-app-store-entitlements"), - DEFAULT_ENTITLEMENTS, - getConfig_Entitlements(params), - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } else { - fetchResource(getEntitlementsFileName(params), - I18N.getString("resource.mac-app-store-entitlements"), - entitlements, - getConfig_Entitlements(params), - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } - fetchResource(getInheritEntitlementsFileName(params), - I18N.getString("resource.mac-app-store-inherit-entitlements"), - DEFAULT_INHERIT_ENTITLEMENTS, - getConfig_Inherit_Entitlements(params), - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } - - private String getEntitlementsFileName(Map params) { - return APP_NAME.fetchFrom(params) + ".entitlements"; - } - - private String getInheritEntitlementsFileName( - Map params) { - return APP_NAME.fetchFrom(params) + "_Inherit.entitlements"; - } - - - /////////////////////////////////////////////////////////////////////// - // Implement Bundler - /////////////////////////////////////////////////////////////////////// - - @Override - public String getName() { - return I18N.getString("store.bundler.name"); - } - - @Override - public String getDescription() { - return I18N.getString("store.bundler.description"); - } - - @Override - public String getID() { - return "mac.appStore"; - } - - @Override - public Collection> getBundleParameters() { - Collection> results = new LinkedHashSet<>(); - results.addAll(getAppBundleParameters()); - results.addAll(getMacAppStoreBundleParameters()); - return results; - } - - public Collection> getMacAppStoreBundleParameters() { - Collection> results = new LinkedHashSet<>(); - - results.addAll(getAppBundleParameters()); - results.remove(DEVELOPER_ID_APP_SIGNING_KEY); - results.addAll(Arrays.asList( - INSTALLER_SUFFIX, - MAC_APP_STORE_APP_SIGNING_KEY, - MAC_APP_STORE_ENTITLEMENTS, - MAC_APP_STORE_PKG_SIGNING_KEY, - SIGNING_KEYCHAIN - )); - - return results; - } - - @Override - public boolean validate(Map params) - throws UnsupportedPlatformException, ConfigException { - try { - if (Platform.getPlatform() != Platform.MAC) { - throw new UnsupportedPlatformException(); - } - - if (params == null) { - throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - } - - // hdiutil is always available so there's no need to test for - // availability. - // run basic validation to ensure requirements are met - - // TODO Mac App Store apps cannot use the system runtime - - // we are not interested in return code, only possible exception - validateAppImageAndBundeler(params); - - // reject explicitly set to not sign - if (!Optional.ofNullable(MacAppImageBuilder. - SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) { - throw new ConfigException( - I18N.getString("error.must-sign-app-store"), - I18N.getString("error.must-sign-app-store.advice")); - } - - // make sure we have settings for signatures - if (MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(params) == null) { - throw new ConfigException( - I18N.getString("error.no-app-signing-key"), - I18N.getString("error.no-app-signing-key.advice")); - } - if (MAC_APP_STORE_PKG_SIGNING_KEY.fetchFrom(params) == null) { - throw new ConfigException( - I18N.getString("error.no-pkg-signing-key"), - I18N.getString("error.no-pkg-signing-key.advice")); - } - - // things we could check... - // check the icons, make sure it has hidpi icons - // check the category, - // make sure it fits in the list apple has provided - // validate bundle identifier is reverse dns - // check for \a+\.\a+\.. - - return true; - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - @Override - public File execute(Map params, - File outputParentDir) throws PackagerException { - return bundle(params, outputParentDir); - } - - @Override - public boolean supported(boolean runtimeInstaller) { - // return (!runtimeInstaller && - // Platform.getPlatform() == Platform.MAC); - return false; // mac-app-store not yet supported - } -} --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java 2019-11-18 21:44:48.483468100 -0500 +++ /dev/null 2019-11-18 21:44:50.000000000 -0500 @@ -1,483 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.*; -import java.nio.file.Files; -import java.text.MessageFormat; -import java.util.*; - -import static jdk.jpackage.internal.StandardBundlerParam.*; - -public class MacDmgBundler extends MacBaseInstallerBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MacResources"); - - static final String DEFAULT_BACKGROUND_IMAGE="background_dmg.png"; - static final String DEFAULT_DMG_SETUP_SCRIPT="DMGsetup.scpt"; - static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns"; - - static final String DEFAULT_LICENSE_PLIST="lic_template.plist"; - - public static final BundlerParamInfo INSTALLER_SUFFIX = - new StandardBundlerParam<> ( - "mac.dmg.installerName.suffix", - String.class, - params -> "", - (s, p) -> s); - - public File bundle(Map params, - File outdir) throws PackagerException { - Log.verbose(MessageFormat.format(I18N.getString("message.building-dmg"), - APP_NAME.fetchFrom(params))); - if (!outdir.isDirectory() && !outdir.mkdirs()) { - throw new PackagerException( - "error.cannot-create-output-dir", - outdir.getAbsolutePath()); - } - if (!outdir.canWrite()) { - throw new PackagerException( - "error.cannot-write-to-output-dir", - outdir.getAbsolutePath()); - } - - File appImageDir = APP_IMAGE_TEMP_ROOT.fetchFrom(params); - try { - appImageDir.mkdirs(); - - if (prepareAppBundle(params, true) != null && - prepareConfigFiles(params)) { - File configScript = getConfig_Script(params); - if (configScript.exists()) { - Log.verbose(MessageFormat.format( - I18N.getString("message.running-script"), - configScript.getAbsolutePath())); - IOUtils.run("bash", configScript, false); - } - - return buildDMG(params, outdir); - } - return null; - } catch (IOException ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - private static final String hdiutil = "/usr/bin/hdiutil"; - - private void prepareDMGSetupScript(String volumeName, - Map p) throws IOException { - File dmgSetup = getConfig_VolumeScript(p); - Log.verbose(MessageFormat.format( - I18N.getString("message.preparing-dmg-setup"), - dmgSetup.getAbsolutePath())); - - //prepare config for exe - Map data = new HashMap<>(); - data.put("DEPLOY_ACTUAL_VOLUME_NAME", volumeName); - data.put("DEPLOY_APPLICATION_NAME", APP_NAME.fetchFrom(p)); - - data.put("DEPLOY_INSTALL_LOCATION", "(path to desktop folder)"); - data.put("DEPLOY_INSTALL_NAME", "Desktop"); - - Writer w = new BufferedWriter(new FileWriter(dmgSetup)); - w.write(preprocessTextResource(dmgSetup.getName(), - I18N.getString("resource.dmg-setup-script"), - DEFAULT_DMG_SETUP_SCRIPT, data, VERBOSE.fetchFrom(p), - RESOURCE_DIR.fetchFrom(p))); - w.close(); - } - - private File getConfig_VolumeScript(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-dmg-setup.scpt"); - } - - private File getConfig_VolumeBackground( - Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-background.png"); - } - - private File getConfig_VolumeIcon(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-volume.icns"); - } - - private File getConfig_LicenseFile(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-license.plist"); - } - - private void prepareLicense(Map params) { - try { - String licFileStr = LICENSE_FILE.fetchFrom(params); - if (licFileStr == null) { - return; - } - - File licFile = new File(licFileStr); - byte[] licenseContentOriginal = Files.readAllBytes(licFile.toPath()); - String licenseInBase64 = - Base64.getEncoder().encodeToString(licenseContentOriginal); - - Map data = new HashMap<>(); - data.put("APPLICATION_LICENSE_TEXT", licenseInBase64); - - Writer w = new BufferedWriter( - new FileWriter(getConfig_LicenseFile(params))); - w.write(preprocessTextResource( - getConfig_LicenseFile(params).getName(), - I18N.getString("resource.license-setup"), - DEFAULT_LICENSE_PLIST, data, VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params))); - w.close(); - - } catch (IOException ex) { - Log.verbose(ex); - } - } - - private boolean prepareConfigFiles(Map params) - throws IOException { - File bgTarget = getConfig_VolumeBackground(params); - fetchResource(bgTarget.getName(), - I18N.getString("resource.dmg-background"), - DEFAULT_BACKGROUND_IMAGE, - bgTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - - File iconTarget = getConfig_VolumeIcon(params); - if (MacAppBundler.ICON_ICNS.fetchFrom(params) == null || - !MacAppBundler.ICON_ICNS.fetchFrom(params).exists()) { - fetchResource(iconTarget.getName(), - I18N.getString("resource.volume-icon"), - TEMPLATE_BUNDLE_ICON, - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } else { - fetchResource(iconTarget.getName(), - I18N.getString("resource.volume-icon"), - MacAppBundler.ICON_ICNS.fetchFrom(params), - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } - - - fetchResource(getConfig_Script(params).getName(), - I18N.getString("resource.post-install-script"), - (String) null, - getConfig_Script(params), - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - - prepareLicense(params); - - // In theory we need to extract name from results of attach command - // However, this will be a problem for customization as name will - // possibly change every time and developer will not be able to fix it - // As we are using tmp dir chance we get "different" name are low => - // Use fixed name we used for bundle - prepareDMGSetupScript(APP_NAME.fetchFrom(params), params); - - return true; - } - - // name of post-image script - private File getConfig_Script(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-post-image.sh"); - } - - // Location of SetFile utility may be different depending on MacOS version - // We look for several known places and if none of them work will - // try ot find it - private String findSetFileUtility() { - String typicalPaths[] = {"/Developer/Tools/SetFile", - "/usr/bin/SetFile", "/Developer/usr/bin/SetFile"}; - - for (String path: typicalPaths) { - File f = new File(path); - if (f.exists() && f.canExecute()) { - return path; - } - } - - // generic find attempt - try { - ProcessBuilder pb = new ProcessBuilder("xcrun", "-find", "SetFile"); - Process p = pb.start(); - InputStreamReader isr = new InputStreamReader(p.getInputStream()); - BufferedReader br = new BufferedReader(isr); - String lineRead = br.readLine(); - if (lineRead != null) { - File f = new File(lineRead); - if (f.exists() && f.canExecute()) { - return f.getAbsolutePath(); - } - } - } catch (IOException ignored) {} - - return null; - } - - private File buildDMG( - Map p, File outdir) - throws IOException { - File imagesRoot = IMAGES_ROOT.fetchFrom(p); - if (!imagesRoot.exists()) imagesRoot.mkdirs(); - - File protoDMG = new File(imagesRoot, APP_NAME.fetchFrom(p) +"-tmp.dmg"); - File finalDMG = new File(outdir, INSTALLER_NAME.fetchFrom(p) - + INSTALLER_SUFFIX.fetchFrom(p) - + ".dmg"); - - File srcFolder = APP_IMAGE_TEMP_ROOT.fetchFrom(p); - File predefinedImage = StandardBundlerParam.getPredefinedAppImage(p); - if (predefinedImage != null) { - srcFolder = predefinedImage; - } - - Log.verbose(MessageFormat.format(I18N.getString( - "message.creating-dmg-file"), finalDMG.getAbsolutePath())); - - protoDMG.delete(); - if (finalDMG.exists() && !finalDMG.delete()) { - throw new IOException(MessageFormat.format(I18N.getString( - "message.dmg-cannot-be-overwritten"), - finalDMG.getAbsolutePath())); - } - - protoDMG.getParentFile().mkdirs(); - finalDMG.getParentFile().mkdirs(); - - String hdiUtilVerbosityFlag = Log.isDebug() ? "-verbose" : "-quiet"; - - // create temp image - ProcessBuilder pb = new ProcessBuilder( - hdiutil, - "create", - hdiUtilVerbosityFlag, - "-srcfolder", srcFolder.getAbsolutePath(), - "-volname", APP_NAME.fetchFrom(p), - "-ov", protoDMG.getAbsolutePath(), - "-fs", "HFS+", - "-format", "UDRW"); - IOUtils.exec(pb, false); - - // mount temp image - pb = new ProcessBuilder( - hdiutil, - "attach", - protoDMG.getAbsolutePath(), - hdiUtilVerbosityFlag, - "-mountroot", imagesRoot.getAbsolutePath()); - IOUtils.exec(pb, false); - - File mountedRoot = - new File(imagesRoot.getAbsolutePath(), APP_NAME.fetchFrom(p)); - - // volume icon - File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns"); - IOUtils.copyFile(getConfig_VolumeIcon(p), - volumeIconFile); - - pb = new ProcessBuilder("osascript", - getConfig_VolumeScript(p).getAbsolutePath()); - IOUtils.exec(pb, false); - - // Indicate that we want a custom icon - // NB: attributes of the root directory are ignored - // when creating the volume - // Therefore we have to do this after we mount image - String setFileUtility = findSetFileUtility(); - if (setFileUtility != null) { - //can not find utility => keep going without icon - try { - volumeIconFile.setWritable(true); - // The "creator" attribute on a file is a legacy attribute - // but it seems Finder excepts these bytes to be - // "icnC" for the volume icon - // http://endrift.com/blog/2010/06/14/dmg-files-volume-icons-cli - // (might not work on Mac 10.13 with old XCode) - pb = new ProcessBuilder( - setFileUtility, - "-c", "icnC", - volumeIconFile.getAbsolutePath()); - IOUtils.exec(pb, false); - volumeIconFile.setReadOnly(); - - pb = new ProcessBuilder( - setFileUtility, - "-a", "C", - mountedRoot.getAbsolutePath()); - IOUtils.exec(pb, false); - } catch (IOException ex) { - Log.error(ex.getMessage()); - Log.verbose("Cannot enable custom icon using SetFile utility"); - } - } else { - Log.verbose( - "Skip enabling custom icon as SetFile utility is not found"); - } - - // Detach the temporary image - pb = new ProcessBuilder( - hdiutil, - "detach", - hdiUtilVerbosityFlag, - mountedRoot.getAbsolutePath()); - IOUtils.exec(pb, false); - - // Compress it to a new image - pb = new ProcessBuilder( - hdiutil, - "convert", - protoDMG.getAbsolutePath(), - hdiUtilVerbosityFlag, - "-format", "UDZO", - "-o", finalDMG.getAbsolutePath()); - IOUtils.exec(pb, false); - - //add license if needed - if (getConfig_LicenseFile(p).exists()) { - //hdiutil unflatten your_image_file.dmg - pb = new ProcessBuilder( - hdiutil, - "unflatten", - finalDMG.getAbsolutePath() - ); - IOUtils.exec(pb, false); - - //add license - pb = new ProcessBuilder( - hdiutil, - "udifrez", - finalDMG.getAbsolutePath(), - "-xml", - getConfig_LicenseFile(p).getAbsolutePath() - ); - IOUtils.exec(pb, false); - - //hdiutil flatten your_image_file.dmg - pb = new ProcessBuilder( - hdiutil, - "flatten", - finalDMG.getAbsolutePath() - ); - IOUtils.exec(pb, false); - - } - - //Delete the temporary image - protoDMG.delete(); - - Log.verbose(MessageFormat.format(I18N.getString( - "message.output-to-location"), - APP_NAME.fetchFrom(p), finalDMG.getAbsolutePath())); - - return finalDMG; - } - - - ////////////////////////////////////////////////////////////////////////// - // Implement Bundler - ////////////////////////////////////////////////////////////////////////// - - @Override - public String getName() { - return I18N.getString("dmg.bundler.name"); - } - - @Override - public String getDescription() { - return I18N.getString("dmg.bundler.description"); - } - - @Override - public String getID() { - return "dmg"; - } - - @Override - public Collection> getBundleParameters() { - Collection> results = new LinkedHashSet<>(); - results.addAll(MacAppBundler.getAppBundleParameters()); - results.addAll(getDMGBundleParameters()); - return results; - } - - public Collection> getDMGBundleParameters() { - Collection> results = new LinkedHashSet<>(); - - results.addAll(MacAppBundler.getAppBundleParameters()); - results.addAll(Arrays.asList( - INSTALLER_SUFFIX, - LICENSE_FILE - )); - - return results; - } - - - @Override - public boolean validate(Map params) - throws UnsupportedPlatformException, ConfigException { - try { - if (params == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - - //run basic validation to ensure requirements are met - //we are not interested in return code, only possible exception - validateAppImageAndBundeler(params); - - return true; - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - @Override - public File execute(Map params, - File outputParentDir) throws PackagerException { - return bundle(params, outputParentDir); - } - - @Override - public boolean supported(boolean runtimeInstaller) { - return Platform.getPlatform() == Platform.MAC; - } -} --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/DMGsetup.scpt 2019-11-18 21:44:57.631533200 -0500 +++ /dev/null 2019-11-18 21:44:58.000000000 -0500 @@ -1,15 +0,0 @@ -tell application "Finder" - tell disk "DEPLOY_ACTUAL_VOLUME_NAME" - open - set current view of container window to icon view - set toolbar visible of container window to false - set statusbar visible of container window to false - - set the bounds of container window to {400, 100, 917, 370} - - set theViewOptions to the icon view options of container window - set arrangement of theViewOptions to not arranged - set icon size of theViewOptions to 128 - end tell -end tell - --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info.plist.template 2019-11-18 21:45:06.041281500 -0500 +++ /dev/null 2019-11-18 21:45:07.000000000 -0500 @@ -1,54 +0,0 @@ - - - - - LSMinimumSystemVersion - 10.7.4 - CFBundleDevelopmentRegion - English - CFBundleAllowMixedLocalizations - - CFBundleExecutable - DEPLOY_LAUNCHER_NAME - CFBundleIconFile - DEPLOY_ICON_FILE - CFBundleIdentifier - DEPLOY_BUNDLE_IDENTIFIER - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - DEPLOY_BUNDLE_NAME - CFBundlePackageType - APPL - CFBundleShortVersionString - DEPLOY_BUNDLE_SHORT_VERSION - CFBundleSignature - ???? - - LSApplicationCategoryType - DEPLOY_BUNDLE_CATEGORY - CFBundleVersion - DEPLOY_BUNDLE_CFBUNDLE_VERSION - NSHumanReadableCopyright - DEPLOY_BUNDLE_COPYRIGHT - JavaRuntime - DEPLOY_JAVA_RUNTIME_NAME - JavaMainClassName - DEPLOY_LAUNCHER_CLASS - JavaAppClasspath - DEPLOY_APP_CLASSPATH - JavaMainJarName - DEPLOY_MAIN_JAR_NAME - JavaOptions - -DEPLOY_JAVA_OPTIONS - - ArgOptions - -DEPLOY_ARGUMENTS - DEPLOY_FILE_ASSOCIATIONS - NSHighResolutionCapable - true - - --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties 2019-11-18 21:45:15.062844600 -0500 +++ /dev/null 2019-11-18 21:45:16.000000000 -0500 @@ -1,104 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -app.bundler.name=Mac Application Image -app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers -store.bundler.name=Mac App Store Ready Bundler -store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store. -dmg.bundler.name=DMG Installer -dmg.bundler.description=Mac DMG Installer Bundle -pkg.bundler.name=PKG Installer -pkg.bundler.description=Mac PKG Installer Bundle. - -error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}]. -error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. -error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified. -error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. -error.non-existent-runtime=The file for the Runtime/JRE directory does not exist. -error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE. -error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory. -error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK. -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration. -error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true. -error.no-app-signing-key=No Mac App Store App Signing Key -error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. -error.no-pkg-signing-key=No Mac App Store Installer Signing Key -error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. -error.certificate.expired=Error: Certificate expired {0}. -error.dmg-does-not-do-daemons=DMG bundler doesn't support services. -error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false. - -resource.bundle-config-file=Bundle config file -resource.app-info-plist=Application Info.plist -resource.runtime-info-plist=Java Runtime Info.plist -resource.mac-app-store-entitlements=Mac App Store Entitlements -resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements -resource.dmg-setup-script=DMG setup script -resource.license-setup=License setup -resource.dmg-background=dmg background -resource.volume-icon=volume icon -resource.post-install-script=script to run after application image is populated -resource.pkg-preinstall-script=PKG preinstall script -resource.pkg-postinstall-script=PKG postinstall script -resource.pkg-background-image=pkg background image - - -message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. -message.null-classpath=Null app resources? -message.preparing-info-plist=Preparing Info.plist: {0}. -message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. -message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3. -message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative. -message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings. -message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots. -message.creating-association-with-null-extension=Creating association with null extension. -message.ignoring.symlink=Warning: codesign is skipping the symlink {0}. -message.keychain.error=Error: unable to get keychain list. -message.building-bundle=Building Mac App Store Bundle for {0}. -mesasge.intermediate-bundle-location=Intermediate application bundle image: {0} -message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. -message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.could-not-retrieve-name=Could not retrieve gecos name. -message.app-image-requires-app-name=When using an external app image you must specify the app name. -message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument. -message.app-image-requires-identifier=When using an external app image you must specify the identifier. -message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument. -message.building-dmg=Building DMG package for {0}. -message.running-script=Running shell script on application image [{0}]. -message.intermediate-image-location=[DEBUG] Intermediate application bundle image: {0}. -message.preparing-dmg-setup=Preparing dmg setup: {0}. -message.creating-dmg-file=Creating DMG file: {0}. -message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed. -message.output-to-location=Result DMG installer for {0}: {1}. -message.building-pkg=Building PKG package for {0}. -message.preparing-scripts=Preparing package scripts. -message.preparing-distribution-dist=Preparing distribution.dist: {0}. -message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. - --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties 2019-11-18 21:45:24.171522200 -0500 +++ /dev/null 2019-11-18 21:45:25.000000000 -0500 @@ -1,104 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -app.bundler.name=Mac Application Image -app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers -store.bundler.name=Mac App Store Ready Bundler -store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store. -dmg.bundler.name=DMG Installer -dmg.bundler.description=Mac DMG Installer Bundle -pkg.bundler.name=PKG Installer -pkg.bundler.description=Mac PKG Installer Bundle. - -error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}]. -error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. -error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified. -error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. -error.non-existent-runtime=The file for the Runtime/JRE directory does not exist. -error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE. -error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory. -error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK. -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration. -error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true. -error.no-app-signing-key=No Mac App Store App Signing Key -error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. -error.no-pkg-signing-key=No Mac App Store Installer Signing Key -error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode. -error.certificate.expired=Error: Certificate expired {0}. -error.dmg-does-not-do-daemons=DMG bundler doesn't support services. -error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false. - -resource.bundle-config-file=Bundle config file -resource.app-info-plist=Application Info.plist -resource.runtime-info-plist=Java Runtime Info.plist -resource.mac-app-store-entitlements=Mac App Store Entitlements -resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements -resource.dmg-setup-script=DMG setup script -resource.license-setup=License setup -resource.dmg-background=dmg background -resource.volume-icon=volume icon -resource.post-install-script=script to run after application image is populated -resource.pkg-preinstall-script=PKG preinstall script -resource.pkg-postinstall-script=PKG postinstall script -resource.pkg-background-image=pkg background image - - -message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. -message.null-classpath=Null app resources? -message.preparing-info-plist=Preparing Info.plist: {0}. -message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. -message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3. -message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative. -message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings. -message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots. -message.creating-association-with-null-extension=Creating association with null extension. -message.ignoring.symlink=Warning: codesign is skipping the symlink {0}. -message.keychain.error=Error: unable to get keychain list. -message.building-bundle=Building Mac App Store Bundle for {0}. -mesasge.intermediate-bundle-location=Intermediate application bundle image: {0} -message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. -message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.could-not-retrieve-name=Could not retrieve gecos name. -message.app-image-requires-app-name=When using an external app image you must specify the app name. -message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument. -message.app-image-requires-identifier=When using an external app image you must specify the identifier. -message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument. -message.building-dmg=Building DMG package for {0}. -message.running-script=Running shell script on application image [{0}]. -message.intermediate-image-location=[DEBUG] Intermediate application bundle image: {0}. -message.preparing-dmg-setup=Preparing dmg setup: {0}. -message.creating-dmg-file=Creating DMG file: {0}. -message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed. -message.output-to-location=Result DMG installer for {0}: {1}. -message.building-pkg=Building PKG package for {0}. -message.preparing-scripts=Preparing package scripts. -message.preparing-distribution-dist=Preparing distribution.dist: {0}. -message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. - --- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/launchd.plist.template 2019-11-18 21:45:32.953339100 -0500 +++ /dev/null 2019-11-18 21:45:34.000000000 -0500 @@ -1,14 +0,0 @@ - - - - - Label - DEPLOY_DAEMON_IDENTIFIER - ProgramArguments - - DEPLOY_DAEMON_LAUNCHER_PATH - - RunAtLoad - KeepAlive - - --- old/src/jdk.jpackage/macosx/classes/module-info.java.extra 2019-11-18 21:45:41.801456800 -0500 +++ /dev/null 2019-11-18 21:45:43.000000000 -0500 @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -provides jdk.jpackage.internal.Bundler with - jdk.jpackage.internal.MacAppBundler, - jdk.jpackage.internal.MacAppStoreBundler, - jdk.jpackage.internal.MacDmgBundler, - jdk.jpackage.internal.MacPkgBundler; - --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractAppImageBuilder.java 2019-11-18 21:45:50.806915100 -0500 +++ /dev/null 2019-11-18 21:45:52.000000000 -0500 @@ -1,263 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.MessageFormat; -import java.util.List; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.ArrayList; - -import jdk.jpackage.internal.resources.ResourceLocator; - -import static jdk.jpackage.internal.StandardBundlerParam.*; -import static jdk.jpackage.internal.StandardBundlerParam.ARGUMENTS; - -public abstract class AbstractAppImageBuilder { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); - - private final Map properties; - private final Path root; - protected List excludeFileList = new ArrayList<>(); - - public AbstractAppImageBuilder(Map properties, - Path root) throws IOException { - this.properties = properties; - this.root = root; - excludeFileList.add(".*\\.diz"); - } - - public InputStream getResourceAsStream(String name) { - return ResourceLocator.class.getResourceAsStream(name); - } - - public abstract void prepareApplicationFiles() throws IOException; - public abstract void prepareJreFiles() throws IOException; - public abstract Path getAppDir(); - public abstract Path getAppModsDir(); - - public Map getProperties() { - return this.properties; - } - - public Path getRoot() { - return this.root; - } - - public String getExcludeFileList() { - return String.join(",", excludeFileList); - } - - protected void copyEntry(Path appDir, File srcdir, String fname) - throws IOException { - Path dest = appDir.resolve(fname); - Files.createDirectories(dest.getParent()); - File src = new File(srcdir, fname); - if (src.isDirectory()) { - IOUtils.copyRecursive(src.toPath(), dest); - } else { - Files.copy(src.toPath(), dest); - } - } - - protected InputStream locateResource(String publicName, String category, - String defaultName, File customFile, - boolean verbose, File publicRoot) throws IOException { - InputStream is = null; - boolean customFromClasspath = false; - boolean customFromFile = false; - if (publicName != null) { - if (publicRoot != null) { - File publicResource = new File(publicRoot, publicName); - if (publicResource.exists() && publicResource.isFile()) { - is = new FileInputStream(publicResource); - } - } else { - is = getResourceAsStream(publicName); - } - customFromClasspath = (is != null); - } - if (is == null && customFile != null) { - is = new FileInputStream(customFile); - customFromFile = (is != null); - } - if (is == null && defaultName != null) { - is = getResourceAsStream(defaultName); - } - if (verbose) { - String msg = null; - if (customFromClasspath) { - msg = MessageFormat.format(I18N.getString( - "message.using-custom-resource"), - category == null ? "" : "[" + category + "] ", publicName); - } else if (customFromFile) { - msg = MessageFormat.format(I18N.getString( - "message.using-custom-resource-from-file"), - category == null ? "" : "[" + category + "] ", - customFile.getAbsoluteFile()); - } else if (is != null) { - msg = MessageFormat.format(I18N.getString( - "message.using-default-resource"), - defaultName, - category == null ? "" : "[" + category + "] ", - publicName); - } else { - msg = MessageFormat.format(I18N.getString( - "message.no-default-resource"), - defaultName == null ? "" : defaultName, - category == null ? "" : "[" + category + "] ", - publicName); - } - if (msg != null) { - Log.verbose(msg); - } - } - return is; - } - - - protected String preprocessTextResource(String publicName, String category, - String defaultName, Map pairs, - boolean verbose, File publicRoot) throws IOException { - InputStream inp = locateResource(publicName, category, - defaultName, null, verbose, publicRoot); - if (inp == null) { - throw new RuntimeException( - "Module corrupt? No "+defaultName+" resource!"); - } - - try (InputStream is = inp) { - //read fully into memory - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int length; - while ((length = is.read(buffer)) != -1) { - baos.write(buffer, 0, length); - } - - //substitute - String result = new String(baos.toByteArray()); - for (Map.Entry e : pairs.entrySet()) { - if (e.getValue() != null) { - result = result.replace(e.getKey(), e.getValue()); - } - } - return result; - } - } - - public void writeCfgFile(Map params, - File cfgFileName, String runtimeLocation) throws IOException { - cfgFileName.delete(); - - File mainJar = JLinkBundlerHelper.getMainJar(params); - ModFile.ModType mainJarType = ModFile.ModType.Unknown; - - if (mainJar != null) { - mainJarType = new ModFile(mainJar).getModType(); - } - - String mainModule = StandardBundlerParam.MODULE.fetchFrom(params); - - PrintStream out = new PrintStream(cfgFileName); - - out.println("[Application]"); - out.println("app.name=" + APP_NAME.fetchFrom(params)); - out.println("app.version=" + VERSION.fetchFrom(params)); - out.println("app.runtime=" + runtimeLocation); - out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); - out.println("app.classpath=" + String.join(File.pathSeparator, - CLASSPATH.fetchFrom(params).split("[ :;]"))); - - // The main app is required to be a jar, modular or unnamed. - if (mainModule != null && - (mainJarType == ModFile.ModType.Unknown || - mainJarType == ModFile.ModType.ModularJar)) { - out.println("app.mainmodule=" + mainModule); - } else { - String mainClass = JLinkBundlerHelper.getMainClass(params); - // If the app is contained in an unnamed jar then launch it the - // legacy way and the main class string must be - // of the format com/foo/Main - if (mainJar != null) { - out.println("app.mainjar=" - + mainJar.toPath().getFileName().toString()); - } - if (mainClass != null) { - out.println("app.mainclass=" - + mainClass.replaceAll("\\.", "/")); - } - } - - Integer port = JLinkBundlerHelper.DEBUG.fetchFrom(params); - - if (port != null) { - out.println( - "app.debug=-agentlib:jdwp=transport=dt_socket," - + "server=y,suspend=y,address=localhost:" - + port); - } - - out.println(); - out.println("[JavaOptions]"); - List jvmargs = JAVA_OPTIONS.fetchFrom(params); - for (String arg : jvmargs) { - out.println(arg); - } - Path modsDir = getAppModsDir(); - if (modsDir != null && modsDir.toFile().exists()) { - out.println("--module-path"); - out.println(getAppDir().relativize(modsDir)); - } - - out.println(); - out.println("[ArgOptions]"); - List args = ARGUMENTS.fetchFrom(params); - for (String arg : args) { - if (arg.endsWith("=") && - (arg.indexOf("=") == arg.lastIndexOf("="))) { - out.print(arg.substring(0, arg.length() - 1)); - out.println("\\="); - } else { - out.println(arg); - } - } - - - out.close(); - } - -} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractBundler.java 2019-11-18 21:45:59.751197700 -0500 +++ /dev/null 2019-11-18 21:46:01.000000000 -0500 @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.StandardCopyOption; -import java.nio.file.Files; -import java.text.MessageFormat; -import java.util.Map; -import java.util.ResourceBundle; - -import jdk.jpackage.internal.resources.ResourceLocator; - -/** - * AbstractBundler - * - * This is the base class all Bundlers extend from. - * It contains methods and parameters common to all Bundlers. - * The concrete implementations are in the platform specific Bundlers. - */ -public abstract class AbstractBundler implements Bundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); - - public static final BundlerParamInfo IMAGES_ROOT = - new StandardBundlerParam<>( - "imagesRoot", - File.class, - params -> new File( - StandardBundlerParam.TEMP_ROOT.fetchFrom(params), "images"), - (s, p) -> null); - - public InputStream getResourceAsStream(String name) { - return ResourceLocator.class.getResourceAsStream(name); - } - - protected void fetchResource(String publicName, String category, - String defaultName, File result, boolean verbose, File publicRoot) - throws IOException { - - InputStream is = streamResource(publicName, category, - defaultName, verbose, publicRoot); - if (is != null) { - try { - Files.copy(is, result.toPath(), - StandardCopyOption.REPLACE_EXISTING); - } finally { - is.close(); - } - } else { - if (verbose) { - Log.verbose(MessageFormat.format(I18N.getString( - "message.no-default-resource"), - defaultName == null ? "" : defaultName, - category == null ? "" : "[" + category + "] ", - publicName)); - } - } - } - - protected void fetchResource(String publicName, String category, - File defaultFile, File result, boolean verbose, File publicRoot) - throws IOException { - - InputStream is = streamResource(publicName, category, - null, verbose, publicRoot); - if (is != null) { - try { - Files.copy(is, result.toPath()); - } finally { - is.close(); - } - } else { - IOUtils.copyFile(defaultFile, result); - if (verbose) { - Log.verbose(MessageFormat.format(I18N.getString( - "message.using-custom-resource-from-file"), - category == null ? "" : "[" + category + "] ", - defaultFile.getAbsoluteFile())); - } - } - } - - private InputStream streamResource(String publicName, String category, - String defaultName, boolean verbose, File publicRoot) - throws IOException { - boolean custom = false; - InputStream is = null; - if (publicName != null) { - if (publicRoot != null) { - File publicResource = new File(publicRoot, publicName); - if (publicResource.exists() && publicResource.isFile()) { - is = new BufferedInputStream( - new FileInputStream(publicResource)); - } - } else { - is = getResourceAsStream(publicName); - } - custom = (is != null); - } - if (is == null && defaultName != null) { - is = getResourceAsStream(defaultName); - } - if (verbose && is != null) { - String msg = null; - if (custom) { - msg = MessageFormat.format(I18N.getString( - "message.using-custom-resource"), - category == null ? - "" : "[" + category + "] ", publicName); - } else { - msg = MessageFormat.format(I18N.getString( - "message.using-default-resource"), - defaultName == null ? "" : defaultName, - category == null ? "" : "[" + category + "] ", - publicName); - } - Log.verbose(msg); - } - return is; - } - - protected String preprocessTextResource(String publicName, String category, - String defaultName, Map pairs, - boolean verbose, File publicRoot) throws IOException { - InputStream inp = streamResource( - publicName, category, defaultName, verbose, publicRoot); - if (inp == null) { - throw new RuntimeException( - "Jar corrupt? No " + defaultName + " resource!"); - } - - // read fully into memory - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int length; - while ((length = inp.read(buffer)) != -1) { - baos.write(buffer, 0, length); - } - - // substitute - String result = new String(baos.toByteArray()); - for (Map.Entry e : pairs.entrySet()) { - if (e.getValue() != null) { - result = result.replace(e.getKey(), e.getValue()); - } - } - return result; - } - - @Override - public String toString() { - return getName(); - } - - @Override - public void cleanup(Map params) { - try { - IOUtils.deleteRecursive( - StandardBundlerParam.TEMP_ROOT.fetchFrom(params)); - } catch (IOException e) { - Log.debug(e.getMessage()); - } - } -} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractImageBundler.java 2019-11-18 21:46:08.892225300 -0500 +++ /dev/null 2019-11-18 21:46:10.000000000 -0500 @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.text.MessageFormat; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.io.File; -import java.io.IOException; - -import static jdk.jpackage.internal.StandardBundlerParam.*; - -/** - * AbstractImageBundler - * - * This is the base class for each of the Application Image Bundlers. - * - * It contains methods and parameters common to all Image Bundlers. - * - * Application Image Bundlers are created in "create-app-image" mode, - * or as an intermeadiate step in "create-installer" mode. - * - * The concrete implementations are in the platform specific Bundlers. - */ -public abstract class AbstractImageBundler extends AbstractBundler { - - private final static String JAVA_VERSION_SPEC = - "java version \"((\\d+).(\\d+).(\\d+).(\\d+))(-(.*))?(\\+[^\"]*)?\""; - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); - - public void imageBundleValidation(Map p) - throws ConfigException { - StandardBundlerParam.validateMainClassInfoFromAppResources(p); - - } - - public static void extractFlagsFromVersion( - Map params, String versionOutput) { - Pattern bitArchPattern = Pattern.compile("(\\d*)[- ]?[bB]it"); - Matcher matcher = bitArchPattern.matcher(versionOutput); - if (matcher.find()) { - params.put(".runtime.bit-arch", matcher.group(1)); - } else { - // presume 32 bit on no match - params.put(".runtime.bit-arch", "32"); - } - - Pattern oldVersionMatcher = Pattern.compile( - "java version \"((\\d+.(\\d+).\\d+)(_(\\d+)))?(-(.*))?\""); - matcher = oldVersionMatcher.matcher(versionOutput); - if (matcher.find()) { - params.put(".runtime.version", matcher.group(1)); - params.put(".runtime.version.release", matcher.group(2)); - params.put(".runtime.version.major", matcher.group(3)); - params.put(".runtime.version.update", matcher.group(5)); - params.put(".runtime.version.minor", matcher.group(5)); - params.put(".runtime.version.security", matcher.group(5)); - params.put(".runtime.version.patch", "0"); - params.put(".runtime.version.modifiers", matcher.group(7)); - } else { - Pattern newVersionMatcher = Pattern.compile(JAVA_VERSION_SPEC); - matcher = newVersionMatcher.matcher(versionOutput); - if (matcher.find()) { - params.put(".runtime.version", matcher.group(1)); - params.put(".runtime.version.release", matcher.group(1)); - params.put(".runtime.version.major", matcher.group(2)); - params.put(".runtime.version.update", matcher.group(3)); - params.put(".runtime.version.minor", matcher.group(3)); - params.put(".runtime.version.security", matcher.group(4)); - params.put(".runtime.version.patch", matcher.group(5)); - params.put(".runtime.version.modifiers", matcher.group(7)); - } else { - params.put(".runtime.version", ""); - params.put(".runtime.version.release", ""); - params.put(".runtime.version.major", ""); - params.put(".runtime.version.update", ""); - params.put(".runtime.version.minor", ""); - params.put(".runtime.version.security", ""); - params.put(".runtime.version.patch", ""); - params.put(".runtime.version.modifiers", ""); - } - } - } - - protected File createRoot(Map p, - File outputDirectory, boolean dependentTask, String name) - throws PackagerException { - if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) { - throw new RuntimeException(MessageFormat.format( - I18N.getString("error.cannot-create-output-dir"), - outputDirectory.getAbsolutePath())); - } - if (!outputDirectory.canWrite()) { - throw new RuntimeException(MessageFormat.format( - I18N.getString("error.cannot-write-to-output-dir"), - outputDirectory.getAbsolutePath())); - } - if (!dependentTask) { - Log.verbose(MessageFormat.format( - I18N.getString("message.creating-app-bundle"), - name, outputDirectory.getAbsolutePath())); - } - - // Create directory structure - File rootDirectory = new File(outputDirectory, name); - - if (rootDirectory.exists()) { - throw new PackagerException("error.root-exists", - rootDirectory.getAbsolutePath()); - } - - rootDirectory.mkdirs(); - - return rootDirectory; - } - -} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundleParams.java 2019-11-18 21:46:17.705506000 -0500 +++ /dev/null 2019-11-18 21:46:19.000000000 -0500 @@ -1,373 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -import static jdk.jpackage.internal.StandardBundlerParam.*; - -public class BundleParams { - - final protected Map params; - - // RelativeFileSet - public static final String PARAM_APP_RESOURCES = "appResources"; - - // BundlerType - public static final String PARAM_TYPE = "type"; - - // String - public static final String PARAM_BUNDLE_FORMAT = "bundleFormat"; - // String - public static final String PARAM_ICON = "icon"; - - // String - Name of bundle file and native launcher - public static final String PARAM_NAME = "name"; - - // String - application vendor, used by most of the bundlers - public static final String PARAM_VENDOR = "vendor"; - - // String - email name and email, only used for debian */ - public static final String PARAM_EMAIL = "email"; - - // String - vendor , only used for debian */ - public static final String PARAM_MAINTAINER = "maintainer"; - - /* String - Copyright. Used on Mac */ - public static final String PARAM_COPYRIGHT = "copyright"; - - // String - GUID on windows for MSI, CFBundleIdentifier on Mac - // If not compatible with requirements then bundler either do not bundle - // or autogenerate - public static final String PARAM_IDENTIFIER = "identifier"; - - /* boolean - shortcut preferences */ - public static final String PARAM_SHORTCUT = "shortcutHint"; - // boolean - menu shortcut preference - public static final String PARAM_MENU = "menuHint"; - - // String - Application version. Format may differ for different bundlers - public static final String PARAM_VERSION = "appVersion"; - - // String - Optional application description. Used by MSI and on Linux - public static final String PARAM_DESCRIPTION = "description"; - - // String - License type. Needed on Linux (rpm) - public static final String PARAM_LICENSE_TYPE = "licenseType"; - - // String - File with license. Format is OS/bundler specific - public static final String PARAM_LICENSE_FILE = "licenseFile"; - - // String Main application class. - // Not used directly but used to derive default values - public static final String PARAM_APPLICATION_CLASS = "applicationClass"; - - // boolean - Adds a dialog to let the user choose a directory - // where the product will be installed. - public static final String PARAM_INSTALLDIR_CHOOSER = "installdirChooser"; - - /** - * create a new bundle with all default values - */ - public BundleParams() { - params = new HashMap<>(); - } - - /** - * Create a bundle params with a copy of the params - * @param params map of initial parameters to be copied in. - */ - public BundleParams(Map params) { - this.params = new HashMap<>(params); - } - - public void addAllBundleParams(Map p) { - params.putAll(p); - } - - public C fetchParam(BundlerParamInfo paramInfo) { - return paramInfo.fetchFrom(params); - } - - @SuppressWarnings("unchecked") - public C fetchParamWithDefault( - Class klass, C defaultValue, String... keys) { - for (String key : keys) { - Object o = params.get(key); - if (klass.isInstance(o)) { - return (C) o; - } else if (params.containsKey(key) && o == null) { - return null; - } else if (o != null) { - Log.debug("Bundle param " + key + " is not type " + klass); - } - } - return defaultValue; - } - - public C fetchParam(Class klass, String... keys) { - return fetchParamWithDefault(klass, null, keys); - } - - // NOTE: we do not care about application parameters here - // as they will be embeded into jar file manifest and - // java launcher will take care of them! - - public Map getBundleParamsAsMap() { - return new HashMap<>(params); - } - - public void setJvmargs(List jvmargs) { - putUnlessNullOrEmpty(JAVA_OPTIONS.getID(), jvmargs); - } - - public void setArguments(List arguments) { - putUnlessNullOrEmpty(ARGUMENTS.getID(), arguments); - } - - public void setAddModules(String value) { - putUnlessNull(StandardBundlerParam.ADD_MODULES.getID(), value); - } - - public void setLimitModules(String value) { - putUnlessNull(StandardBundlerParam.LIMIT_MODULES.getID(), value); - } - - public void setModulePath(String value) { - putUnlessNull(StandardBundlerParam.MODULE_PATH.getID(), value); - } - - public void setMainModule(String value) { - putUnlessNull(StandardBundlerParam.MODULE.getID(), value); - } - - public void setDebug(String value) { - putUnlessNull(JLinkBundlerHelper.DEBUG.getID(), value); - } - - public String getApplicationID() { - return fetchParam(IDENTIFIER); - } - - public String getApplicationClass() { - return fetchParam(MAIN_CLASS); - } - - public void setApplicationClass(String applicationClass) { - putUnlessNull(PARAM_APPLICATION_CLASS, applicationClass); - } - - public String getAppVersion() { - return fetchParam(VERSION); - } - - public void setAppVersion(String version) { - putUnlessNull(PARAM_VERSION, version); - } - - public String getDescription() { - return fetchParam(DESCRIPTION); - } - - public void setDescription(String s) { - putUnlessNull(PARAM_DESCRIPTION, s); - } - - public void setInstalldirChooser(Boolean b) { - putUnlessNull(PARAM_INSTALLDIR_CHOOSER, b); - } - - public String getName() { - return fetchParam(APP_NAME); - } - - public void setName(String name) { - putUnlessNull(PARAM_NAME, name); - } - - @SuppressWarnings("deprecation") - public BundlerType getType() { - return fetchParam(BundlerType.class, PARAM_TYPE); - } - - @SuppressWarnings("deprecation") - public void setType(BundlerType type) { - putUnlessNull(PARAM_TYPE, type); - } - - public String getBundleFormat() { - return fetchParam(String.class, PARAM_BUNDLE_FORMAT); - } - - public void setBundleFormat(String t) { - putUnlessNull(PARAM_BUNDLE_FORMAT, t); - } - - public boolean getVerbose() { - return fetchParam(VERBOSE); - } - - public List getJvmargs() { - return JAVA_OPTIONS.fetchFrom(params); - } - - public List getArguments() { - return ARGUMENTS.fetchFrom(params); - } - - public jdk.jpackage.internal.RelativeFileSet getAppResource() { - return fetchParam(APP_RESOURCES); - } - - public void setAppResource(jdk.jpackage.internal.RelativeFileSet fs) { - putUnlessNull(PARAM_APP_RESOURCES, fs); - } - - public void setAppResourcesList( - List rfs) { - putUnlessNull(APP_RESOURCES_LIST.getID(), rfs); - } - - public String getMainClassName() { - String applicationClass = getApplicationClass(); - - if (applicationClass == null) { - return null; - } - - int idx = applicationClass.lastIndexOf("."); - if (idx >= 0) { - return applicationClass.substring(idx+1); - } - return applicationClass; - } - - public String getCopyright() { - return fetchParam(COPYRIGHT); - } - - public void setCopyright(String c) { - putUnlessNull(PARAM_COPYRIGHT, c); - } - - private String mainJar = null; - - // assuming that application was packaged according to the rules - // we must have application jar, i.e. jar where we embed launcher - // and have main application class listed as main class! - // If there are more than one, or none - it will be treated as - // deployment error - // - // Note we look for both JavaFX executable jars and regular executable jars - // As long as main "application" entry point is the same it is main class - // (i.e. for FX jar we will use JavaFX manifest entry ...) - public String getMainApplicationJar() { - jdk.jpackage.internal.RelativeFileSet appResources = getAppResource(); - if (mainJar != null) { - if (getApplicationClass() == null) try { - if (appResources != null) { - File srcdir = appResources.getBaseDirectory(); - JarFile jf = new JarFile(new File(srcdir, mainJar)); - Manifest m = jf.getManifest(); - Attributes attrs = (m != null) ? - m.getMainAttributes() : null; - if (attrs != null) { - setApplicationClass( - attrs.getValue(Attributes.Name.MAIN_CLASS)); - } - } - } catch (IOException ignore) { - } - return mainJar; - } - - String applicationClass = getApplicationClass(); - - if (appResources == null || applicationClass == null) { - return null; - } - File srcdir = appResources.getBaseDirectory(); - for (String fname : appResources.getIncludedFiles()) { - JarFile jf; - try { - jf = new JarFile(new File(srcdir, fname)); - Manifest m = jf.getManifest(); - Attributes attrs = (m != null) ? m.getMainAttributes() : null; - if (attrs != null) { - boolean javaMain = applicationClass.equals( - attrs.getValue(Attributes.Name.MAIN_CLASS)); - - if (javaMain) { - mainJar = fname; - return mainJar; - } - } - } catch (IOException ignore) { - } - } - return null; - } - - public String getVendor() { - return fetchParam(VENDOR); - } - - public void setVendor(String vendor) { - putUnlessNull(PARAM_VENDOR, vendor); - } - - public String getEmail() { - return fetchParam(String.class, PARAM_EMAIL); - } - - public void setEmail(String email) { - putUnlessNull(PARAM_EMAIL, email); - } - - public void putUnlessNull(String param, Object value) { - if (value != null) { - params.put(param, value); - } - } - - public void putUnlessNullOrEmpty(String param, Collection value) { - if (value != null && !value.isEmpty()) { - params.put(param, value); - } - } - - public void putUnlessNullOrEmpty(String param, Map value) { - if (value != null && !value.isEmpty()) { - params.put(param, value); - } - } - -} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerType.java 2019-11-18 21:46:26.987106900 -0500 +++ /dev/null 2019-11-18 21:46:28.000000000 -0500 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -enum BundlerType { - NONE, - IMAGE, // Generates app image only - INSTALLER // Generates installers -} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java 2019-11-18 21:46:35.973803000 -0500 +++ /dev/null 2019-11-18 21:46:37.000000000 -0500 @@ -1,312 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.*; -import java.net.URL; -import java.util.Arrays; -import java.nio.channels.FileChannel; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.List; - -/** - * IOUtils - * - * A collection of static utility methods. - */ -public class IOUtils { - - public static void deleteRecursive(File path) throws IOException { - if (!path.exists()) { - return; - } - Path directory = path.toPath(); - Files.walkFileTree(directory, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, - BasicFileAttributes attr) throws IOException { - if (Platform.getPlatform() == Platform.WINDOWS) { - Files.setAttribute(file, "dos:readonly", false); - } - Files.delete(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult preVisitDirectory(Path dir, - BasicFileAttributes attr) throws IOException { - if (Platform.getPlatform() == Platform.WINDOWS) { - Files.setAttribute(dir, "dos:readonly", false); - } - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException e) - throws IOException { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } - }); - } - - public static void copyRecursive(Path src, Path dest) throws IOException { - Files.walkFileTree(src, new SimpleFileVisitor() { - @Override - public FileVisitResult preVisitDirectory(final Path dir, - final BasicFileAttributes attrs) throws IOException { - Files.createDirectories(dest.resolve(src.relativize(dir))); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFile(final Path file, - final BasicFileAttributes attrs) throws IOException { - Files.copy(file, dest.resolve(src.relativize(file))); - return FileVisitResult.CONTINUE; - } - }); - } - - public static void copyRecursive(Path src, Path dest, - final List excludes) throws IOException { - Files.walkFileTree(src, new SimpleFileVisitor() { - @Override - public FileVisitResult preVisitDirectory(final Path dir, - final BasicFileAttributes attrs) throws IOException { - if (excludes.contains(dir.toFile().getName())) { - return FileVisitResult.SKIP_SUBTREE; - } else { - Files.createDirectories(dest.resolve(src.relativize(dir))); - return FileVisitResult.CONTINUE; - } - } - - @Override - public FileVisitResult visitFile(final Path file, - final BasicFileAttributes attrs) throws IOException { - if (!excludes.contains(file.toFile().getName())) { - Files.copy(file, dest.resolve(src.relativize(file))); - } - return FileVisitResult.CONTINUE; - } - }); - } - - public static void copyFromURL(URL location, File file) throws IOException { - copyFromURL(location, file, false); - } - - public static void copyFromURL(URL location, File file, boolean append) - throws IOException { - if (location == null) { - throw new IOException("Missing input resource!"); - } - if (file.exists() && !append) { - file.delete(); - } - InputStream in = location.openStream(); - FileOutputStream out = new FileOutputStream(file, append); - byte[] buffer = new byte[1024]; - int len; - while ((len = in.read(buffer)) != -1) { - out.write(buffer, 0, len); - } - out.close(); - in.close(); - file.setReadOnly(); - file.setReadable(true, false); - } - - public static void copyFile(File sourceFile, File destFile) - throws IOException { - destFile.getParentFile().mkdirs(); - - //recreate the file as existing copy may have weird permissions - destFile.delete(); - destFile.createNewFile(); - - FileChannel source = null; - FileChannel destination = null; - source = new FileInputStream(sourceFile).getChannel(); - destination = new FileOutputStream(destFile).getChannel(); - if (destination != null && source != null) { - destination.transferFrom(source, 0, source.size()); - } - if (source != null) { - source.close(); - } - if (destination != null) { - destination.close(); - } - - //preserve executable bit! - if (sourceFile.canExecute()) { - destFile.setExecutable(true, false); - } - if (!sourceFile.canWrite()) { - destFile.setReadOnly(); - } - destFile.setReadable(true, false); - } - - public static long getFolderSize(File folder) { - long foldersize = 0; - - File[] children = folder.listFiles(); - if (children != null) { - for (File f : children) { - if (f.isDirectory()) { - foldersize += getFolderSize(f); - } else { - foldersize += f.length(); - } - } - } - - return foldersize; - } - - // run "launcher paramfile" in the directory where paramfile is kept - public static void run(String launcher, File paramFile, boolean verbose) - throws IOException { - if (paramFile != null && paramFile.exists()) { - ProcessBuilder pb = - new ProcessBuilder(launcher, paramFile.getName()); - pb = pb.directory(paramFile.getParentFile()); - exec(pb, verbose); - } - } - - public static void exec(ProcessBuilder pb, boolean verbose) - throws IOException { - exec(pb, verbose, false); - } - - public static void exec(ProcessBuilder pb, boolean verbose, - boolean testForPresenseOnly) throws IOException { - exec(pb, verbose, testForPresenseOnly, null); - } - - public static void exec(ProcessBuilder pb, boolean verbose, - boolean testForPresenseOnly, PrintStream consumer) - throws IOException { - pb.redirectErrorStream(true); - Log.verbose("Running " - + Arrays.toString(pb.command().toArray(new String[0])) - + (pb.directory() != null ? (" in " + pb.directory()) : "")); - Process p = pb.start(); - InputStreamReader isr = new InputStreamReader(p.getInputStream()); - BufferedReader br = new BufferedReader(isr); - String lineRead; - while ((lineRead = br.readLine()) != null) { - if (consumer != null) { - consumer.print(lineRead + '\n'); - } else { - Log.verbose(lineRead); - } - } - try { - int ret = p.waitFor(); - if (ret != 0 && !(testForPresenseOnly && ret != 127)) { - throw new IOException("Exec failed with code " - + ret + " command [" - + Arrays.toString(pb.command().toArray(new String[0])) - + " in " + (pb.directory() != null ? - pb.directory().getAbsolutePath() : - "unspecified directory")); - } - } catch (InterruptedException ex) { - } - } - - @SuppressWarnings("unchecked") - private static Process startProcess(Object... args) throws IOException { - final ArrayList argsList = new ArrayList<>(); - for (Object a : args) { - if (a instanceof List) { - argsList.addAll((List)a); - } else if (a instanceof String) { - argsList.add((String)a); - } - } - - return Runtime.getRuntime().exec( - argsList.toArray(new String[argsList.size()])); - } - - private static void logErrorStream(Process p) { - final BufferedReader err = - new BufferedReader(new InputStreamReader(p.getErrorStream())); - Thread t = new Thread(() -> { - try { - String line; - while ((line = err.readLine()) != null) { - Log.error(line); - } - } catch (IOException ioe) { - Log.verbose(ioe); - } - }); - t.setDaemon(true); - t.start(); - } - - public static int getProcessOutput(List result, Object... args) - throws IOException, InterruptedException { - final Process p = startProcess(args); - - List list = new ArrayList<>(); - final BufferedReader in = - new BufferedReader(new InputStreamReader(p.getInputStream())); - Thread t = new Thread(() -> { - try { - String line; - while ((line = in.readLine()) != null) { - list.add(line); - } - } catch (IOException ioe) { - jdk.jpackage.internal.Log.verbose(ioe); - } - }); - t.setDaemon(true); - t.start(); - - logErrorStream(p); - - int ret = p.waitFor(); - - result.clear(); - result.addAll(list); - - return ret; - } -} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleManager.java 2019-11-18 21:46:44.779349900 -0500 +++ /dev/null 2019-11-18 21:46:46.000000000 -0500 @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.List; - -final class ModuleManager { - private final List folders = new ArrayList(); - - enum SearchType {UnnamedJar, ModularJar, Jmod, ExplodedModule} - - ModuleManager(String folders) { - super(); - String lfolders = folders.replaceAll("^\"|\"$", ""); - List paths = new ArrayList(); - - for (String folder : - Arrays.asList(lfolders.split(File.pathSeparator))) { - File file = new File(folder); - paths.add(file.toPath()); - } - - initialize(paths); - } - - ModuleManager(List Paths) { - super(); - initialize(Paths); - } - - private void initialize(List Paths) { - for (Path path : Paths) { - folders.add(path.toString().replaceAll("^\"|\"$", "")); - } - } - - List getModules() { - return getModules(EnumSet.of(SearchType.UnnamedJar, - SearchType.ModularJar, SearchType.Jmod, - SearchType.ExplodedModule)); - } - - List getModules(EnumSet Search) { - List result = new ArrayList(); - - for (String folder : folders) { - result.addAll(getAllModulesInDirectory(folder, Search)); - } - - return result; - } - - private static List getAllModulesInDirectory(String folder, - EnumSet Search) { - List result = new ArrayList(); - File lfolder = new File(folder); - File[] files = { lfolder }; - if (lfolder.isDirectory()) { - files = lfolder.listFiles(); - } - - if (files != null) { - for (File file : files) { - ModFile modFile = new ModFile(file); - - switch (modFile.getModType()) { - case Unknown: - break; - case UnnamedJar: - if (Search.contains(SearchType.UnnamedJar)) { - result.add(modFile); - } - break; - case ModularJar: - if (Search.contains(SearchType.ModularJar)) { - result.add(modFile); - } - break; - case Jmod: - if (Search.contains(SearchType.Jmod)) { - result.add(modFile); - } - break; - case ExplodedModule: - if (Search.contains(SearchType.ExplodedModule)) { - result.add(modFile); - } - break; - } - } - } - return result; - } -} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Param.java 2019-11-18 21:46:53.737753000 -0500 +++ /dev/null 2019-11-18 21:46:55.000000000 -0500 @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -class Param { - String name; - String value; - - void setName(String name) { - this.name = name; - } - - void setValue(String value) { - this.value = value; - } - -} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/UnsupportedPlatformException.java 2019-11-18 21:47:02.741553800 -0500 +++ /dev/null 2019-11-18 21:47:04.000000000 -0500 @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -public class UnsupportedPlatformException extends Exception { - private static final long serialVersionUID = 1L; -} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/VersionExtractor.java 2019-11-18 21:47:11.616876600 -0500 +++ /dev/null 2019-11-18 21:47:13.000000000 -0500 @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.text.MessageFormat; -import java.util.ResourceBundle; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class VersionExtractor extends PrintStream { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); - - private final String pattern; - private String version = null; - - public VersionExtractor(String pattern) { - super(new ByteArrayOutputStream()); - - this.pattern = pattern; - } - - public String getVersion() { - if (version == null) { - String content - = new String(((ByteArrayOutputStream) out).toByteArray()); - Pattern p = Pattern.compile(pattern); - Matcher matcher = p.matcher(content); - if (matcher.find()) { - version = matcher.group(1); - } - } - return version; - } - - public static boolean isLessThan(String version, String compareTo) - throws RuntimeException { - if (version == null || version.isEmpty()) { - throw new RuntimeException(MessageFormat.format( - I18N.getString("ERR_VersionComparison"), - version, compareTo)); - } - - if (compareTo == null || compareTo.isEmpty()) { - throw new RuntimeException(MessageFormat.format( - I18N.getString("ERR_VersionComparison"), - version, compareTo)); - } - - String [] versionArray = version.trim().split(Pattern.quote(".")); - String [] compareToArray = compareTo.trim().split(Pattern.quote(".")); - - for (int i = 0; i < versionArray.length; i++) { - int v1 = Integer.parseInt(versionArray[i]); - int v2 = Integer.parseInt(compareToArray[i]); - if (v1 < v2) { - return true; - } else if (v1 > v2) { - return false; - } - } - - return false; - } -} --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties 2019-11-18 21:47:20.653797700 -0500 +++ /dev/null 2019-11-18 21:47:21.000000000 -0500 @@ -1,279 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -MSG_Help=Usage: jpackage \n\ -\n\ -where mode is one of: \n\ -\ create-app-image\n\ -\ Generates a platform-specific application image.\n\ -\ create-installer\n\ -\ Generates a platform-specific installer for the application.\n\ -\ \n\ -Sample usages:\n\ ---------------\n\ -\ Generate a non-modular application image:\n\ -\ jpackage create-app-image -o outputdir -i inputdir -n name \\\n\ -\ --main-class className --main-jar MyJar.jar\n\ -\ Generate a modular application image:\n\ -\ jpackage create-app-image -o outputdir -n name \\\n\ -\ -p modulePath -m moduleName/className\n\ -\ To provide your own options to jlink, run jlink separately:\n\ -\ jlink --output appRuntimeImage -p ModulePath -m moduleName\\\n\ -\ --no-header-files [...]\n\ -\ jpackage create-app-image -o outputdir -n name\\\n\ -\ -m moduleName/className --runtime-image appRuntimeIMage\n\ -\ Generate an application installer:\n\ -\ jpackage create-installer -o outputdir -n name \\\n\ -\ -p modulePath -m moduleName/className\n\ -\ jpackage create-installer -i inputdir -o outputdir -n name \\\n\ -\ --main-class package.ClassName --main-jar MyJar.jar\n\ -\ jpackage create-installer -o outputdir -n \\\n\ -\ --app-image [--installer-type ]\n\ -\ Generate a Java runtime installer:\n\ -\ jpackage create-installer -o outputdir -n name \\\n\ -\ --runtime-image \n\ -\n\ -Generic Options:\n\ -\ @ \n\ -\ Read options and/or mode from a file \n\ -\ This option can be used multiple times.\n\ -\ --app-version \n\ -\ Version of the application and/or installer\n\ -\ --copyright \n\ -\ Copyright for the application\n\ -\ --description \n\ -\ Description of the application\n\ -\ --help -h \n\ -\ Print the usage text with a list and description of each valid\n\ -\ option for the current platform to the output stream, and exit\n\ -\ --name -n \n\ -\ Name of the application and/or installer\n\ -\ --output -o \n\ -\ Path where generated output file is placed\n\ -\ (absolute path or relative to the current directory)\n\ -\ --temp-root \n\ -\ Path of a new or empty directory used to create temporary files\n\ -\ (absolute path or relative to the current directory)\n\ -\ If specified, the temp-root will not be removed upon the task\n\ -\ completion and must be removed manually\n\ -\ If not specified, a temporary directory will be created and\n\ -\ removed upon the task completion.\n\ -\ --vendor \n\ -\ Vendor of the application\n\ -\ --verbose\n\ -\ Enables verbose output\n\ -\ --version\n\ -\ Print the product version to the output stream and exit\n\ -\n\ -\Options for creating the runtime image:\n\ -\ --add-modules [,...]\n\ -\ A comma (",") separated list of modules to add.\n\ -\ This module list, along with the main module (if specified)\n\ -\ will be passed to jlink as the --add-module argument.\n\ -\ if not specified, either just the main module (if --module is\n\ -\ specified), or the default set of modules (if --main-jar is \n\ -\ specified) are used.\n\ -\ This option can be used multiple times.\n\ -\ --module-path -p ...\n\ -\ A {0} separated list of paths\n\ -\ Each path is either a directory of modules or the path to a\n\ -\ modular jar.\n\ -\ (each path is absolute or relative to the current directory)\n\ -\ This option can be used multiple times.\n\ -\ --runtime-image \n\ -\ Path of the predefined runtime image that will be copied into\n\ -\ the application image\n\ -\ (absolute path or relative to the current directory)\n\ -\ If --runtime-image is not specified, jpackage will run jlink to\n\ -\ create the runtime image using options:\n\ -\ --strip-debug, --no-header-files, --no-man-pages, and\n\ -\ --strip-native-commands. --bind-services will also be added if\n\ -\ --add-modules is not specified.\n\ -\n\ -\Options for creating the application image:\n\ -\ --icon \n\ -\ Path of the icon of the application bundle\n\ -\ (absolute path or relative to the current directory)\n\ -\ --input -i \n\ -\ Path of the input directory that contains the files to be packaged\n\ -\ (absolute path or relative to the current directory)\n\ -\ All files in the input directory will be packaged into the\n\ -\ application image.\n\ -\n\ -\Options for creating the application launcher(s):\n\ -\ --add-launcher =\n\ -\ Name of launcher, and a path to a Properties file that contains\n\ -\ a list of key, value pairs\n\ -\ (absolute path or relative to the current directory)\n\ -\ The keys "module", "add-modules", "main-jar", "main-class",\n\ -\ "arguments", "java-options", "app-version", "icon", and\n\ -\ "win-console" can be used.\n\ -\ These options are added to, or used to overwrite, the original\n\ -\ command line options to build an additional alternative launcher.\n\ -\ The main application launcher will be built from the command line\n\ -\ options. Additional alternative launchers can be built using\n\ -\ this option, and this option can be used multiple times to\n\ -\ build multiple additional launchers. \n\ -\ --arguments
    \n\ -\ Command line arguments to pass to the main class if no command\n\ -\ line arguments are given to the launcher\n\ -\ This option can be used multiple times.\n\ -\ --java-options \n\ -\ Options to pass to the Java runtime\n\ -\ This option can be used multiple times.\n\ -\ --main-class \n\ -\ Qualified name of the application main class to execute\n\ -\ This option can only be used if --main-jar is specified.\n\ -\ --main-jar
    \n\ -\ The main JAR of the application; containing the main class\n\ -\ (specified as a path relative to the input path)\n\ -\ Either --module or --main-jar option can be specified but not\n\ -\ both.\n\ -\ --module -m [/
    ]\n\ -\ The main module (and optionally main class) of the application\n\ -\ This module must be located on the module path.\n\ -\ When this option is specified, the main module will be linked\n\ -\ in the Java runtime image. Either --module or --main-jar\n\ -\ option can be specified but not both.\n\ -{2}\n\ -\Options for creating the application installer(s):\n\ -\ --app-image \n\ -\ Location of the predefined application image that is used\n\ -\ to build an installable package\n\ -\ (absolute path or relative to the current directory)\n\ -\ See create-app-image mode options to create the application image.\n\ -\ --file-associations \n\ -\ Path to a Properties file that contains list of key, value pairs\n\ -\ (absolute path or relative to the current directory)\n\ -\ The keys "extension", "mime-type", "icon", and "description"\n\ -\ can be used to describe the association.\n\ -\ This option can be used multiple times.\n\ -\ --identifier \n\ -\ An identifier that uniquely identifies the application\n\ -\ Defaults to the main class name.\n\ -\ The value should be a valid DNS name.\n\ -\ --install-dir \n\ -\ {4}\ -\ --installer-type \n\ -\ The type of the installer to create\n\ -\ Valid values are: {1} \n\ -\ If this option is not specified (in create-installer mode) all\n\ -\ supported types of installable packages for the current\n\ -\ platform will be created.\n\ -\ --license-file \n\ -\ Path to the license file\n\ -\ (absolute path or relative to the current directory)\n\ -\ --resource-dir \n\ -\ Path to override jpackage resources\n\ -\ Icons, template files, and other resources of jpackage can be\n\ -\ over-ridden by adding replacement resources to this directory.\n\ -\ (absolute path or relative to the current directory)\n\ -\ --runtime-image \n\ -\ Path of the predefined runtime image to install\n\ -\ (absolute path or relative to the current directory)\n\ -\ Option is required when creating a runtime installer.\n\ -\n\ -\Platform dependent options for creating the application installer(s):\n\ -{3} - -MSG_Help_win_launcher=\ -\n\ -\Platform dependent option for creating the application launcher:\n\ -\ --win-console\n\ -\ Creates a console launcher for the application, should be\n\ -\ specified for application which requires console interactions\n\ - -MSG_Help_win_install=\ -\ --win-dir-chooser\n\ -\ Adds a dialog to enable the user to choose a directory in which\n\ -\ the product is installed\n\ -\ --win-menu\n\ -\ Adds the application to the system menu\n\ -\ --win-menu-group \n\ -\ Start Menu group this application is placed in\n\ -\ --win-per-user-install\n\ -\ Request to perform an install on a per-user basis\n\ -\ --win-registry-name \n\ -\ Name of the application for registry references.\n\ -\ The default is the Application Name with only\n\ -\ alphanumerics, dots, and dashes (no whitespace)\n\ -\ --win-shortcut\n\ -\ Creates a desktop shortcut for the application\n\ -\ --win-upgrade-uuid \n\ -\ UUID associated with upgrades for this package\n\ - -MSG_Help_win_install_dir=\ -\Relative sub-path under the default installation location\n\ - -MSG_Help_mac_launcher=\ -\ --mac-bundle-identifier \n\ -\ An identifier that uniquely identifies the application for MacOSX\n\ -\ Defaults to the value of --identifier option.\n\ -\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ -\ and period (.) characters.\n\ -\ --mac-bundle-name \n\ -\ Name of the application as it appears in the Menu Bar\n\ -\ This can be different from the application name.\n\ -\ This name must be less than 16 characters long and be suitable for\n\ -\ displaying in the menu bar and the application Info window.\n\ -\ Defaults to the application name.\n\ -\ --mac-bundle-signing-prefix \n\ -\ When signing the application bundle, this value is prefixed to all\n\ -\ components that need to be signed that don't have\n\ -\ an existing bundle identifier.\n\ -\ --mac-sign\n\ -\ Request that the bundle be signed\n\ -\ --mac-signing-keychain \n\ -\ Path of the keychain to use\n\ -\ (absolute path or relative to the current directory)\n\ -\ If not specified, the standard keychains are used.\n\ -\ --mac-signing-key-user-name \n\ -\ User name portion of the typical\n\ -\ "Mac Developer ID Application: " signing key\n\ - -MSG_Help_linux_install=\ -\ --linux-bundle-name \n\ -\ Name for Linux bundle, defaults to the application name\n\ -\ --linux-deb-maintainer \n\ -\ Maintainer for .deb bundle\n\ -\ --linux-menu-group \n\ -\ Menu group this application is placed in\n\ -\ --linux-package-deps\n\ -\ Required packages or capabilities for the application\n\ -\ --linux-rpm-license-type \n\ -\ Type of the license ("License: " of the RPM .spec)\n\ - -MSG_Help_mac_linux_install_dir=\ -\Absolute path of the installation directory of the application\n\ - -MSG_Help_default_install_dir=\ -\Absolute path of the installation directory of the application on OS X\n\ -\ or Linux. Relative sub-path of the installation location of the application\n\ -\ such as "Program Files" or "AppData" on Windows.\n\ - -MSG_Help_no_args=Usage: jpackage \n\ -\Use jpackage --help (or -h) for a list of possible options\ - --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties 2019-11-18 21:47:29.531475700 -0500 +++ /dev/null 2019-11-18 21:47:30.000000000 -0500 @@ -1,279 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -MSG_Help=Usage: jpackage \n\ -\n\ -where mode is one of: \n\ -\ create-app-image\n\ -\ Generates a platform-specific application image.\n\ -\ create-installer\n\ -\ Generates a platform-specific installer for the application.\n\ -\ \n\ -Sample usages:\n\ ---------------\n\ -\ Generate a non-modular application image:\n\ -\ jpackage create-app-image -o outputdir -i inputdir -n name \\\n\ -\ --main-class className --main-jar MyJar.jar\n\ -\ Generate a modular application image:\n\ -\ jpackage create-app-image -o outputdir -n name \\\n\ -\ -p modulePath -m moduleName/className\n\ -\ To provide your own options to jlink, run jlink separately:\n\ -\ jlink --output appRuntimeImage -p ModulePath -m moduleName\\\n\ -\ --no-header-files [...]\n\ -\ jpackage create-app-image -o outputdir -n name\\\n\ -\ -m moduleName/className --runtime-image appRuntimeIMage\n\ -\ Generate an application installer:\n\ -\ jpackage create-installer -o outputdir -n name \\\n\ -\ -p modulePath -m moduleName/className\n\ -\ jpackage create-installer -i inputdir -o outputdir -n name \\\n\ -\ --main-class package.ClassName --main-jar MyJar.jar\n\ -\ jpackage create-installer -o outputdir -n \\\n\ -\ --app-image [--installer-type ]\n\ -\ Generate a Java runtime installer:\n\ -\ jpackage create-installer -o outputdir -n name \\\n\ -\ --runtime-image \n\ -\n\ -Generic Options:\n\ -\ @ \n\ -\ Read options and/or mode from a file \n\ -\ This option can be used multiple times.\n\ -\ --app-version \n\ -\ Version of the application and/or installer\n\ -\ --copyright \n\ -\ Copyright for the application\n\ -\ --description \n\ -\ Description of the application\n\ -\ --help -h \n\ -\ Print the usage text with a list and description of each valid\n\ -\ option for the current platform to the output stream, and exit\n\ -\ --name -n \n\ -\ Name of the application and/or installer\n\ -\ --output -o \n\ -\ Path where generated output file is placed\n\ -\ (absolute path or relative to the current directory)\n\ -\ --temp-root \n\ -\ Path of a new or empty directory used to create temporary files\n\ -\ (absolute path or relative to the current directory)\n\ -\ If specified, the temp-root will not be removed upon the task\n\ -\ completion and must be removed manually\n\ -\ If not specified, a temporary directory will be created and\n\ -\ removed upon the task completion.\n\ -\ --vendor \n\ -\ Vendor of the application\n\ -\ --verbose\n\ -\ Enables verbose output\n\ -\ --version\n\ -\ Print the product version to the output stream and exit\n\ -\n\ -\Options for creating the runtime image:\n\ -\ --add-modules [,...]\n\ -\ A comma (",") separated list of modules to add.\n\ -\ This module list, along with the main module (if specified)\n\ -\ will be passed to jlink as the --add-module argument.\n\ -\ if not specified, either just the main module (if --module is\n\ -\ specified), or the default set of modules (if --main-jar is \n\ -\ specified) are used.\n\ -\ This option can be used multiple times.\n\ -\ --module-path -p ...\n\ -\ A {0} separated list of paths\n\ -\ Each path is either a directory of modules or the path to a\n\ -\ modular jar.\n\ -\ (each path is absolute or relative to the current directory)\n\ -\ This option can be used multiple times.\n\ -\ --runtime-image \n\ -\ Path of the predefined runtime image that will be copied into\n\ -\ the application image\n\ -\ (absolute path or relative to the current directory)\n\ -\ If --runtime-image is not specified, jpackage will run jlink to\n\ -\ create the runtime image using options:\n\ -\ --strip-debug, --no-header-files, --no-man-pages, and\n\ -\ --strip-native-commands. --bind-services will also be added if\n\ -\ --add-modules is not specified.\n\ -\n\ -\Options for creating the application image:\n\ -\ --icon \n\ -\ Path of the icon of the application bundle\n\ -\ (absolute path or relative to the current directory)\n\ -\ --input -i \n\ -\ Path of the input directory that contains the files to be packaged\n\ -\ (absolute path or relative to the current directory)\n\ -\ All files in the input directory will be packaged into the\n\ -\ application image.\n\ -\n\ -\Options for creating the application launcher(s):\n\ -\ --add-launcher =\n\ -\ Name of launcher, and a path to a Properties file that contains\n\ -\ a list of key, value pairs\n\ -\ (absolute path or relative to the current directory)\n\ -\ The keys "module", "add-modules", "main-jar", "main-class",\n\ -\ "arguments", "java-options", "app-version", "icon", and\n\ -\ "win-console" can be used.\n\ -\ These options are added to, or used to overwrite, the original\n\ -\ command line options to build an additional alternative launcher.\n\ -\ The main application launcher will be built from the command line\n\ -\ options. Additional alternative launchers can be built using\n\ -\ this option, and this option can be used multiple times to\n\ -\ build multiple additional launchers. \n\ -\ --arguments
    \n\ -\ Command line arguments to pass to the main class if no command\n\ -\ line arguments are given to the launcher\n\ -\ This option can be used multiple times.\n\ -\ --java-options \n\ -\ Options to pass to the Java runtime\n\ -\ This option can be used multiple times.\n\ -\ --main-class \n\ -\ Qualified name of the application main class to execute\n\ -\ This option can only be used if --main-jar is specified.\n\ -\ --main-jar
    \n\ -\ The main JAR of the application; containing the main class\n\ -\ (specified as a path relative to the input path)\n\ -\ Either --module or --main-jar option can be specified but not\n\ -\ both.\n\ -\ --module -m [/
    ]\n\ -\ The main module (and optionally main class) of the application\n\ -\ This module must be located on the module path.\n\ -\ When this option is specified, the main module will be linked\n\ -\ in the Java runtime image. Either --module or --main-jar\n\ -\ option can be specified but not both.\n\ -{2}\n\ -\Options for creating the application installer(s):\n\ -\ --app-image \n\ -\ Location of the predefined application image that is used\n\ -\ to build an installable package\n\ -\ (absolute path or relative to the current directory)\n\ -\ See create-app-image mode options to create the application image.\n\ -\ --file-associations \n\ -\ Path to a Properties file that contains list of key, value pairs\n\ -\ (absolute path or relative to the current directory)\n\ -\ The keys "extension", "mime-type", "icon", and "description"\n\ -\ can be used to describe the association.\n\ -\ This option can be used multiple times.\n\ -\ --identifier \n\ -\ An identifier that uniquely identifies the application\n\ -\ Defaults to the main class name.\n\ -\ The value should be a valid DNS name.\n\ -\ --install-dir \n\ -\ {4}\ -\ --installer-type \n\ -\ The type of the installer to create\n\ -\ Valid values are: {1} \n\ -\ If this option is not specified (in create-installer mode) all\n\ -\ supported types of installable packages for the current\n\ -\ platform will be created.\n\ -\ --license-file \n\ -\ Path to the license file\n\ -\ (absolute path or relative to the current directory)\n\ -\ --resource-dir \n\ -\ Path to override jpackage resources\n\ -\ Icons, template files, and other resources of jpackage can be\n\ -\ over-ridden by adding replacement resources to this directory.\n\ -\ (absolute path or relative to the current directory)\n\ -\ --runtime-image \n\ -\ Path of the predefined runtime image to install\n\ -\ (absolute path or relative to the current directory)\n\ -\ Option is required when creating a runtime installer.\n\ -\n\ -\Platform dependent options for creating the application installer(s):\n\ -{3} - -MSG_Help_win_launcher=\ -\n\ -\Platform dependent option for creating the application launcher:\n\ -\ --win-console\n\ -\ Creates a console launcher for the application, should be\n\ -\ specified for application which requires console interactions\n\ - -MSG_Help_win_install=\ -\ --win-dir-chooser\n\ -\ Adds a dialog to enable the user to choose a directory in which\n\ -\ the product is installed\n\ -\ --win-menu\n\ -\ Adds the application to the system menu\n\ -\ --win-menu-group \n\ -\ Start Menu group this application is placed in\n\ -\ --win-per-user-install\n\ -\ Request to perform an install on a per-user basis\n\ -\ --win-registry-name \n\ -\ Name of the application for registry references.\n\ -\ The default is the Application Name with only\n\ -\ alphanumerics, dots, and dashes (no whitespace)\n\ -\ --win-shortcut\n\ -\ Creates a desktop shortcut for the application\n\ -\ --win-upgrade-uuid \n\ -\ UUID associated with upgrades for this package\n\ - -MSG_Help_win_install_dir=\ -\Relative sub-path under the default installation location\n\ - -MSG_Help_mac_launcher=\ -\ --mac-bundle-identifier \n\ -\ An identifier that uniquely identifies the application for MacOSX\n\ -\ Defaults to the value of --identifier option.\n\ -\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ -\ and period (.) characters.\n\ -\ --mac-bundle-name \n\ -\ Name of the application as it appears in the Menu Bar\n\ -\ This can be different from the application name.\n\ -\ This name must be less than 16 characters long and be suitable for\n\ -\ displaying in the menu bar and the application Info window.\n\ -\ Defaults to the application name.\n\ -\ --mac-bundle-signing-prefix \n\ -\ When signing the application bundle, this value is prefixed to all\n\ -\ components that need to be signed that don't have\n\ -\ an existing bundle identifier.\n\ -\ --mac-sign\n\ -\ Request that the bundle be signed\n\ -\ --mac-signing-keychain \n\ -\ Path of the keychain to use\n\ -\ (absolute path or relative to the current directory)\n\ -\ If not specified, the standard keychains are used.\n\ -\ --mac-signing-key-user-name \n\ -\ User name portion of the typical\n\ -\ "Mac Developer ID Application: " signing key\n\ - -MSG_Help_linux_install=\ -\ --linux-bundle-name \n\ -\ Name for Linux bundle, defaults to the application name\n\ -\ --linux-deb-maintainer \n\ -\ Maintainer for .deb bundle\n\ -\ --linux-menu-group \n\ -\ Menu group this application is placed in\n\ -\ --linux-package-deps\n\ -\ Required packages or capabilities for the application\n\ -\ --linux-rpm-license-type \n\ -\ Type of the license ("License: " of the RPM .spec)\n\ - -MSG_Help_mac_linux_install_dir=\ -\Absolute path of the installation directory of the application\n\ - -MSG_Help_default_install_dir=\ -\Absolute path of the installation directory of the application on OS X\n\ -\ or Linux. Relative sub-path of the installation location of the application\n\ -\ such as "Program Files" or "AppData" on Windows.\n\ - -MSG_Help_no_args=Usage: jpackage \n\ -\Use jpackage --help (or -h) for a list of possible options\ - --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties 2019-11-18 21:47:38.686587900 -0500 +++ /dev/null 2019-11-18 21:47:40.000000000 -0500 @@ -1,97 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -param.category.default=Unknown -param.copyright.default=Copyright (C) {0,date,YYYY} -param.description.default=none -param.vendor.default=Unknown -param.version.default=1.0 - -message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize). -message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize). -message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}). -message.using-custom-resource=Using custom package resource {0} (loaded from {1}). -message.creating-app-bundle=Creating app bundle: {0} in {1}. -message.detected.modules=Automatically adding detected modules: {0}. -message.modules=Adding modules: {0} to runtime image. -message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. -message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}: {1} does not exists. -message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.debug-working-directory=Kept working directory for debug: {0}. -message.bundle-created=Succeeded in building {0} bundle. - -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.root-exists=Error: Application output directory {0} already exists. -error.no-application-class=Main application class is missing. -error.no-application-class.advice=Please specify main application class. -error.no-main-class-with-main-jar=A main class was not specified nor was one found in the jar {0}. -error.no-main-class-with-main-jar.advice=Specify a main class or ensure that the jar {0} specifies one in the manifest. -error.no-main-class=A main class was not specified nor was one found in the supplied application resources. -error.no-main-class.advice=Please specify a application class or ensure that the appResources has a jar containing one in the manifest. -error.main-jar-does-not-exist=The configured main jar does not exist {0} in the input directory. -error.main-jar-does-not-exist.advice=The main jar must be specified relative to the input directory (not an absolute path), and must exist within that directory. - -warning.module.does.not.exist=Module [{0}] does not exist. -warning.no.jdk.modules.found=Warning: No JDK Modules found. -warning.missing.arg.file=Warning: Missing argument file: {0}. - -MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle. -MSG_BundlerPlatformException=Bundler {0} skipped because the bundler does not support bundling on this platform. -MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1}. \n\ -Advice to fix: {2} -MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem: {1}. -MSG_BundlerRuntimeException=Bundler {0} failed because of {1}. -MSG_Version=jpackage version -MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle. - - - -ERR_UnsupportedOption=Error: Option [{0}] is not valid on this platform. -ERR_NotImageOption=Error: Option [{0}] is not valid in create-app-image mode. -ERR_NotInstallerOption=Error: Option [{0}] is not valid with --app-image option. -ERR_NoInstallerEntryPoint=Error: Option [{0}] is not valid without --module or --main-jar entry point option. - -ERR_MissingMode=Error: Mode is not specified. -ERR_MissingArgument=Error: Missing argument: {0}. -ERR_MissingAppResources=Error: No application jars found. -ERR_AppImageNotExist=Error: App image directory "{0}" does not exist. -ERR_AppImageInvalid=Error: App image directory "{0}" does not contain "app" sub-directory. -ERR_NoAddLauncherName=Error: --add-launcher option requires a name and a file path (--add-launcher =). -ERR_NoUniqueName=Error: --add-launcher = requires a unique name. -ERR_NoJreInstallerName=Error: Jre Installers require a name parameter. -ERR_InvalidAppName=Error: Invalid Application name: {0}. -ERR_InvalidSLName=Error: Invalid Add Launcher name: {0}. -ERR_LicenseFileNotExit=Error: Specified license file does not exist. -ERR_BuildRootInvalid=Error: temp-root ({0}) must be non-existant directory. -ERR_InvalidOption=Error: Invalid Option: [{0}]. -ERR_VersionComparison=Error: Failed to compare version {0} with {1}. -ERR_InvalidInstallerType=Error: Invalid or Unsupported Installer type: [{0}]. -ERR_BothMainJarAndModule=Error: Cannot have both --main-jar and --module Options. -ERR_NoEntryPoint=Error: create-app-image requires --main-jar or --module Option. -ERR_InputNotDirectory=Error: Input directory specified is not a directory: {0}. -ERR_CannotReadInputDir=Error: No permission to read from input directory: {0}. --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties 2019-11-18 21:47:47.644081500 -0500 +++ /dev/null 2019-11-18 21:47:49.000000000 -0500 @@ -1,97 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -param.category.default=Unknown -param.copyright.default=Copyright (C) {0,date,YYYY} -param.description.default=none -param.vendor.default=Unknown -param.version.default=1.0 - -message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize). -message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize). -message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}). -message.using-custom-resource=Using custom package resource {0} (loaded from {1}). -message.creating-app-bundle=Creating app bundle: {0} in {1}. -message.detected.modules=Automatically adding detected modules: {0}. -message.modules=Adding modules: {0} to runtime image. -message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. -message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}: {1} does not exists. -message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.debug-working-directory=Kept working directory for debug: {0}. -message.bundle-created=Succeeded in building {0} bundle. - -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.root-exists=Error: Application output directory {0} already exists. -error.no-application-class=Main application class is missing. -error.no-application-class.advice=Please specify main application class. -error.no-main-class-with-main-jar=A main class was not specified nor was one found in the jar {0}. -error.no-main-class-with-main-jar.advice=Specify a main class or ensure that the jar {0} specifies one in the manifest. -error.no-main-class=A main class was not specified nor was one found in the supplied application resources. -error.no-main-class.advice=Please specify a application class or ensure that the appResources has a jar containing one in the manifest. -error.main-jar-does-not-exist=The configured main jar does not exist {0} in the input directory. -error.main-jar-does-not-exist.advice=The main jar must be specified relative to the input directory (not an absolute path), and must exist within that directory. - -warning.module.does.not.exist=Module [{0}] does not exist. -warning.no.jdk.modules.found=Warning: No JDK Modules found. -warning.missing.arg.file=Warning: Missing argument file: {0}. - -MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle. -MSG_BundlerPlatformException=Bundler {0} skipped because the bundler does not support bundling on this platform. -MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1}. \n\ -Advice to fix: {2} -MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem: {1}. -MSG_BundlerRuntimeException=Bundler {0} failed because of {1}. -MSG_Version=jpackage version -MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle. - - - -ERR_UnsupportedOption=Error: Option [{0}] is not valid on this platform. -ERR_NotImageOption=Error: Option [{0}] is not valid in create-app-image mode. -ERR_NotInstallerOption=Error: Option [{0}] is not valid with --app-image option. -ERR_NoInstallerEntryPoint=Error: Option [{0}] is not valid without --module or --main-jar entry point option. - -ERR_MissingMode=Error: Mode is not specified. -ERR_MissingArgument=Error: Missing argument: {0}. -ERR_MissingAppResources=Error: No application jars found. -ERR_AppImageNotExist=Error: App image directory "{0}" does not exist. -ERR_AppImageInvalid=Error: App image directory "{0}" does not contain "app" sub-directory. -ERR_NoAddLauncherName=Error: --add-launcher option requires a name and a file path (--add-launcher =). -ERR_NoUniqueName=Error: --add-launcher = requires a unique name. -ERR_NoJreInstallerName=Error: Jre Installers require a name parameter. -ERR_InvalidAppName=Error: Invalid Application name: {0}. -ERR_InvalidSLName=Error: Invalid Add Launcher name: {0}. -ERR_LicenseFileNotExit=Error: Specified license file does not exist. -ERR_BuildRootInvalid=Error: temp-root ({0}) must be non-existant directory. -ERR_InvalidOption=Error: Invalid Option: [{0}]. -ERR_VersionComparison=Error: Failed to compare version {0} with {1}. -ERR_InvalidInstallerType=Error: Invalid or Unsupported Installer type: [{0}]. -ERR_BothMainJarAndModule=Error: Cannot have both --main-jar and --module Options. -ERR_NoEntryPoint=Error: create-app-image requires --main-jar or --module Option. -ERR_InputNotDirectory=Error: Input directory specified is not a directory: {0}. -ERR_CannotReadInputDir=Error: No permission to read from input directory: {0}. --- old/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties 2019-11-18 21:47:56.357326200 -0500 +++ /dev/null 2019-11-18 21:47:57.000000000 -0500 @@ -1,97 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -param.category.default=Unknown -param.copyright.default=Copyright (C) {0,date,YYYY} -param.description.default=none -param.vendor.default=Unknown -param.version.default=1.0 - -message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize). -message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize). -message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}). -message.using-custom-resource=Using custom package resource {0} (loaded from {1}). -message.creating-app-bundle=Creating app bundle: {0} in {1}. -message.detected.modules=Automatically adding detected modules: {0}. -message.modules=Adding modules: {0} to runtime image. -message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists. -message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}: {1} does not exists. -message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists. -message.debug-working-directory=Kept working directory for debug: {0}. -message.bundle-created=Succeeded in building {0} bundle. - -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.root-exists=Error: Application output directory {0} already exists. -error.no-application-class=Main application class is missing. -error.no-application-class.advice=Please specify main application class. -error.no-main-class-with-main-jar=A main class was not specified nor was one found in the jar {0}. -error.no-main-class-with-main-jar.advice=Specify a main class or ensure that the jar {0} specifies one in the manifest. -error.no-main-class=A main class was not specified nor was one found in the supplied application resources. -error.no-main-class.advice=Please specify a application class or ensure that the appResources has a jar containing one in the manifest. -error.main-jar-does-not-exist=The configured main jar does not exist {0} in the input directory. -error.main-jar-does-not-exist.advice=The main jar must be specified relative to the input directory (not an absolute path), and must exist within that directory. - -warning.module.does.not.exist=Module [{0}] does not exist. -warning.no.jdk.modules.found=Warning: No JDK Modules found. -warning.missing.arg.file=Warning: Missing argument file: {0}. - -MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle. -MSG_BundlerPlatformException=Bundler {0} skipped because the bundler does not support bundling on this platform. -MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1}. \n\ -Advice to fix: {2} -MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem: {1}. -MSG_BundlerRuntimeException=Bundler {0} failed because of {1}. -MSG_Version=jpackage version -MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle. - - - -ERR_UnsupportedOption=Error: Option [{0}] is not valid on this platform. -ERR_NotImageOption=Error: Option [{0}] is not valid in create-app-image mode. -ERR_NotInstallerOption=Error: Option [{0}] is not valid with --app-image option. -ERR_NoInstallerEntryPoint=Error: Option [{0}] is not valid without --module or --main-jar entry point option. - -ERR_MissingMode=Error: Mode is not specified. -ERR_MissingArgument=Error: Missing argument: {0}. -ERR_MissingAppResources=Error: No application jars found. -ERR_AppImageNotExist=Error: App image directory "{0}" does not exist. -ERR_AppImageInvalid=Error: App image directory "{0}" does not contain "app" sub-directory. -ERR_NoAddLauncherName=Error: --add-launcher option requires a name and a file path (--add-launcher =). -ERR_NoUniqueName=Error: --add-launcher = requires a unique name. -ERR_NoJreInstallerName=Error: Jre Installers require a name parameter. -ERR_InvalidAppName=Error: Invalid Application name: {0}. -ERR_InvalidSLName=Error: Invalid Add Launcher name: {0}. -ERR_LicenseFileNotExit=Error: Specified license file does not exist. -ERR_BuildRootInvalid=Error: temp-root ({0}) must be non-existant directory. -ERR_InvalidOption=Error: Invalid Option: [{0}]. -ERR_VersionComparison=Error: Failed to compare version {0} with {1}. -ERR_InvalidInstallerType=Error: Invalid or Unsupported Installer type: [{0}]. -ERR_BothMainJarAndModule=Error: Cannot have both --main-jar and --module Options. -ERR_NoEntryPoint=Error: create-app-image requires --main-jar or --module Option. -ERR_InputNotDirectory=Error: Input directory specified is not a directory: {0}. -ERR_CannotReadInputDir=Error: No permission to read from input directory: {0}. --- old/src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java 2019-11-18 21:48:05.444290700 -0500 +++ /dev/null 2019-11-18 21:48:07.000000000 -0500 @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.main; - -import jdk.jpackage.internal.Arguments; -import jdk.jpackage.internal.Log; -import jdk.jpackage.internal.CLIHelp; -import java.io.PrintWriter; -import java.util.ResourceBundle; - -public class Main { - - private static final ResourceBundle bundle = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); - - private static final String version = bundle.getString("MSG_Version") - + " " + System.getProperty("java.version"); - - /** - * main(String... args) - * This is the entry point for the jpackage tool. - * - * @param args command line arguments - */ - public static void main(String... args) throws Exception { - // Create logger with default system.out and system.err - Log.Logger logger = new Log.Logger(false); - Log.setLogger(logger); - - int status = run(args); - System.exit(status); - } - - /** - * run() - this is the entry point for the ToolProvider API. - * - * @param out output stream - * @param err error output stream - * @param args command line arguments - * @return an exit code. 0 means success, non-zero means an error occurred. - */ - public static int run(PrintWriter out, PrintWriter err, String... args) - throws Exception { - // Create logger with provided streams - Log.Logger logger = new Log.Logger(false); - logger.setPrintWriter(out, err); - Log.setLogger(logger); - - int status = run(args); - Log.flush(); - return status; - } - - private static int run(String... args) throws Exception { - String[] newArgs = CommandLine.parse(args); - if (newArgs.length == 0) { - CLIHelp.showHelp(true); - } else if (hasHelp(newArgs)){ - if (hasVersion(newArgs)) { - Log.info(version + "\n"); - } - CLIHelp.showHelp(false); - } else if (hasVersion(newArgs)) { - Log.info(version); - } else { - try { - Arguments arguments = new Arguments(newArgs); - if (!arguments.processArguments()) { - // processArguments() should log error message if failed. - return -1; - } - } catch (Exception e) { - if (Log.isVerbose()) { - Log.verbose(e); - } else { - Log.error(e.getMessage()); - if (e.getCause() != null && e.getCause() != e) { - Log.error(e.getCause().getMessage()); - } - } - return -1; - } - } - - return 0; - } - - private static boolean hasHelp(String[] args) { - for (String a : args) { - if ("--help".equals(a) || "-h".equals(a)) { - return true; - } - } - return false; - } - - private static boolean hasVersion(String[] args) { - for (String a : args) { - if ("--version".equals(a)) { - return true; - } - } - return false; - } - -} --- old/src/jdk.jpackage/share/classes/module-info.java 2019-11-18 21:48:14.112081100 -0500 +++ /dev/null 2019-11-18 21:48:15.000000000 -0500 @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * Defines the Java Packaging tool, jpackage. - * - *

    jpackage is a tool for generating self-contained application bundles. - * - *

    This module provides the equivalent of command-line access to jpackage - * via the {@link java.util.spi.ToolProvider ToolProvider} SPI. - * Instances of the tool can be obtained by calling - * {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst} - * or the {@link java.util.ServiceLoader service loader} with the name - * {@code "jpackage"}. - * - * @implNote The {@code jpackage} tool is not thread-safe. An application - * should not call either of the - * {@link java.util.spi.ToolProvider ToolProvider} {@code run} methods - * concurrently, even with separate {@code "jpackage"} {@code ToolProvider} - * instances, or undefined behavior may result. - *

    - * - * @moduleGraph - * @since 13 - */ - -module jdk.jpackage { - requires jdk.jlink; - - requires java.xml; - requires java.logging; - requires java.desktop; - - uses jdk.jpackage.internal.Bundler; - uses jdk.jpackage.internal.Bundlers; - - provides jdk.jpackage.internal.Bundlers with - jdk.jpackage.internal.BasicBundlers; - - provides java.util.spi.ToolProvider - with jdk.jpackage.internal.JPackageToolProvider; -} --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java 2019-11-18 21:48:23.242643900 -0500 +++ /dev/null 2019-11-18 21:48:24.000000000 -0500 @@ -1,869 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.*; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.text.MessageFormat; -import java.util.*; - -import static jdk.jpackage.internal.WindowsBundlerParam.*; - -public class WinExeBundler extends AbstractBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.WinResources"); - - public static final BundlerParamInfo APP_BUNDLER = - new WindowsBundlerParam<>( - "win.app.bundler", - WinAppBundler.class, - params -> new WinAppBundler(), - null); - - public static final BundlerParamInfo EXE_IMAGE_DIR = - new WindowsBundlerParam<>( - "win.exe.imageDir", - File.class, - params -> { - File imagesRoot = IMAGES_ROOT.fetchFrom(params); - if (!imagesRoot.exists()) imagesRoot.mkdirs(); - return new File(imagesRoot, "win-exe.image"); - }, - (s, p) -> null); - - public static final BundlerParamInfo WIN_APP_IMAGE = - new WindowsBundlerParam<>( - "win.app.image", - File.class, - null, - (s, p) -> null); - - public static final BundlerParamInfo UPGRADE_UUID = - new WindowsBundlerParam<>( - Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(), - UUID.class, - params -> UUID.randomUUID(), - (s, p) -> UUID.fromString(s)); - - public static final StandardBundlerParam EXE_SYSTEM_WIDE = - new StandardBundlerParam<>( - Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(), - Boolean.class, - params -> true, // default to system wide - (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null - : Boolean.valueOf(s) - ); - public static final StandardBundlerParam PRODUCT_VERSION = - new StandardBundlerParam<>( - "win.msi.productVersion", - String.class, - VERSION::fetchFrom, - (s, p) -> s - ); - - public static final StandardBundlerParam MENU_HINT = - new WindowsBundlerParam<>( - Arguments.CLIOptions.WIN_MENU_HINT.getId(), - Boolean.class, - params -> false, - (s, p) -> (s == null || - "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s) - ); - - public static final StandardBundlerParam SHORTCUT_HINT = - new WindowsBundlerParam<>( - Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(), - Boolean.class, - params -> false, - (s, p) -> (s == null || - "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s) - ); - - private final static String DEFAULT_EXE_PROJECT_TEMPLATE = "template.iss"; - private final static String DEFAULT_JRE_EXE_TEMPLATE = "template.jre.iss"; - private static final String TOOL_INNO_SETUP_COMPILER = "iscc.exe"; - - public static final BundlerParamInfo - TOOL_INNO_SETUP_COMPILER_EXECUTABLE = new WindowsBundlerParam<>( - "win.exe.iscc.exe", - String.class, - params -> { - for (String dirString : (System.getenv("PATH") - + ";C:\\Program Files (x86)\\Inno Setup 5;" - + "C:\\Program Files\\Inno Setup 5").split(";")) { - File f = new File(dirString.replace("\"", ""), - TOOL_INNO_SETUP_COMPILER); - if (f.isFile()) { - return f.toString(); - } - } - return null; - }, - null); - - @Override - public String getName() { - return getString("exe.bundler.name"); - } - - @Override - public String getDescription() { - return getString("exe.bundler.description"); - } - - @Override - public String getID() { - return "exe"; - } - - @Override - public String getBundleType() { - return "INSTALLER"; - } - - @Override - public Collection> getBundleParameters() { - Collection> results = new LinkedHashSet<>(); - results.addAll(WinAppBundler.getAppBundleParameters()); - results.addAll(getExeBundleParameters()); - return results; - } - - public static Collection> getExeBundleParameters() { - return Arrays.asList( - DESCRIPTION, - COPYRIGHT, - LICENSE_FILE, - MENU_GROUP, - MENU_HINT, - SHORTCUT_HINT, - EXE_SYSTEM_WIDE, - VENDOR, - INSTALLDIR_CHOOSER - ); - } - - @Override - public File execute(Map p, - File outputParentDir) throws PackagerException { - return bundle(p, outputParentDir); - } - - @Override - public boolean supported(boolean platformInstaller) { - return (Platform.getPlatform() == Platform.WINDOWS); - } - - private static String findToolVersion(String toolName) { - try { - if (toolName == null || "".equals(toolName)) return null; - - ProcessBuilder pb = new ProcessBuilder( - toolName, - "/?"); - VersionExtractor ve = - new VersionExtractor("Inno Setup (\\d+.?\\d*)"); - IOUtils.exec(pb, Log.isDebug(), true, ve); - // not interested in the output - String version = ve.getVersion(); - Log.verbose(MessageFormat.format( - getString("message.tool-version"), toolName, version)); - return version; - } catch (Exception e) { - if (Log.isDebug()) { - Log.verbose(e); - } - return null; - } - } - - @Override - public boolean validate(Map p) - throws UnsupportedPlatformException, ConfigException { - try { - if (p == null) throw new ConfigException( - getString("error.parameters-null"), - getString("error.parameters-null.advice")); - - // run basic validation to ensure requirements are met - // we are not interested in return code, only possible exception - APP_BUNDLER.fetchFrom(p).validate(p); - - // make sure some key values don't have newlines - for (BundlerParamInfo pi : Arrays.asList( - APP_NAME, - COPYRIGHT, - DESCRIPTION, - MENU_GROUP, - VENDOR, - VERSION) - ) { - String v = pi.fetchFrom(p); - if (v.contains("\n") | v.contains("\r")) { - throw new ConfigException("Parmeter '" + pi.getID() + - "' cannot contain a newline.", - " Change the value of '" + pi.getID() + - " so that it does not contain any newlines"); - } - } - - // exe bundlers trim the copyright to 100 characters, - // tell them this will happen - if (COPYRIGHT.fetchFrom(p).length() > 100) { - throw new ConfigException( - getString("error.copyright-is-too-long"), - getString("error.copyright-is-too-long.advice")); - } - - String innoVersion = findToolVersion( - TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p)); - - //Inno Setup 5+ is required - String minVersion = "5.0"; - - if (VersionExtractor.isLessThan(innoVersion, minVersion)) { - Log.error(MessageFormat.format( - getString("message.tool-wrong-version"), - TOOL_INNO_SETUP_COMPILER, innoVersion, minVersion)); - throw new ConfigException( - getString("error.iscc-not-found"), - getString("error.iscc-not-found.advice")); - } - - /********* validate bundle parameters *************/ - - // only one mime type per association, at least one file extension - List> associations = - FILE_ASSOCIATIONS.fetchFrom(p); - if (associations != null) { - for (int i = 0; i < associations.size(); i++) { - Map assoc = associations.get(i); - List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); - if (mimes.size() > 1) { - throw new ConfigException(MessageFormat.format( - getString("error.too-many-content-" - + "types-for-file-association"), i), - getString("error.too-many-content-" - + "types-for-file-association.advice")); - } - } - } - - return true; - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - private boolean prepareProto(Map p) - throws PackagerException, IOException { - File appImage = StandardBundlerParam.getPredefinedAppImage(p); - File appDir = null; - - // we either have an application image or need to build one - if (appImage != null) { - appDir = new File( - EXE_IMAGE_DIR.fetchFrom(p), APP_NAME.fetchFrom(p)); - // copy everything from appImage dir into appDir/name - IOUtils.copyRecursive(appImage.toPath(), appDir.toPath()); - } else { - appDir = APP_BUNDLER.fetchFrom(p).doBundle(p, - EXE_IMAGE_DIR.fetchFrom(p), true); - } - - if (appDir == null) { - return false; - } - - p.put(WIN_APP_IMAGE.getID(), appDir); - - String licenseFile = LICENSE_FILE.fetchFrom(p); - if (licenseFile != null) { - // need to copy license file to the working directory and convert to rtf if needed - File lfile = new File(licenseFile); - File destFile = new File(CONFIG_ROOT.fetchFrom(p), lfile.getName()); - IOUtils.copyFile(lfile, destFile); - ensureByMutationFileIsRTF(destFile); - } - - // copy file association icons - List> fileAssociations = - FILE_ASSOCIATIONS.fetchFrom(p); - - for (Map fa : fileAssociations) { - File icon = FA_ICON.fetchFrom(fa); // TODO FA_ICON_ICO - if (icon == null) { - continue; - } - - File faIconFile = new File(appDir, icon.getName()); - - if (icon.exists()) { - try { - IOUtils.copyFile(icon, faIconFile); - } catch (IOException e) { - Log.verbose(e); - } - } - } - - return true; - } - - public File bundle(Map p, File outdir) - throws PackagerException { - if (!outdir.isDirectory() && !outdir.mkdirs()) { - throw new PackagerException("error.cannot-create-output-dir", - outdir.getAbsolutePath()); - } - if (!outdir.canWrite()) { - throw new PackagerException("error.cannot-write-to-output-dir", - outdir.getAbsolutePath()); - } - - String tempDirectory = WindowsDefender.getUserTempDirectory(); - if (Arguments.CLIOptions.context().userProvidedBuildRoot) { - tempDirectory = TEMP_ROOT.fetchFrom(p).getAbsolutePath(); - } - if (WindowsDefender.isThereAPotentialWindowsDefenderIssue( - tempDirectory)) { - Log.error(MessageFormat.format( - getString("message.potential.windows.defender.issue"), - tempDirectory)); - } - - // validate we have valid tools before continuing - String iscc = TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p); - if (iscc == null || !new File(iscc).isFile()) { - Log.verbose(getString("error.iscc-not-found")); - Log.verbose(MessageFormat.format( - getString("message.iscc-file-string"), iscc)); - throw new PackagerException("error.iscc-not-found"); - } - - File imageDir = EXE_IMAGE_DIR.fetchFrom(p); - try { - imageDir.mkdirs(); - - boolean menuShortcut = MENU_HINT.fetchFrom(p); - boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(p); - if (!menuShortcut && !desktopShortcut) { - // both can not be false - user will not find the app - Log.verbose(getString("message.one-shortcut-required")); - p.put(MENU_HINT.getID(), true); - } - - if (prepareProto(p) && prepareProjectConfig(p)) { - File configScript = getConfig_Script(p); - if (configScript.exists()) { - Log.verbose(MessageFormat.format( - getString("message.running-wsh-script"), - configScript.getAbsolutePath())); - IOUtils.run("wscript", configScript, VERBOSE.fetchFrom(p)); - } - return buildEXE(p, outdir); - } - return null; - } catch (IOException ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - // name of post-image script - private File getConfig_Script(Map p) { - return new File(EXE_IMAGE_DIR.fetchFrom(p), - APP_NAME.fetchFrom(p) + "-post-image.wsf"); - } - - private String getAppIdentifier(Map p) { - String nm = UPGRADE_UUID.fetchFrom(p).toString(); - - // limitation of innosetup - if (nm.length() > 126) { - Log.error(getString("message-truncating-id")); - nm = nm.substring(0, 126); - } - - return nm; - } - - private String getLicenseFile(Map p) { - String licenseFile = LICENSE_FILE.fetchFrom(p); - if (licenseFile != null) { - File lfile = new File(licenseFile); - File destFile = new File(CONFIG_ROOT.fetchFrom(p), lfile.getName()); - String filePath = destFile.getAbsolutePath(); - if (filePath.contains(" ")) { - return "\"" + filePath + "\""; - } else { - return filePath; - } - } - - return null; - } - - void validateValueAndPut(Map data, String key, - BundlerParamInfo param, - Map p) throws IOException { - String value = param.fetchFrom(p); - if (value.contains("\r") || value.contains("\n")) { - throw new IOException("Configuration Parameter " + - param.getID() + " cannot contain multiple lines of text"); - } - data.put(key, innosetupEscape(value)); - } - - private String innosetupEscape(String value) { - if (value == null) { - return ""; - } - - if (value.contains("\"") || !value.trim().equals(value)) { - value = "\"" + value.replace("\"", "\"\"") + "\""; - } - return value; - } - - boolean prepareMainProjectFile(Map p) - throws IOException { - Map data = new HashMap<>(); - data.put("PRODUCT_APP_IDENTIFIER", - innosetupEscape(getAppIdentifier(p))); - - validateValueAndPut(data, "INSTALL_DIR", WINDOWS_INSTALL_DIR, p); - validateValueAndPut(data, "INSTALLER_NAME", APP_NAME, p); - validateValueAndPut(data, "APPLICATION_VENDOR", VENDOR, p); - validateValueAndPut(data, "APPLICATION_VERSION", VERSION, p); - validateValueAndPut(data, "INSTALLER_FILE_NAME", - INSTALLER_FILE_NAME, p); - - data.put("LAUNCHER_NAME", - innosetupEscape(WinAppBundler.getAppName(p))); - - data.put("APPLICATION_LAUNCHER_FILENAME", - innosetupEscape(WinAppBundler.getLauncherName(p))); - - data.put("APPLICATION_DESKTOP_SHORTCUT", - SHORTCUT_HINT.fetchFrom(p) ? "returnTrue" : "returnFalse"); - data.put("APPLICATION_MENU_SHORTCUT", - MENU_HINT.fetchFrom(p) ? "returnTrue" : "returnFalse"); - validateValueAndPut(data, "APPLICATION_GROUP", MENU_GROUP, p); - validateValueAndPut(data, "APPLICATION_COPYRIGHT", COPYRIGHT, p); - - data.put("APPLICATION_LICENSE_FILE", - innosetupEscape(getLicenseFile(p))); - data.put("DISABLE_DIR_PAGE", - INSTALLDIR_CHOOSER.fetchFrom(p) ? "No" : "Yes"); - - Boolean isSystemWide = EXE_SYSTEM_WIDE.fetchFrom(p); - - if (isSystemWide) { - data.put("APPLICATION_INSTALL_ROOT", "{pf}"); - data.put("APPLICATION_INSTALL_PRIVILEGE", "admin"); - } else { - data.put("APPLICATION_INSTALL_ROOT", "{localappdata}"); - data.put("APPLICATION_INSTALL_PRIVILEGE", "lowest"); - } - - data.put("ARCHITECTURE_BIT_MODE", "x64"); - - validateValueAndPut(data, "RUN_FILENAME", APP_NAME, p); - - validateValueAndPut(data, "APPLICATION_DESCRIPTION", - DESCRIPTION, p); - - data.put("APPLICATION_SERVICE", "returnFalse"); - data.put("APPLICATION_NOT_SERVICE", "returnFalse"); - data.put("APPLICATION_APP_CDS_INSTALL", "returnFalse"); - data.put("START_ON_INSTALL", ""); - data.put("STOP_ON_UNINSTALL", ""); - data.put("RUN_AT_STARTUP", ""); - - String imagePathString = - WIN_APP_IMAGE.fetchFrom(p).toPath().toAbsolutePath().toString(); - data.put("APPLICATION_IMAGE", innosetupEscape(imagePathString)); - Log.verbose("setting APPLICATION_IMAGE to " + - innosetupEscape(imagePathString) + " for InnoSetup"); - - StringBuilder addLaunchersCfg = new StringBuilder(); - for (Map - launcher : ADD_LAUNCHERS.fetchFrom(p)) { - String application_name = APP_NAME.fetchFrom(launcher); - if (MENU_HINT.fetchFrom(launcher)) { - // Name: "{group}\APPLICATION_NAME"; - // Filename: "{app}\APPLICATION_NAME.exe"; - // IconFilename: "{app}\APPLICATION_NAME.ico" - addLaunchersCfg.append("Name: \"{group}\\"); - addLaunchersCfg.append(application_name); - addLaunchersCfg.append("\"; Filename: \"{app}\\"); - addLaunchersCfg.append(application_name); - addLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\"); - addLaunchersCfg.append(application_name); - addLaunchersCfg.append(".ico\"\r\n"); - } - if (SHORTCUT_HINT.fetchFrom(launcher)) { - // Name: "{commondesktop}\APPLICATION_NAME"; - // Filename: "{app}\APPLICATION_NAME.exe"; - // IconFilename: "{app}\APPLICATION_NAME.ico" - addLaunchersCfg.append("Name: \"{commondesktop}\\"); - addLaunchersCfg.append(application_name); - addLaunchersCfg.append("\"; Filename: \"{app}\\"); - addLaunchersCfg.append(application_name); - addLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\"); - addLaunchersCfg.append(application_name); - addLaunchersCfg.append(".ico\"\r\n"); - } - } - data.put("ADD_LAUNCHERS", addLaunchersCfg.toString()); - - StringBuilder registryEntries = new StringBuilder(); - String regName = APP_REGISTRY_NAME.fetchFrom(p); - List> fetchFrom = - FILE_ASSOCIATIONS.fetchFrom(p); - for (int i = 0; i < fetchFrom.size(); i++) { - Map fileAssociation = fetchFrom.get(i); - String description = FA_DESCRIPTION.fetchFrom(fileAssociation); - File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICO - - List extensions = FA_EXTENSIONS.fetchFrom(fileAssociation); - String entryName = regName + "File"; - if (i > 0) { - entryName += "." + i; - } - - if (extensions == null) { - Log.verbose(getString( - "message.creating-association-with-null-extension")); - } else { - for (String ext : extensions) { - if (isSystemWide) { - // "Root: HKCR; Subkey: \".myp\"; - // ValueType: string; ValueName: \"\"; - // ValueData: \"MyProgramFile\"; - // Flags: uninsdeletevalue" - registryEntries.append("Root: HKCR; Subkey: \".") - .append(ext) - .append("\"; ValueType: string;" - + " ValueName: \"\"; ValueData: \"") - .append(entryName) - .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n"); - } else { - registryEntries.append( - "Root: HKCU; Subkey: \"Software\\Classes\\.") - .append(ext) - .append("\"; ValueType: string;" - + " ValueName: \"\"; ValueData: \"") - .append(entryName) - .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n"); - } - } - } - - if (extensions != null && !extensions.isEmpty()) { - String ext = extensions.get(0); - List mimeTypes = - FA_CONTENT_TYPE.fetchFrom(fileAssociation); - for (String mime : mimeTypes) { - if (isSystemWide) { - // "Root: HKCR; - // Subkey: HKCR\\Mime\\Database\\ - // Content Type\\application/chaos; - // ValueType: string; - // ValueName: Extension; - // ValueData: .chaos; - // Flags: uninsdeletevalue" - registryEntries.append("Root: HKCR; Subkey: " + - "\"Mime\\Database\\Content Type\\") - .append(mime) - .append("\"; ValueType: string; ValueName: " + - "\"Extension\"; ValueData: \".") - .append(ext) - .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n"); - } else { - registryEntries.append( - "Root: HKCU; Subkey: \"Software\\" + - "Classes\\Mime\\Database\\Content Type\\") - .append(mime) - .append("\"; ValueType: string; " + - "ValueName: \"Extension\"; ValueData: \".") - .append(ext) - .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n"); - } - } - } - - if (isSystemWide) { - // "Root: HKCR; - // Subkey: \"MyProgramFile\"; - // ValueType: string; - // ValueName: \"\"; - // ValueData: \"My Program File\"; - // Flags: uninsdeletekey" - registryEntries.append("Root: HKCR; Subkey: \"") - .append(entryName) - .append( - "\"; ValueType: string; ValueName: \"\"; ValueData: \"") - .append(removeQuotes(description)) - .append("\"; Flags: uninsdeletekey\r\n"); - } else { - registryEntries.append( - "Root: HKCU; Subkey: \"Software\\Classes\\") - .append(entryName) - .append( - "\"; ValueType: string; ValueName: \"\"; ValueData: \"") - .append(removeQuotes(description)) - .append("\"; Flags: uninsdeletekey\r\n"); - } - - if (icon != null && icon.exists()) { - if (isSystemWide) { - // "Root: HKCR; - // Subkey: \"MyProgramFile\\DefaultIcon\"; - // ValueType: string; - // ValueName: \"\"; - // ValueData: \"{app}\\MYPROG.EXE,0\"\n" + - registryEntries.append("Root: HKCR; Subkey: \"") - .append(entryName) - .append("\\DefaultIcon\"; ValueType: string; " + - "ValueName: \"\"; ValueData: \"{app}\\") - .append(icon.getName()) - .append("\"\r\n"); - } else { - registryEntries.append( - "Root: HKCU; Subkey: \"Software\\Classes\\") - .append(entryName) - .append("\\DefaultIcon\"; ValueType: string; " + - "ValueName: \"\"; ValueData: \"{app}\\") - .append(icon.getName()) - .append("\"\r\n"); - } - } - - if (isSystemWide) { - // "Root: HKCR; - // Subkey: \"MyProgramFile\\shell\\open\\command\"; - // ValueType: string; - // ValueName: \"\"; - // ValueData: \"\"\"{app}\\MYPROG.EXE\"\" \"\"%1\"\"\"\n" - registryEntries.append("Root: HKCR; Subkey: \"") - .append(entryName) - .append("\\shell\\open\\command\"; ValueType: " + - "string; ValueName: \"\"; ValueData: \"\"\"{app}\\") - .append(APP_NAME.fetchFrom(p)) - .append("\"\" \"\"%1\"\"\"\r\n"); - } else { - registryEntries.append( - "Root: HKCU; Subkey: \"Software\\Classes\\") - .append(entryName) - .append("\\shell\\open\\command\"; ValueType: " + - "string; ValueName: \"\"; ValueData: \"\"\"{app}\\") - .append(APP_NAME.fetchFrom(p)) - .append("\"\" \"\"%1\"\"\"\r\n"); - } - } - if (registryEntries.length() > 0) { - data.put("FILE_ASSOCIATIONS", - "ChangesAssociations=yes\r\n\r\n[Registry]\r\n" + - registryEntries.toString()); - } else { - data.put("FILE_ASSOCIATIONS", ""); - } - - String iss = StandardBundlerParam.isRuntimeInstaller(p) ? - DEFAULT_JRE_EXE_TEMPLATE : DEFAULT_EXE_PROJECT_TEMPLATE; - - Writer w = new BufferedWriter(new FileWriter( - getConfig_ExeProjectFile(p))); - - String content = preprocessTextResource( - getConfig_ExeProjectFile(p).getName(), - getString("resource.inno-setup-project-file"), - iss, data, VERBOSE.fetchFrom(p), - RESOURCE_DIR.fetchFrom(p)); - w.write(content); - w.close(); - return true; - } - - private final static String removeQuotes(String s) { - if (s.length() > 2 && s.startsWith("\"") && s.endsWith("\"")) { - // special case for '"XXX"' return 'XXX' not '-XXX-' - // note '"' and '""' are excluded from this special case - s = s.substring(1, s.length() - 1); - } - // if there interior double quotes replace them with '-' - return s.replaceAll("\"", "-"); - } - - private final static String DEFAULT_INNO_SETUP_ICON = - "icon_inno_setup.bmp"; - - private boolean prepareProjectConfig(Map p) - throws IOException { - prepareMainProjectFile(p); - - // prepare installer icon - File iconTarget = getConfig_SmallInnoSetupIcon(p); - fetchResource(iconTarget.getName(), - getString("resource.setup-icon"), - DEFAULT_INNO_SETUP_ICON, - iconTarget, - VERBOSE.fetchFrom(p), - RESOURCE_DIR.fetchFrom(p)); - - fetchResource(getConfig_Script(p).getName(), - getString("resource.post-install-script"), - (String) null, - getConfig_Script(p), - VERBOSE.fetchFrom(p), - RESOURCE_DIR.fetchFrom(p)); - return true; - } - - private File getConfig_SmallInnoSetupIcon( - Map p) { - return new File(EXE_IMAGE_DIR.fetchFrom(p), - APP_NAME.fetchFrom(p) + "-setup-icon.bmp"); - } - - private File getConfig_ExeProjectFile(Map p) { - return new File(EXE_IMAGE_DIR.fetchFrom(p), - APP_NAME.fetchFrom(p) + ".iss"); - } - - - private File buildEXE(Map p, File outdir) - throws IOException { - Log.verbose(MessageFormat.format( - getString("message.outputting-to-location"), - outdir.getAbsolutePath())); - - outdir.mkdirs(); - - // run Inno Setup - ProcessBuilder pb = new ProcessBuilder( - TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p), - "/q", // turn off inno setup output - "/o"+outdir.getAbsolutePath(), - getConfig_ExeProjectFile(p).getAbsolutePath()); - pb = pb.directory(EXE_IMAGE_DIR.fetchFrom(p)); - IOUtils.exec(pb, VERBOSE.fetchFrom(p)); - - Log.verbose(MessageFormat.format( - getString("message.output-location"), - outdir.getAbsolutePath())); - - // presume the result is the ".exe" file with the newest modified time - // not the best solution, but it is the most reliable - File result = null; - long lastModified = 0; - File[] list = outdir.listFiles(); - if (list != null) { - for (File f : list) { - if (f.getName().endsWith(".exe") && - f.lastModified() > lastModified) { - result = f; - lastModified = f.lastModified(); - } - } - } - - return result; - } - - public static void ensureByMutationFileIsRTF(File f) { - if (f == null || !f.isFile()) return; - - try { - boolean existingLicenseIsRTF = false; - - try (FileInputStream fin = new FileInputStream(f)) { - byte[] firstBits = new byte[7]; - - if (fin.read(firstBits) == firstBits.length) { - String header = new String(firstBits); - existingLicenseIsRTF = "{\\rtf1\\".equals(header); - } - } - - if (!existingLicenseIsRTF) { - List oldLicense = Files.readAllLines(f.toPath()); - try (Writer w = Files.newBufferedWriter( - f.toPath(), Charset.forName("Windows-1252"))) { - w.write("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033" - + "{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}}\n" - + "\\viewkind4\\uc1\\pard\\sa200\\sl276" - + "\\slmult1\\lang9\\fs20 "); - oldLicense.forEach(l -> { - try { - for (char c : l.toCharArray()) { - if (c < 0x10) { - w.write("\\'0"); - w.write(Integer.toHexString(c)); - } else if (c > 0xff) { - w.write("\\ud"); - w.write(Integer.toString(c)); - w.write("?"); - } else if ((c < 0x20) || (c >= 0x80) || - (c == 0x5C) || (c == 0x7B) || - (c == 0x7D)) { - w.write("\\'"); - w.write(Integer.toHexString(c)); - } else { - w.write(c); - } - } - if (l.length() < 1) { - w.write("\\par"); - } else { - w.write(" "); - } - w.write("\r\n"); - } catch (IOException e) { - Log.verbose(e); - } - }); - w.write("}\r\n"); - } - } - } catch (IOException e) { - Log.verbose(e); - } - } - - private static String getString(String key) - throws MissingResourceException { - return I18N.getString(key); - } -} --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java 2019-11-18 21:48:32.234961200 -0500 +++ /dev/null 2019-11-18 21:48:33.000000000 -0500 @@ -1,1204 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.*; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.text.MessageFormat; -import java.util.*; -import java.util.regex.Pattern; - -import static jdk.jpackage.internal.WindowsBundlerParam.*; - -public class WinMsiBundler extends AbstractBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.WinResources"); - - public static final BundlerParamInfo APP_BUNDLER = - new WindowsBundlerParam<>( - "win.app.bundler", - WinAppBundler.class, - params -> new WinAppBundler(), - null); - - public static final BundlerParamInfo CAN_USE_WIX36 = - new WindowsBundlerParam<>( - "win.msi.canUseWix36", - Boolean.class, - params -> false, - (s, p) -> Boolean.valueOf(s)); - - public static final BundlerParamInfo MSI_IMAGE_DIR = - new WindowsBundlerParam<>( - "win.msi.imageDir", - File.class, - params -> { - File imagesRoot = IMAGES_ROOT.fetchFrom(params); - if (!imagesRoot.exists()) imagesRoot.mkdirs(); - return new File(imagesRoot, "win-msi.image"); - }, - (s, p) -> null); - - public static final BundlerParamInfo WIN_APP_IMAGE = - new WindowsBundlerParam<>( - "win.app.image", - File.class, - null, - (s, p) -> null); - - public static final StandardBundlerParam MSI_SYSTEM_WIDE = - new StandardBundlerParam<>( - Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(), - Boolean.class, - params -> true, // MSIs default to system wide - // valueOf(null) is false, - // and we actually do want null - (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null - : Boolean.valueOf(s) - ); - - - public static final StandardBundlerParam PRODUCT_VERSION = - new StandardBundlerParam<>( - "win.msi.productVersion", - String.class, - VERSION::fetchFrom, - (s, p) -> s - ); - - public static final BundlerParamInfo UPGRADE_UUID = - new WindowsBundlerParam<>( - Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(), - UUID.class, - params -> UUID.randomUUID(), - (s, p) -> UUID.fromString(s)); - - private static final String TOOL_CANDLE = "candle.exe"; - private static final String TOOL_LIGHT = "light.exe"; - // autodetect just v3.7, v3.8, 3.9, 3.10 and 3.11 - private static final String AUTODETECT_DIRS = - ";C:\\Program Files (x86)\\WiX Toolset v3.11\\bin;" - + "C:\\Program Files\\WiX Toolset v3.11\\bin;" - + "C:\\Program Files (x86)\\WiX Toolset v3.10\\bin;" - + "C:\\Program Files\\WiX Toolset v3.10\\bin;" - + "C:\\Program Files (x86)\\WiX Toolset v3.9\\bin;" - + "C:\\Program Files\\WiX Toolset v3.9\\bin;" - + "C:\\Program Files (x86)\\WiX Toolset v3.8\\bin;" - + "C:\\Program Files\\WiX Toolset v3.8\\bin;" - + "C:\\Program Files (x86)\\WiX Toolset v3.7\\bin;" - + "C:\\Program Files\\WiX Toolset v3.7\\bin"; - - public static final BundlerParamInfo TOOL_CANDLE_EXECUTABLE = - new WindowsBundlerParam<>( - "win.msi.candle.exe", - String.class, - params -> { - for (String dirString : (System.getenv("PATH") + - AUTODETECT_DIRS).split(";")) { - File f = new File(dirString.replace("\"", ""), TOOL_CANDLE); - if (f.isFile()) { - return f.toString(); - } - } - return null; - }, - null); - - public static final BundlerParamInfo TOOL_LIGHT_EXECUTABLE = - new WindowsBundlerParam<>( - "win.msi.light.exe", - String.class, - params -> { - for (String dirString : (System.getenv("PATH") + - AUTODETECT_DIRS).split(";")) { - File f = new File(dirString.replace("\"", ""), TOOL_LIGHT); - if (f.isFile()) { - return f.toString(); - } - } - return null; - }, - null); - - public static final StandardBundlerParam MENU_HINT = - new WindowsBundlerParam<>( - Arguments.CLIOptions.WIN_MENU_HINT.getId(), - Boolean.class, - params -> false, - // valueOf(null) is false, - // and we actually do want null in some cases - (s, p) -> (s == null || - "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s) - ); - - public static final StandardBundlerParam SHORTCUT_HINT = - new WindowsBundlerParam<>( - Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(), - Boolean.class, - params -> false, - // valueOf(null) is false, - // and we actually do want null in some cases - (s, p) -> (s == null || - "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s) - ); - - @Override - public String getName() { - return I18N.getString("msi.bundler.name"); - } - - @Override - public String getDescription() { - return I18N.getString("msi.bundler.description"); - } - - @Override - public String getID() { - return "msi"; - } - - @Override - public String getBundleType() { - return "INSTALLER"; - } - - @Override - public Collection> getBundleParameters() { - Collection> results = new LinkedHashSet<>(); - results.addAll(WinAppBundler.getAppBundleParameters()); - results.addAll(getMsiBundleParameters()); - return results; - } - - public static Collection> getMsiBundleParameters() { - return Arrays.asList( - DESCRIPTION, - MENU_GROUP, - MENU_HINT, - PRODUCT_VERSION, - SHORTCUT_HINT, - MSI_SYSTEM_WIDE, - VENDOR, - LICENSE_FILE, - INSTALLDIR_CHOOSER - ); - } - - @Override - public File execute(Map params, - File outputParentDir) throws PackagerException { - return bundle(params, outputParentDir); - } - - @Override - public boolean supported(boolean platformInstaller) { - return (Platform.getPlatform() == Platform.WINDOWS); - } - - private static String findToolVersion(String toolName) { - try { - if (toolName == null || "".equals(toolName)) return null; - - ProcessBuilder pb = new ProcessBuilder( - toolName, - "/?"); - VersionExtractor ve = new VersionExtractor("version (\\d+.\\d+)"); - // not interested in the output - IOUtils.exec(pb, Log.isDebug(), true, ve); - String version = ve.getVersion(); - Log.verbose(MessageFormat.format( - I18N.getString("message.tool-version"), - toolName, version)); - return version; - } catch (Exception e) { - if (Log.isDebug()) { - Log.verbose(e); - } - return null; - } - } - - @Override - public boolean validate(Map p) - throws UnsupportedPlatformException, ConfigException { - try { - if (p == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - - // run basic validation to ensure requirements are met - // we are not interested in return code, only possible exception - APP_BUNDLER.fetchFrom(p).validate(p); - - String candleVersion = - findToolVersion(TOOL_CANDLE_EXECUTABLE.fetchFrom(p)); - String lightVersion = - findToolVersion(TOOL_LIGHT_EXECUTABLE.fetchFrom(p)); - - // WiX 3.0+ is required - String minVersion = "3.0"; - boolean bad = false; - - if (VersionExtractor.isLessThan(candleVersion, minVersion)) { - Log.verbose(MessageFormat.format( - I18N.getString("message.wrong-tool-version"), - TOOL_CANDLE, candleVersion, minVersion)); - bad = true; - } - if (VersionExtractor.isLessThan(lightVersion, minVersion)) { - Log.verbose(MessageFormat.format( - I18N.getString("message.wrong-tool-version"), - TOOL_LIGHT, lightVersion, minVersion)); - bad = true; - } - - if (bad){ - throw new ConfigException( - I18N.getString("error.no-wix-tools"), - I18N.getString("error.no-wix-tools.advice")); - } - - if (!VersionExtractor.isLessThan(lightVersion, "3.6")) { - Log.verbose(I18N.getString("message.use-wix36-features")); - p.put(CAN_USE_WIX36.getID(), Boolean.TRUE); - } - - /********* validate bundle parameters *************/ - - String version = PRODUCT_VERSION.fetchFrom(p); - if (!isVersionStringValid(version)) { - throw new ConfigException( - MessageFormat.format(I18N.getString( - "error.version-string-wrong-format"), version), - MessageFormat.format(I18N.getString( - "error.version-string-wrong-format.advice"), - PRODUCT_VERSION.getID())); - } - - // only one mime type per association, at least one file extension - List> associations = - FILE_ASSOCIATIONS.fetchFrom(p); - if (associations != null) { - for (int i = 0; i < associations.size(); i++) { - Map assoc = associations.get(i); - List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); - if (mimes.size() > 1) { - throw new ConfigException(MessageFormat.format( - I18N.getString("error.too-many-content-" - + "types-for-file-association"), i), - I18N.getString("error.too-many-content-" - + "types-for-file-association.advice")); - } - } - } - - return true; - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - // http://msdn.microsoft.com/en-us/library/aa370859%28v=VS.85%29.aspx - // The format of the string is as follows: - // major.minor.build - // The first field is the major version and has a maximum value of 255. - // The second field is the minor version and has a maximum value of 255. - // The third field is called the build version or the update version and - // has a maximum value of 65,535. - static boolean isVersionStringValid(String v) { - if (v == null) { - return true; - } - - String p[] = v.split("\\."); - if (p.length > 3) { - Log.verbose(I18N.getString( - "message.version-string-too-many-components")); - return false; - } - - try { - int val = Integer.parseInt(p[0]); - if (val < 0 || val > 255) { - Log.verbose(I18N.getString( - "error.version-string-major-out-of-range")); - return false; - } - if (p.length > 1) { - val = Integer.parseInt(p[1]); - if (val < 0 || val > 255) { - Log.verbose(I18N.getString( - "error.version-string-minor-out-of-range")); - return false; - } - } - if (p.length > 2) { - val = Integer.parseInt(p[2]); - if (val < 0 || val > 65535) { - Log.verbose(I18N.getString( - "error.version-string-build-out-of-range")); - return false; - } - } - } catch (NumberFormatException ne) { - Log.verbose(I18N.getString("error.version-string-part-not-number")); - Log.verbose(ne); - return false; - } - - return true; - } - - private boolean prepareProto(Map p) - throws PackagerException, IOException { - File appImage = StandardBundlerParam.getPredefinedAppImage(p); - File appDir = null; - - // we either have an application image or need to build one - if (appImage != null) { - appDir = new File( - MSI_IMAGE_DIR.fetchFrom(p), APP_NAME.fetchFrom(p)); - // copy everything from appImage dir into appDir/name - IOUtils.copyRecursive(appImage.toPath(), appDir.toPath()); - } else { - appDir = APP_BUNDLER.fetchFrom(p).doBundle(p, - MSI_IMAGE_DIR.fetchFrom(p), true); - } - - p.put(WIN_APP_IMAGE.getID(), appDir); - - String licenseFile = LICENSE_FILE.fetchFrom(p); - if (licenseFile != null) { - // need to copy license file to the working directory and convert to rtf if needed - File lfile = new File(licenseFile); - File destFile = new File(CONFIG_ROOT.fetchFrom(p), lfile.getName()); - IOUtils.copyFile(lfile, destFile); - ensureByMutationFileIsRTF(destFile); - } - - // copy file association icons - List> fileAssociations = - FILE_ASSOCIATIONS.fetchFrom(p); - for (Map fa : fileAssociations) { - File icon = FA_ICON.fetchFrom(fa); // TODO FA_ICON_ICO - if (icon == null) { - continue; - } - - File faIconFile = new File(appDir, icon.getName()); - - if (icon.exists()) { - try { - IOUtils.copyFile(icon, faIconFile); - } catch (IOException e) { - Log.verbose(e); - } - } - } - - return appDir != null; - } - - public File bundle(Map p, File outdir) - throws PackagerException { - if (!outdir.isDirectory() && !outdir.mkdirs()) { - throw new PackagerException("error.cannot-create-output-dir", - outdir.getAbsolutePath()); - } - if (!outdir.canWrite()) { - throw new PackagerException("error.cannot-write-to-output-dir", - outdir.getAbsolutePath()); - } - - // validate we have valid tools before continuing - String light = TOOL_LIGHT_EXECUTABLE.fetchFrom(p); - String candle = TOOL_CANDLE_EXECUTABLE.fetchFrom(p); - if (light == null || !new File(light).isFile() || - candle == null || !new File(candle).isFile()) { - Log.verbose(MessageFormat.format( - I18N.getString("message.light-file-string"), light)); - Log.verbose(MessageFormat.format( - I18N.getString("message.candle-file-string"), candle)); - throw new PackagerException("error.no-wix-tools"); - } - - File imageDir = MSI_IMAGE_DIR.fetchFrom(p); - try { - imageDir.mkdirs(); - - boolean menuShortcut = MENU_HINT.fetchFrom(p); - boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(p); - if (!menuShortcut && !desktopShortcut) { - // both can not be false - user will not find the app - Log.verbose(I18N.getString("message.one-shortcut-required")); - p.put(MENU_HINT.getID(), true); - } - - if (prepareProto(p) && prepareWiXConfig(p) - && prepareBasicProjectConfig(p)) { - File configScriptSrc = getConfig_Script(p); - if (configScriptSrc.exists()) { - // we need to be running post script in the image folder - - // NOTE: Would it be better to generate it to the image - // folder and save only if "verbose" is requested? - - // for now we replicate it - File configScript = - new File(imageDir, configScriptSrc.getName()); - IOUtils.copyFile(configScriptSrc, configScript); - Log.verbose(MessageFormat.format( - I18N.getString("message.running-wsh-script"), - configScript.getAbsolutePath())); - IOUtils.run("wscript", - configScript, false); - } - return buildMSI(p, outdir); - } - return null; - } catch (IOException ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - // name of post-image script - private File getConfig_Script(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-post-image.wsf"); - } - - private boolean prepareBasicProjectConfig( - Map params) throws IOException { - fetchResource(getConfig_Script(params).getName(), - I18N.getString("resource.post-install-script"), - (String) null, - getConfig_Script(params), - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - return true; - } - - private String relativePath(File basedir, File file) { - return file.getAbsolutePath().substring( - basedir.getAbsolutePath().length() + 1); - } - - boolean prepareMainProjectFile( - Map params) throws IOException { - Map data = new HashMap<>(); - - UUID productGUID = UUID.randomUUID(); - - Log.verbose(MessageFormat.format( - I18N.getString("message.generated-product-guid"), - productGUID.toString())); - - // we use random GUID for product itself but - // user provided for upgrade guid - // Upgrade guid is important to decide whether it is an upgrade of - // installed app. I.e. we need it to be the same for - // 2 different versions of app if possible - data.put("PRODUCT_GUID", productGUID.toString()); - data.put("PRODUCT_UPGRADE_GUID", - UPGRADE_UUID.fetchFrom(params).toString()); - data.put("UPGRADE_BLOCK", getUpgradeBlock(params)); - - data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params)); - data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params)); - data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params)); - data.put("APPLICATION_VERSION", PRODUCT_VERSION.fetchFrom(params)); - - // WinAppBundler will add application folder again => step out - File imageRootDir = WIN_APP_IMAGE.fetchFrom(params); - File launcher = new File(imageRootDir, - WinAppBundler.getLauncherName(params)); - - String launcherPath = relativePath(imageRootDir, launcher); - data.put("APPLICATION_LAUNCHER", launcherPath); - - String iconPath = launcherPath.replace(".exe", ".ico"); - - data.put("APPLICATION_ICON", iconPath); - - data.put("REGISTRY_ROOT", getRegistryRoot(params)); - - boolean canUseWix36Features = CAN_USE_WIX36.fetchFrom(params); - data.put("WIX36_ONLY_START", - canUseWix36Features ? "" : ""); - - if (MSI_SYSTEM_WIDE.fetchFrom(params)) { - data.put("INSTALL_SCOPE", "perMachine"); - } else { - data.put("INSTALL_SCOPE", "perUser"); - } - - data.put("PLATFORM", "x64"); - data.put("WIN64", "yes"); - - data.put("UI_BLOCK", getUIBlock(params)); - - // Add CA to check install dir - if (INSTALLDIR_CHOOSER.fetchFrom(params)) { - data.put("CA_BLOCK", CA_BLOCK); - data.put("INVALID_INSTALL_DIR_DLG_BLOCK", INVALID_INSTALL_DIR_DLG_BLOCK); - } else { - data.put("CA_BLOCK", ""); - data.put("INVALID_INSTALL_DIR_DLG_BLOCK", ""); - } - - List> addLaunchers = - ADD_LAUNCHERS.fetchFrom(params); - - StringBuilder addLauncherIcons = new StringBuilder(); - for (int i = 0; i < addLaunchers.size(); i++) { - Map sl = addLaunchers.get(i); - // - if (SHORTCUT_HINT.fetchFrom(sl) || MENU_HINT.fetchFrom(sl)) { - File addLauncher = new File(imageRootDir, - WinAppBundler.getLauncherName(sl)); - String addLauncherPath = - relativePath(imageRootDir, addLauncher); - String addLauncherIconPath = - addLauncherPath.replace(".exe", ".ico"); - - addLauncherIcons.append(" \r\n"); - } - } - data.put("ADD_LAUNCHER_ICONS", addLauncherIcons.toString()); - - String wxs = StandardBundlerParam.isRuntimeInstaller(params) ? - MSI_PROJECT_TEMPLATE_SERVER_JRE : MSI_PROJECT_TEMPLATE; - - Writer w = new BufferedWriter( - new FileWriter(getConfig_ProjectFile(params))); - - String content = preprocessTextResource( - getConfig_ProjectFile(params).getName(), - I18N.getString("resource.wix-config-file"), - wxs, data, VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - w.write(content); - w.close(); - return true; - } - private int id; - private int compId; - private final static String LAUNCHER_ID = "LauncherId"; - - private static final String CA_BLOCK = - "\n" + - ""; - - private static final String INVALID_INSTALL_DIR_DLG_BLOCK = - "\n" + - "\n" + - "1\n" + - "\n" + - "\n" + - "1\n" + - "\n" + - "\n" + - "" + I18N.getString("message.install.dir.exist") + "\n" + - "\n" + - ""; - - /** - * Overrides the dialog sequence in built-in dialog set "WixUI_InstallDir" - * to exclude license dialog - */ - private static final String TWEAK_FOR_EXCLUDING_LICENSE = - " 1\n" - + " 1\n"; - - private static final String CHECK_INSTALL_DLG_CTRL = - " 1\n" - + " INSTALLDIR_VALID=\"0\"\n" - + " INSTALLDIR_VALID=\"1\"\n"; - - // Required upgrade element for installers which support major upgrade (when user - // specifies --win-upgrade-uuid). We will allow downgrades. - private static final String UPGRADE_BLOCK = - ""; - - private String getUpgradeBlock(Map params) { - if (UPGRADE_UUID.getIsDefaultValue()) { - return ""; - } else { - return UPGRADE_BLOCK; - } - } - - /** - * Creates UI element using WiX built-in dialog sets - * - WixUI_InstallDir/WixUI_Minimal. - * The dialog sets are the closest to what we want to implement. - * - * WixUI_Minimal for license dialog only - * WixUI_InstallDir for installdir dialog only or for both - * installdir/license dialogs - */ - private String getUIBlock(Map params) throws IOException { - String uiBlock = ""; // UI-less element - - // Copy CA dll to include with installer - if (INSTALLDIR_CHOOSER.fetchFrom(params)) { - File helper = new File(CONFIG_ROOT.fetchFrom(params), "wixhelper.dll"); - try (InputStream is_lib = getResourceAsStream("wixhelper.dll")) { - Files.copy(is_lib, helper.toPath()); - } - } - - if (INSTALLDIR_CHOOSER.fetchFrom(params)) { - boolean enableTweakForExcludingLicense = - (getLicenseFile(params) == null); - uiBlock = " \n" - + " \n" - + (enableTweakForExcludingLicense ? - TWEAK_FOR_EXCLUDING_LICENSE : "") - + CHECK_INSTALL_DLG_CTRL; - } else if (getLicenseFile(params) != null) { - uiBlock = " \n"; - } - - return uiBlock; - } - - private void walkFileTree(Map params, - File root, PrintStream out, String prefix) { - List dirs = new ArrayList<>(); - List files = new ArrayList<>(); - - if (!root.isDirectory()) { - throw new RuntimeException( - MessageFormat.format( - I18N.getString("error.cannot-walk-directory"), - root.getAbsolutePath())); - } - - // sort to files and dirs - File[] children = root.listFiles(); - if (children != null) { - for (File f : children) { - if (f.isDirectory()) { - dirs.add(f); - } else { - files.add(f); - } - } - } - - // have files => need to output component - out.println(prefix + " "); - out.println(prefix + " "); - out.println(prefix + " "); - - boolean needRegistryKey = !MSI_SYSTEM_WIDE.fetchFrom(params); - File imageRootDir = WIN_APP_IMAGE.fetchFrom(params); - File launcherFile = - new File(imageRootDir, WinAppBundler.getLauncherName(params)); - - // Find out if we need to use registry. We need it if - // - we doing user level install as file can not serve as KeyPath - // - if we adding shortcut in this component - - for (File f: files) { - boolean isLauncher = f.equals(launcherFile); - if (isLauncher) { - needRegistryKey = true; - } - } - - if (needRegistryKey) { - // has to be under HKCU to make WiX happy - out.println(prefix + " " : " Action=\"createAndRemoveOnUninstall\">")); - out.println(prefix - + " "); - out.println(prefix + " "); - } - - boolean menuShortcut = MENU_HINT.fetchFrom(params); - boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(params); - - Map idToFileMap = new TreeMap<>(); - boolean launcherSet = false; - - for (File f : files) { - boolean isLauncher = f.equals(launcherFile); - - launcherSet = launcherSet || isLauncher; - - boolean doShortcuts = - isLauncher && (menuShortcut || desktopShortcut); - - String thisFileId = isLauncher ? LAUNCHER_ID : ("FileId" + (id++)); - idToFileMap.put(f.getName(), thisFileId); - - out.println(prefix + " "); - if (doShortcuts && desktopShortcut) { - out.println(prefix - + " "); - } - if (doShortcuts && menuShortcut) { - out.println(prefix - + " "); - } - - List> addLaunchers = - ADD_LAUNCHERS.fetchFrom(params); - for (int i = 0; i < addLaunchers.size(); i++) { - Map sl = addLaunchers.get(i); - File addLauncherFile = new File(imageRootDir, - WinAppBundler.getLauncherName(sl)); - if (f.equals(addLauncherFile)) { - if (SHORTCUT_HINT.fetchFrom(sl)) { - out.println(prefix - + " "); - } - if (MENU_HINT.fetchFrom(sl)) { - out.println(prefix - + " "); - // Should we allow different menu groups? Not for now. - } - } - } - out.println(prefix + " "); - } - - if (launcherSet) { - List> fileAssociations = - FILE_ASSOCIATIONS.fetchFrom(params); - String regName = APP_REGISTRY_NAME.fetchFrom(params); - Set defaultedMimes = new TreeSet<>(); - int count = 0; - for (Map fa : fileAssociations) { - String description = FA_DESCRIPTION.fetchFrom(fa); - List extensions = FA_EXTENSIONS.fetchFrom(fa); - List mimeTypes = FA_CONTENT_TYPE.fetchFrom(fa); - File icon = FA_ICON.fetchFrom(fa); // TODO FA_ICON_ICO - - String mime = (mimeTypes == null || - mimeTypes.isEmpty()) ? null : mimeTypes.get(0); - - if (extensions == null) { - Log.verbose(I18N.getString( - "message.creating-association-with-null-extension")); - - String entryName = regName + "File"; - if (count > 0) { - entryName += "." + count; - } - count++; - out.print(prefix + " "); - } else { - for (String ext : extensions) { - String entryName = regName + "File"; - if (count > 0) { - entryName += "." + count; - } - count++; - - out.print(prefix + " "); - - if (extensions == null) { - Log.verbose(I18N.getString( - "message.creating-association-with-null-extension")); - } else { - out.print(prefix + " "); - } else { - out.println(" ContentType='" + mime + "'>"); - if (!defaultedMimes.contains(mime)) { - out.println(prefix - + " "); - defaultedMimes.add(mime); - } - } - out.println(prefix - + " "); - out.println(prefix + " "); - } - out.println(prefix + " "); - } - } - } - } - - out.println(prefix + " "); - - for (File d : dirs) { - out.println(prefix + " "); - walkFileTree(params, d, out, prefix + " "); - out.println(prefix + " "); - } - } - - String getRegistryRoot(Map params) { - if (MSI_SYSTEM_WIDE.fetchFrom(params)) { - return "HKLM"; - } else { - return "HKCU"; - } - } - - boolean prepareContentList(Map params) - throws FileNotFoundException { - File f = new File( - CONFIG_ROOT.fetchFrom(params), MSI_PROJECT_CONTENT_FILE); - PrintStream out = new PrintStream(f); - - // opening - out.println(""); - out.println(""); - - out.println(" "); - if (MSI_SYSTEM_WIDE.fetchFrom(params)) { - // install to programfiles - out.println(" "); - } else { - // install to user folder - out.println( - " "); - } - - // We should get valid folder or subfolders - String installDir = WINDOWS_INSTALL_DIR.fetchFrom(params); - String [] installDirs = installDir.split(Pattern.quote("\\")); - for (int i = 0; i < (installDirs.length - 1); i++) { - out.println(" "); - } - - out.println(" "); - - // dynamic part - id = 0; - compId = 0; // reset counters - walkFileTree(params, WIN_APP_IMAGE.fetchFrom(params), out, " "); - - // closing - for (int i = 0; i < installDirs.length; i++) { - out.println(" "); - } - out.println(" "); - - // for shortcuts - if (SHORTCUT_HINT.fetchFrom(params)) { - out.println(" "); - } - if (MENU_HINT.fetchFrom(params)) { - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" "); - // This has to be under HKCU to make WiX happy. - // There are numberous discussions on this amoung WiX users - // (if user A installs and user B uninstalls key is left behind) - // there are suggested workarounds but none of them are appealing. - // Leave it for now - out.println( - " "); - out.println(" "); - out.println(" "); - out.println(" "); - } - - out.println(" "); - - out.println(" "); - for (int j = 0; j < compId; j++) { - out.println(" "); - } - // component is defined in the template.wsx - out.println(" "); - out.println(" "); - out.println(""); - - out.close(); - return true; - } - - private File getConfig_ProjectFile(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + ".wxs"); - } - - private String getLicenseFile(Map p) { - String licenseFile = LICENSE_FILE.fetchFrom(p); - if (licenseFile != null) { - File lfile = new File(licenseFile); - File destFile = new File(CONFIG_ROOT.fetchFrom(p), lfile.getName()); - String filePath = destFile.getAbsolutePath(); - if (filePath.contains(" ")) { - return "\"" + filePath + "\""; - } else { - return filePath; - } - } - - return null; - } - - private boolean prepareWiXConfig( - Map params) throws IOException { - return prepareMainProjectFile(params) && prepareContentList(params); - - } - private final static String MSI_PROJECT_TEMPLATE = "template.wxs"; - private final static String MSI_PROJECT_TEMPLATE_SERVER_JRE = - "template.jre.wxs"; - private final static String MSI_PROJECT_CONTENT_FILE = "bundle.wxi"; - - private File buildMSI(Map params, File outdir) - throws IOException { - File tmpDir = new File(TEMP_ROOT.fetchFrom(params), "tmp"); - File candleOut = new File( - tmpDir, APP_NAME.fetchFrom(params) +".wixobj"); - File msiOut = new File( - outdir, INSTALLER_FILE_NAME.fetchFrom(params) + ".msi"); - - Log.verbose(MessageFormat.format(I18N.getString( - "message.preparing-msi-config"), msiOut.getAbsolutePath())); - - msiOut.getParentFile().mkdirs(); - - // run candle - ProcessBuilder pb = new ProcessBuilder( - TOOL_CANDLE_EXECUTABLE.fetchFrom(params), - "-nologo", - getConfig_ProjectFile(params).getAbsolutePath(), - "-ext", "WixUtilExtension", - "-out", candleOut.getAbsolutePath()); - pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params)); - IOUtils.exec(pb, false); - - Log.verbose(MessageFormat.format(I18N.getString( - "message.generating-msi"), msiOut.getAbsolutePath())); - - boolean enableLicenseUI = (getLicenseFile(params) != null); - boolean enableInstalldirUI = INSTALLDIR_CHOOSER.fetchFrom(params); - - List commandLine = new ArrayList<>(); - - commandLine.add(TOOL_LIGHT_EXECUTABLE.fetchFrom(params)); - if (enableLicenseUI) { - commandLine.add("-dWixUILicenseRtf="+getLicenseFile(params)); - } - commandLine.add("-nologo"); - commandLine.add("-spdb"); - commandLine.add("-sice:60"); - // ignore warnings due to "missing launcguage info" (ICE60) - commandLine.add(candleOut.getAbsolutePath()); - commandLine.add("-ext"); - commandLine.add("WixUtilExtension"); - if (enableLicenseUI || enableInstalldirUI) { - commandLine.add("-ext"); - commandLine.add("WixUIExtension.dll"); - } - - // Only needed if we using CA dll, so Wix can find it - if (enableInstalldirUI) { - commandLine.add("-b"); - commandLine.add(CONFIG_ROOT.fetchFrom(params).getAbsolutePath()); - } - - commandLine.add("-out"); - commandLine.add(msiOut.getAbsolutePath()); - - // create .msi - pb = new ProcessBuilder(commandLine); - - pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params)); - IOUtils.exec(pb, false); - - candleOut.delete(); - IOUtils.deleteRecursive(tmpDir); - - return msiOut; - } - - public static void ensureByMutationFileIsRTF(File f) { - if (f == null || !f.isFile()) return; - - try { - boolean existingLicenseIsRTF = false; - - try (FileInputStream fin = new FileInputStream(f)) { - byte[] firstBits = new byte[7]; - - if (fin.read(firstBits) == firstBits.length) { - String header = new String(firstBits); - existingLicenseIsRTF = "{\\rtf1\\".equals(header); - } - } - - if (!existingLicenseIsRTF) { - List oldLicense = Files.readAllLines(f.toPath()); - try (Writer w = Files.newBufferedWriter( - f.toPath(), Charset.forName("Windows-1252"))) { - w.write("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033" - + "{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}}\n" - + "\\viewkind4\\uc1\\pard\\sa200\\sl276" - + "\\slmult1\\lang9\\fs20 "); - oldLicense.forEach(l -> { - try { - for (char c : l.toCharArray()) { - // 0x00 <= ch < 0x20 Escaped (\'hh) - // 0x20 <= ch < 0x80 Raw(non - escaped) char - // 0x80 <= ch <= 0xFF Escaped(\ 'hh) - // 0x5C, 0x7B, 0x7D (special RTF characters - // \,{,})Escaped(\'hh) - // ch > 0xff Escaped (\\ud###?) - if (c < 0x10) { - w.write("\\'0"); - w.write(Integer.toHexString(c)); - } else if (c > 0xff) { - w.write("\\ud"); - w.write(Integer.toString(c)); - // \\uc1 is in the header and in effect - // so we trail with a replacement char if - // the font lacks that character - '?' - w.write("?"); - } else if ((c < 0x20) || (c >= 0x80) || - (c == 0x5C) || (c == 0x7B) || - (c == 0x7D)) { - w.write("\\'"); - w.write(Integer.toHexString(c)); - } else { - w.write(c); - } - } - // blank lines are interpreted as paragraph breaks - if (l.length() < 1) { - w.write("\\par"); - } else { - w.write(" "); - } - w.write("\r\n"); - } catch (IOException e) { - Log.verbose(e); - } - }); - w.write("}\r\n"); - } - } - } catch (IOException e) { - Log.verbose(e); - } - - } -} --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties 2019-11-18 21:48:41.469046800 -0500 +++ /dev/null 2019-11-18 21:48:42.000000000 -0500 @@ -1,90 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -app.bundler.name=Windows Application Image -app.bundler.description=A Directory based image of a windows Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers -exe.bundler.name=EXE Installer -exe.bundler.description=Microsoft Windows EXE Installer, via InnoIDE. -msi.bundler.name=MSI Installer -msi.bundler.description=Microsoft Windows MSI Installer, via WiX. - -param.menu-group.default=Unknown - -resource.application-icon=application icon -resource.executable-properties-template=Template for creating executable properties file. -resource.inno-setup-project-file=Inno Setup project file -resource.setup-icon=setup dialog icon -resource.post-install-script=script to run after application image is populated -resource.wix-config-file=WiX config file - -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.no-windows-resources=This copy of the JDK does not support Windows. -error.no-windows-resources.advice=Please use the Oracle JDK for Windows. -error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe). -error.iscc-not-found.advice=Download Inno Setup 5 or later from http://www.jrsoftware.org and add it to the PATH. -error.copyright-is-too-long=The copyright string is too long for InnoSetup. -error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters. -error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. -error.too-many-content-types-for-file-association.advice=Specify one and only one MIME type for each file association. -error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe). -error.no-wix-tools.advice=Download WiX 3.0 or later from http://wix.sf.net and add it to the PATH. -error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}]. -error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: http://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx . -error.version-string-major-out-of-range=Major version must be in the range [0, 255]. -error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535]. -error.version-string-minor-out-of-range=Minor version must be in the range [0, 255]. -error.version-string-part-not-number=Failed to convert version component to int. -error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory. -error.version-swap=Failed to update version information for {0}. - -message.result-dir=Result application bundle: {0}. -message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. -message.multiple-launchers=Multiple launchers found in predefined app-image. {0} will be used as primary launcher. -message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". -message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required. -message.outputting-to-location=Generating EXE for installer to: {0}. -message.output-location=Installer (.exe) saved to: {0} -message.tool-version=Detected [{0}] version [{1}]. -message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. -message.running-wsh-script=Running WSH script on application image [{0}]. -message.iscc-file-string=InnoSetup compiler set to {0}. -message.creating-association-with-null-extension=Creating association with null extension. -message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. -message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . -message.truncating.id=Truncating Application ID to 126 chars for Inno Setup. -message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action. -message.generated-product-guid=Generated product GUID: {0}. -message.preparing-msi-config=Preparing MSI config: {0}. -message.generating-msi=Generating MSI: {0}. -message.light-file-string=WiX light tool set to {0}. -message.candle-file-string=WiX candle tool set to {0}. -message.install.dir.exist=The folder [APPLICATIONFOLDER] already exist. Whould you like to install to that folder anyway? -message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}". - --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties 2019-11-18 21:48:50.013757500 -0500 +++ /dev/null 2019-11-18 21:48:51.000000000 -0500 @@ -1,90 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -app.bundler.name=Windows Application Image -app.bundler.description=A Directory based image of a windows Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers -exe.bundler.name=EXE Installer -exe.bundler.description=Microsoft Windows EXE Installer, via InnoIDE. -msi.bundler.name=MSI Installer -msi.bundler.description=Microsoft Windows MSI Installer, via WiX. - -param.menu-group.default=Unknown - -resource.application-icon=application icon -resource.executable-properties-template=Template for creating executable properties file. -resource.inno-setup-project-file=Inno Setup project file -resource.setup-icon=setup dialog icon -resource.post-install-script=script to run after application image is populated -resource.wix-config-file=WiX config file - -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.no-windows-resources=This copy of the JDK does not support Windows. -error.no-windows-resources.advice=Please use the Oracle JDK for Windows. -error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe). -error.iscc-not-found.advice=Download Inno Setup 5 or later from http://www.jrsoftware.org and add it to the PATH. -error.copyright-is-too-long=The copyright string is too long for InnoSetup. -error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters. -error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. -error.too-many-content-types-for-file-association.advice=Specify one and only one MIME type for each file association. -error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe). -error.no-wix-tools.advice=Download WiX 3.0 or later from http://wix.sf.net and add it to the PATH. -error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}]. -error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: http://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx . -error.version-string-major-out-of-range=Major version must be in the range [0, 255]. -error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535]. -error.version-string-minor-out-of-range=Minor version must be in the range [0, 255]. -error.version-string-part-not-number=Failed to convert version component to int. -error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory. -error.version-swap=Failed to update version information for {0}. - -message.result-dir=Result application bundle: {0}. -message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. -message.multiple-launchers=Multiple launchers found in predefined app-image. {0} will be used as primary launcher. -message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". -message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required. -message.outputting-to-location=Generating EXE for installer to: {0}. -message.output-location=Installer (.exe) saved to: {0} -message.tool-version=Detected [{0}] version [{1}]. -message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. -message.running-wsh-script=Running WSH script on application image [{0}]. -message.iscc-file-string=InnoSetup compiler set to {0}. -message.creating-association-with-null-extension=Creating association with null extension. -message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. -message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . -message.truncating.id=Truncating Application ID to 126 chars for Inno Setup. -message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action. -message.generated-product-guid=Generated product GUID: {0}. -message.preparing-msi-config=Preparing MSI config: {0}. -message.generating-msi=Generating MSI: {0}. -message.light-file-string=WiX light tool set to {0}. -message.candle-file-string=WiX candle tool set to {0}. -message.install.dir.exist=The folder [APPLICATIONFOLDER] already exist. Whould you like to install to that folder anyway? -message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}". - --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties 2019-11-18 21:48:58.656360600 -0500 +++ /dev/null 2019-11-18 21:49:00.000000000 -0500 @@ -1,90 +0,0 @@ -# -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - -app.bundler.name=Windows Application Image -app.bundler.description=A Directory based image of a windows Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers -exe.bundler.name=EXE Installer -exe.bundler.description=Microsoft Windows EXE Installer, via InnoIDE. -msi.bundler.name=MSI Installer -msi.bundler.description=Microsoft Windows MSI Installer, via WiX. - -param.menu-group.default=Unknown - -resource.application-icon=application icon -resource.executable-properties-template=Template for creating executable properties file. -resource.inno-setup-project-file=Inno Setup project file -resource.setup-icon=setup dialog icon -resource.post-install-script=script to run after application image is populated -resource.wix-config-file=WiX config file - -error.parameters-null=Parameters map is null. -error.parameters-null.advice=Pass in a non-null parameters map. -error.no-windows-resources=This copy of the JDK does not support Windows. -error.no-windows-resources.advice=Please use the Oracle JDK for Windows. -error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}. -error.cannot-create-output-dir=Output directory {0} cannot be created. -error.cannot-write-to-output-dir=Output directory {0} is not writable. -error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe). -error.iscc-not-found.advice=Download Inno Setup 5 or later from http://www.jrsoftware.org and add it to the PATH. -error.copyright-is-too-long=The copyright string is too long for InnoSetup. -error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters. -error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. -error.too-many-content-types-for-file-association.advice=Specify one and only one MIME type for each file association. -error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe). -error.no-wix-tools.advice=Download WiX 3.0 or later from http://wix.sf.net and add it to the PATH. -error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}]. -error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: http://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx . -error.version-string-major-out-of-range=Major version must be in the range [0, 255]. -error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535]. -error.version-string-minor-out-of-range=Minor version must be in the range [0, 255]. -error.version-string-part-not-number=Failed to convert version component to int. -error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory. -error.version-swap=Failed to update version information for {0}. - -message.result-dir=Result application bundle: {0}. -message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. -message.multiple-launchers=Multiple launchers found in predefined app-image. {0} will be used as primary launcher. -message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". -message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required. -message.outputting-to-location=Generating EXE for installer to: {0}. -message.output-location=Installer (.exe) saved to: {0} -message.tool-version=Detected [{0}] version [{1}]. -message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. -message.running-wsh-script=Running WSH script on application image [{0}]. -message.iscc-file-string=InnoSetup compiler set to {0}. -message.creating-association-with-null-extension=Creating association with null extension. -message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. -message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . -message.truncating.id=Truncating Application ID to 126 chars for Inno Setup. -message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action. -message.generated-product-guid=Generated product GUID: {0}. -message.preparing-msi-config=Preparing MSI config: {0}. -message.generating-msi=Generating MSI: {0}. -message.light-file-string=WiX light tool set to {0}. -message.candle-file-string=WiX candle tool set to {0}. -message.install.dir.exist=The folder [APPLICATIONFOLDER] already exist. Whould you like to install to that folder anyway? -message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}". - --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.iss 2019-11-18 21:49:06.980210100 -0500 +++ /dev/null 2019-11-18 21:49:08.000000000 -0500 @@ -1,74 +0,0 @@ -;This file will be executed next to the application bundle image -;I.e. current directory will contain folder INSTALLER_NAME with application files -[Setup] -AppId=PRODUCT_APP_IDENTIFIER -AppName=INSTALLER_NAME -AppVersion=APPLICATION_VERSION -AppVerName=INSTALLER_NAME APPLICATION_VERSION -AppPublisher=APPLICATION_VENDOR -AppComments=APPLICATION_DESCRIPTION -AppCopyright=APPLICATION_COPYRIGHT -VersionInfoVersion=APPLICATION_VERSION -VersionInfoDescription=APPLICATION_DESCRIPTION -DefaultDirName=APPLICATION_INSTALL_ROOT\INSTALL_DIR -DisableStartupPrompt=Yes -DisableDirPage=DISABLE_DIR_PAGE -DisableProgramGroupPage=Yes -DisableReadyPage=Yes -DisableFinishedPage=Yes -DisableWelcomePage=Yes -DefaultGroupName=APPLICATION_GROUP -;Optional License -LicenseFile=APPLICATION_LICENSE_FILE -;WinXP or above -MinVersion=0,5.1 -OutputBaseFilename=INSTALLER_FILE_NAME -Compression=lzma -SolidCompression=yes -PrivilegesRequired=APPLICATION_INSTALL_PRIVILEGE -SetupIconFile=INSTALLER_NAME\LAUNCHER_NAME.ico -UninstallDisplayIcon={app}\LAUNCHER_NAME.ico -UninstallDisplayName=INSTALLER_NAME -WizardImageStretch=No -WizardSmallImageFile=INSTALLER_NAME-setup-icon.bmp -ArchitecturesInstallIn64BitMode=ARCHITECTURE_BIT_MODE -FILE_ASSOCIATIONS - -[Languages] -Name: "english"; MessagesFile: "compiler:Default.isl" - -[Files] -Source: "INSTALLER_NAME\LAUNCHER_NAME.exe"; DestDir: "{app}"; Flags: ignoreversion -Source: "INSTALLER_NAME\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs - -[Icons] -Name: "{group}\INSTALLER_NAME"; Filename: "{app}\LAUNCHER_NAME.exe"; IconFilename: "{app}\LAUNCHER_NAME.ico"; Check: APPLICATION_MENU_SHORTCUT() -Name: "{commondesktop}\INSTALLER_NAME"; Filename: "{app}\LAUNCHER_NAME.exe"; IconFilename: "{app}\LAUNCHER_NAME.ico"; Check: APPLICATION_DESKTOP_SHORTCUT() -ADD_LAUNCHERS - -[Run] -Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-Xappcds:generatecache"; Check: APPLICATION_APP_CDS_INSTALL() -Filename: "{app}\RUN_FILENAME.exe"; Description: "{cm:LaunchProgram,INSTALLER_NAME}"; Flags: nowait postinstall skipifsilent; Check: APPLICATION_NOT_SERVICE() -Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-install -svcName ""INSTALLER_NAME"" -svcDesc ""APPLICATION_DESCRIPTION"" -mainExe ""APPLICATION_LAUNCHER_FILENAME"" START_ON_INSTALL RUN_AT_STARTUP"; Check: APPLICATION_SERVICE() - -[UninstallRun] -Filename: "{app}\RUN_FILENAME.exe "; Parameters: "-uninstall -svcName INSTALLER_NAME STOP_ON_UNINSTALL"; Check: APPLICATION_SERVICE() - -[Code] -function returnTrue(): Boolean; -begin - Result := True; -end; - -function returnFalse(): Boolean; -begin - Result := False; -end; - -function InitializeSetup(): Boolean; -begin -// Possible future improvements: -// if version less or same => just launch app -// if upgrade => check if same app is running and wait for it to exit - Result := True; -end; --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.iss 2019-11-18 21:49:15.233323900 -0500 +++ /dev/null 2019-11-18 21:49:16.000000000 -0500 @@ -1,57 +0,0 @@ -;This file will be executed next to the application bundle image -;I.e. current directory will contain folder INSTALLER_NAME with application files -[Setup] -AppId=PRODUCT_APP_IDENTIFIER -AppName=INSTALLER_NAME -AppVersion=APPLICATION_VERSION -AppVerName=INSTALLER_NAME APPLICATION_VERSION -AppPublisher=APPLICATION_VENDOR -AppComments=APPLICATION_DESCRIPTION -AppCopyright=APPLICATION_COPYRIGHT -VersionInfoVersion=APPLICATION_VERSION -VersionInfoDescription=APPLICATION_DESCRIPTION -DefaultDirName=APPLICATION_INSTALL_ROOT\INSTALLER_NAME -DisableStartupPrompt=Yes -DisableDirPage=DISABLE_DIR_PAGE -DisableProgramGroupPage=Yes -DisableReadyPage=Yes -DisableFinishedPage=Yes -DisableWelcomePage=Yes -DefaultGroupName=APPLICATION_GROUP -;Optional License -LicenseFile=APPLICATION_LICENSE_FILE -;WinXP or above -MinVersion=0,5.1 -OutputBaseFilename=INSTALLER_FILE_NAME -Compression=lzma -SolidCompression=yes -PrivilegesRequired=APPLICATION_INSTALL_PRIVILEGE -SetupIconFile= -UninstallDisplayIcon= -UninstallDisplayName=INSTALLER_NAME -WizardImageStretch=No -WizardSmallImageFile=INSTALLER_NAME-setup-icon.bmp -ArchitecturesInstallIn64BitMode=ARCHITECTURE_BIT_MODE -FILE_ASSOCIATIONS - -[Languages] -Name: "english"; MessagesFile: "compiler:Default.isl" - -[Files] -Source: "APPLICATION_IMAGE\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs - -[Code] -function returnTrue(): Boolean; -begin - Result := True; -end; - -function returnFalse(): Boolean; -begin - Result := False; -end; - -function InitializeSetup(): Boolean; -begin - Result := True; -end; --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.wxs 2019-11-18 21:49:24.902710400 -0500 +++ /dev/null 2019-11-18 21:49:26.000000000 -0500 @@ -1,46 +0,0 @@ - - - - - -UPGRADE_BLOCK - - - - - - - - - - WIX36_ONLY_START - - WIX36_ONLY_END - - - -UI_BLOCK - - --- old/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.wxs 2019-11-18 21:49:33.880423800 -0500 +++ /dev/null 2019-11-18 21:49:35.000000000 -0500 @@ -1,53 +0,0 @@ - - - - - - UPGRADE_BLOCK - - - - - - - - - - WIX36_ONLY_START - - WIX36_ONLY_END - - - - CA_BLOCK - - INVALID_INSTALL_DIR_DLG_BLOCK - UI_BLOCK - - - - ADD_LAUNCHER_ICONS - - --- old/src/jdk.jpackage/windows/classes/module-info.java.extra 2019-11-18 21:49:42.426032600 -0500 +++ /dev/null 2019-11-18 21:49:43.000000000 -0500 @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -provides jdk.jpackage.internal.Bundler with - jdk.jpackage.internal.WinAppBundler, - jdk.jpackage.internal.WinExeBundler, - jdk.jpackage.internal.WinMsiBundler; - --- old/test/jdk/tools/jpackage/JPackageHelpTest.java 2019-11-18 21:49:50.745628300 -0500 +++ /dev/null 2019-11-18 21:49:52.000000000 -0500 @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage help test - * @library helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageHelpTest - */ -public class JPackageHelpTest { - - // Platform specific help messages. - private static final String WINDOWS_HELP = - "--win-dir-chooser"; - private static final String OSX_HELP = - "--mac-bundle-identifier"; - private static final String LINUX_HELP = - "--linux-bundle-name"; - - private static void validate(String output1, String output2) - throws Exception { - if (output1.split("\n").length < 25) { - throw new AssertionError("jpacakger --help failed"); - } - - if (output2.split("\n").length < 25) { - throw new AssertionError("jpacakger -h failed"); - } - - // Make sure output matches for --help and -h - if (!output1.equals(output2)) { - System.err.println("================= --help ================="); - System.err.println(output1); - - System.err.println("=================== -h ==================="); - System.err.println(output2); - - throw new AssertionError( - "jpacakger help text does not match between --help and -h"); - } - - if (JPackageHelper.isWindows()) { - if (!output1.contains(WINDOWS_HELP)) { - throw new AssertionError( - "jpacakger help text missing Windows specific help"); - } - - if (output1.contains(OSX_HELP) || output1.contains(LINUX_HELP)) { - throw new AssertionError( - "jpacakger help text contains other platforms specific help"); - - } - } else if (JPackageHelper.isOSX()) { - if (!output1.contains(OSX_HELP)) { - throw new AssertionError( - "jpacakger help text missing OS X specific help"); - } - - if (output1.contains(WINDOWS_HELP) || - output1.contains(LINUX_HELP)) { - throw new AssertionError( - "jpacakger help text contains other platforms specific help"); - } - } else if (JPackageHelper.isLinux()) { - if (!output1.contains(LINUX_HELP)) { - throw new AssertionError( - "jpacakger help text missing Linux specific help"); - } - - if (output1.contains(OSX_HELP) || output1.contains(WINDOWS_HELP)) { - throw new AssertionError( - "jpacakger help text contains other platforms specific help"); - } - } - } - - private static void testHelp() throws Exception { - String output1 = JPackageHelper.executeCLI(true, "--help"); - String output2 = JPackageHelper.executeCLI(true, "-h"); - validate(output1, output2); - } - - private static void testHelpToolProvider() throws Exception { - String output1 = JPackageHelper.executeToolProvider(true, "--help"); - String output2 = JPackageHelper.executeToolProvider(true, "-h"); - validate(output1, output2); - } - - public static void main(String[] args) throws Exception { - testHelp(); - testHelpToolProvider(); - } - -} --- old/test/jdk/tools/jpackage/JPackageNoArgTest.java 2019-11-18 21:49:59.332382000 -0500 +++ /dev/null 2019-11-18 21:50:00.000000000 -0500 @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage no argument test - * @library helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageNoArgTest - */ -public class JPackageNoArgTest { - - private static final String RESULT1 = "Usage: jpackage "; - private static final String[] EXPECTED = - {"--help", "list of possible options"}; - - private static void validate(String output) throws Exception { - String[] result = output.split("\n"); - if (result.length != 2) { - System.err.println(output); - throw new AssertionError( - "Invalid number of lines in output: " + result.length); - } - - if (!result[0].trim().equals(RESULT1)) { - System.err.println("Expected: " + RESULT1); - System.err.println("Actual: " + result[0]); - throw new AssertionError("Unexpected line 1"); - } - - for (String expected : EXPECTED) { - if (!result[1].contains(expected)) { - System.err.println("Expected to contain: " + expected); - System.err.println("Actual: " + result[1]); - throw new AssertionError("Unexpected line 2"); - } - } - } - - private static void testNoArg() throws Exception { - String output = JPackageHelper.executeCLI(true, new String[0]); - validate(output); - } - - private static void testNoArgToolProvider() throws Exception { - String output = - JPackageHelper.executeToolProvider(true, new String[0]); - validate(output); - } - - public static void main(String[] args) throws Exception { - testNoArg(); - testNoArgToolProvider(); - } - -} --- old/test/jdk/tools/jpackage/JPackageVersionTest.java 2019-11-18 21:50:07.785057100 -0500 +++ /dev/null 2019-11-18 21:50:09.000000000 -0500 @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage version test - * @library helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageVersionTest - */ -public class JPackageVersionTest { - - private static final String ARG = "--version"; - private static final String RESULT = "jpackage version" - + " " + System.getProperty("java.version"); - - private static void validate(String output) throws Exception { - String[] result = output.split("\n"); - if (result.length != 1) { - System.err.println(output); - throw new AssertionError("Invalid number of lines in output: " + result.length); - } - - if (!result[0].trim().equals(RESULT)) { - System.err.println("Expected: " + RESULT); - System.err.println("Actual: " + result[0]); - throw new AssertionError("Unexpected line 1"); - } - } - - private static void testVersion() throws Exception { - String output = JPackageHelper.executeCLI(true, ARG); - validate(output); - } - - private static void testVersionToolProvider() throws Exception { - String output = JPackageHelper.executeToolProvider(true, ARG); - validate(output); - } - - public static void main(String[] args) throws Exception { - testVersion(); - testVersionToolProvider(); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddModulesTest.java 2019-11-18 21:50:16.284313600 -0500 +++ /dev/null 2019-11-18 21:50:17.000000000 -0500 @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create image module test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageAddModulesTest - */ -public class JPackageCreateAppImageAddModulesTest { - private static final String OUTPUT = "output"; - - private static final String [] CMD1 = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input", - "--add-modules", "java.desktop", - }; - - private static final String [] CMD2 = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input", - "--add-modules", "java.desktop,java.xml", - }; - - private static final String [] CMD3 = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input", - "--add-modules", "java.desktop", - "--add-modules", "java.xml", - }; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloModule(); - JPackageCreateAppImageBase.testCreateAppImage(CMD1); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD1); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD2); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD3); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageArgumentsBase.java 2019-11-18 21:50:24.890115700 -0500 +++ /dev/null 2019-11-18 21:50:26.000000000 -0500 @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateAppImageArgumentsBase { - - private static final String app = JPackagePath.getApp(); - private static final String appOutput = JPackagePath.getAppOutputFile(); - private static final String appWorkingDir = JPackagePath.getAppWorkingDir(); - - private static final String ARGUMENT1 = "argument"; - private static final String ARGUMENT2 = "Some Arguments"; - private static final String ARGUMENT3 = "Value \"with\" quotes"; - - private static final String ARGUMENT_CMD1 = "test"; - - private static final List arguments = new ArrayList<>(); - private static final List argumentsCmd = new ArrayList<>(); - - public static void initArguments(boolean toolProvider, String[] cmd) { - if (arguments.isEmpty()) { - arguments.add(ARGUMENT1); - arguments.add(ARGUMENT2); - arguments.add(ARGUMENT3); - } - - if (argumentsCmd.isEmpty()) { - argumentsCmd.add(ARGUMENT_CMD1); - } - - String argumentsMap - = JPackageHelper.listToArgumentsMap(arguments, toolProvider); - cmd[cmd.length - 1] = argumentsMap; - } - - private static void validateResult(String[] result, List args) - throws Exception { - if (result.length != (args.size() + 2)) { - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: " + args.size())) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - - int index = 2; - for (String arg : args) { - if (!result[index].trim().equals(arg)) { - throw new AssertionError( - "Unexpected result[" + index + "]: " + result[index]); - } - index++; - } - } - - private static void validate(String arg, List expectedArgs) - throws Exception { - int retVal; - - if (arg == null) { - retVal = JPackageHelper.execute(null, app); - } else { - retVal = JPackageHelper.execute(null, app, arg); - } - if (retVal != 0) { - throw new AssertionError("Test application exited with error: " - + retVal); - } - - File outfile = new File(appWorkingDir + File.separator + appOutput); - if (!outfile.exists()) { - throw new AssertionError(appOutput + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - String[] result = output.split("\n"); - validateResult(result, expectedArgs); - } - - public static void testCreateAppImage(String[] cmd) throws Exception { - initArguments(false, cmd); - JPackageHelper.executeCLI(true, cmd); - validate(null, arguments); - validate(ARGUMENT_CMD1, argumentsCmd); - } - - public static void testCreateAppImageToolProvider(String[] cmd) throws Exception { - initArguments(true, cmd); - JPackageHelper.executeToolProvider(true, cmd); - validate(null, arguments); - validate(ARGUMENT_CMD1, argumentsCmd); - } -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageArgumentsModuleTest.java 2019-11-18 21:50:33.416010300 -0500 +++ /dev/null 2019-11-18 21:50:34.000000000 -0500 @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create image with --arguments test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageArgumentsBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageArgumentsModuleTest - */ -public class JPackageCreateAppImageArgumentsModuleTest { - private static final String OUTPUT = "output"; - - private static final String[] CMD = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input", - "--arguments", "TBD"}; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloModule(); - JPackageCreateAppImageArgumentsBase.testCreateAppImage(CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageArgumentsBase.testCreateAppImageToolProvider(CMD); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageArgumentsTest.java 2019-11-18 21:50:41.808950500 -0500 +++ /dev/null 2019-11-18 21:50:43.000000000 -0500 @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create image with --arguments test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageArgumentsBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageArgumentsTest - */ -public class JPackageCreateAppImageArgumentsTest { - private static final String OUTPUT = "output"; - - private static final String[] CMD = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--arguments", "TBD"}; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - JPackageCreateAppImageArgumentsBase.testCreateAppImage(CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageArgumentsBase.testCreateAppImageToolProvider(CMD); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageIconTest.java 2019-11-18 21:50:50.097097000 -0500 +++ /dev/null 2019-11-18 21:50:51.000000000 -0500 @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; - -/* - * @test - * @summary jpackage create image to verify --icon - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageIconTest - */ -public class JPackageCreateAppImageIconTest { - private static final String OUTPUT = "output"; - private static final String app = JPackagePath.getApp(); - private static final String appOutput = JPackagePath.getAppOutputFile(); - private static final String appWorkingDir = JPackagePath.getAppWorkingDir(); - - private static final String[] CMD = { - "create-app-image", - "--input", "input", - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--icon", getIconPath(), - "--output", OUTPUT}; - - private static void validateResult(String[] result) throws Exception { - if (result.length != 2) { - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: 0")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - } - - private static void validate() throws Exception { - int retVal = JPackageHelper.execute(null, app); - if (retVal != 0) { - throw new AssertionError( - "Test application exited with error: " + retVal); - } - - File outfile = new File(appWorkingDir + File.separator + appOutput); - if (!outfile.exists()) { - throw new AssertionError(appOutput + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - String[] result = output.split("\n"); - validateResult(result); - } - - private static void validateIcon() throws Exception { - File origIcon = new File(getIconPath()); - File icon = new File(JPackagePath.getAppIcon()); - if (origIcon.length() != icon.length()) { - System.err.println("origIcon.length(): " + origIcon.length()); - System.err.println("icon.length(): " + icon.length()); - throw new AssertionError("Icons size does not match"); - } - } - - private static void testIcon() throws Exception { - JPackageHelper.executeCLI(true, CMD); - validate(); - validateIcon(); - } - - private static void testIconToolProvider() throws Exception { - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageHelper.executeToolProvider(true, CMD); - validate(); - validateIcon(); - } - - private static String getIconPath() { - String ext = ".ico"; - if (JPackageHelper.isOSX()) { - ext = ".icns"; - } else if (JPackageHelper.isLinux()) { - ext = ".png"; - } - - String path = JPackagePath.getTestSrcRoot() + File.separator + "resources" - + File.separator + "icon" + ext; - - return path; - } - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - testIcon(); - testIconToolProvider(); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageMainClassAttributeTest.java 2019-11-18 21:50:58.732133200 -0500 +++ /dev/null 2019-11-18 21:51:00.000000000 -0500 @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; - -/* - * @test - * @summary jpackage create image with no main class arguments and with main-class attribute - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageMainClassAttributeTest - */ -public class JPackageCreateAppImageMainClassAttributeTest { - private static final String OUTPUT = "output"; - private static final String app = JPackagePath.getApp(); - private static final String appOutput = JPackagePath.getAppOutputFile(); - private static final String appWorkingDir = JPackagePath.getAppWorkingDir(); - - private static final String[] CMD = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar"}; - - private static void validateResult(String[] result) throws Exception { - if (result.length != 2) { - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: 0")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - } - - private static void validate() throws Exception { - int retVal = JPackageHelper.execute(null, app); - if (retVal != 0) { - throw new AssertionError( - "Test application exited with error: " + retVal); - } - - File outfile = new File(appWorkingDir + File.separator + appOutput); - if (!outfile.exists()) { - throw new AssertionError(appOutput + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - String[] result = output.split("\n"); - validateResult(result); - } - - private static void testMainClassAttribute() throws Exception { - JPackageHelper.executeCLI(true, CMD); - validate(); - } - - private static void testMainClassAttributeToolProvider() throws Exception { - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageHelper.executeToolProvider(true, CMD); - validate(); - } - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJarWithMainClass(); - testMainClassAttribute(); - testMainClassAttributeToolProvider(); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageMainClassErrorTest.java 2019-11-18 21:51:07.201995500 -0500 +++ /dev/null 2019-11-18 21:51:08.000000000 -0500 @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; - -/* - * @test - * @summary jpackage create image with no main class arguments and with main-class attribute - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageMainClassErrorTest - */ -public class JPackageCreateAppImageMainClassErrorTest { - private static final String OUTPUT = "output"; - private static final String app = JPackagePath.getApp(); - private static final String appOutput = JPackagePath.getAppOutputFile(); - private static final String appWorkingDir = JPackagePath.getAppWorkingDir(); - - private static final String[] CMD = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar"}; - - private static void validate(String output) throws Exception { - String[] result = output.split("\n"); - if (result.length != 2) { - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().contains("main class was not specified")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().startsWith("Advice to fix: ")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - } - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - - JPackageHelper.deleteOutputFolder(OUTPUT); - validate(JPackageHelper.executeCLI(false, CMD)); - - JPackageHelper.deleteOutputFolder(OUTPUT); - validate(JPackageHelper.executeToolProvider(false, CMD)); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModularJarTest.java 2019-11-18 21:51:15.582273500 -0500 +++ /dev/null 2019-11-18 21:51:17.000000000 -0500 @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create image modular jar test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageModularJarTest - */ -public class JPackageCreateAppImageModularJarTest { - private static final String OUTPUT = "output"; - - private static final String [] CMD1 = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "com.hello.jar", - "--main-class", "com.hello.Hello", - }; - - private static final String [] CMD2 = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input/com.hello.jar", - }; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloModule(); - - JPackageCreateAppImageBase.testCreateAppImage(CMD1); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD1); - - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageBase.testCreateAppImage(CMD2); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD2); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModuleMainClassErrorTest.java 2019-11-18 21:51:24.236938700 -0500 +++ /dev/null 2019-11-18 21:51:25.000000000 -0500 @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; - -/* - * @test - * @summary jpackage create image with no main class arguments and with main-class attribute - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageModuleMainClassErrorTest - */ -public class JPackageCreateAppImageModuleMainClassErrorTest { - private static final String OUTPUT = "output"; - private static final String app = JPackagePath.getApp(); - private static final String appOutput = JPackagePath.getAppOutputFile(); - private static final String appWorkingDir = JPackagePath.getAppWorkingDir(); - - private static final String [] CMD = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello", - "--module-path", "input"}; - - private static void validate(String buildOutput) throws Exception { - - File outfile = new File(appWorkingDir + File.separator + appOutput); - int retVal = JPackageHelper.execute(outfile, app); - if (retVal == 0) { - throw new AssertionError( - "Test application exited without error: "); - } - - if (!outfile.exists()) { - throw new AssertionError(appOutput + " was not created"); - } - String output = Files.readString(outfile.toPath()); - String[] result = output.split("\n"); - - if (result.length != 1) { - System.out.println("outfile (" + outfile + ") content: " + output); - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().contains( - "does not have a ModuleMainClass attribute")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - } - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloModule(); - - JPackageHelper.deleteOutputFolder(OUTPUT); - validate(JPackageHelper.executeCLI(true, CMD)); - - JPackageHelper.deleteOutputFolder(OUTPUT); - validate(JPackageHelper.executeToolProvider(true, CMD)); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModulePathTest.java 2019-11-18 21:51:32.620086000 -0500 +++ /dev/null 2019-11-18 21:51:34.000000000 -0500 @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create image module test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageModulePathTest - */ - -import java.io.File; - -public class JPackageCreateAppImageModulePathTest { - private static final String OUTPUT = "output"; - - private static final String [] CMD1 = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input", - }; - - private static final String [] CMD2 = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input" + File.pathSeparator + "input-other", - }; - - private static final String [] CMD3 = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input", - "--module-path", "input-other", - }; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloModule(); - JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD1); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD2); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD3); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModuleTest.java 2019-11-18 21:51:41.275075500 -0500 +++ /dev/null 2019-11-18 21:51:42.000000000 -0500 @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create image module test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageModuleTest - */ -public class JPackageCreateAppImageModuleTest { - private static final String OUTPUT = "output"; - - private static final String [] CMD = { - "create-app-image", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input"}; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloModule(); - JPackageCreateAppImageBase.testCreateAppImage(CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageNoNameTest.java 2019-11-18 21:51:49.672459600 -0500 +++ /dev/null 2019-11-18 21:51:51.000000000 -0500 @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; - -/* - * @test - * @summary jpackage create image with no --name - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageNoNameTest - */ -public class JPackageCreateAppImageNoNameTest { - private static final String OUTPUT = "output"; - private static final String app = JPackagePath.getAppNoName(); - private static final String appOutput = JPackagePath.getAppOutputFile(); - private static final String appWorkingDir = JPackagePath.getAppWorkingDirNoName(); - - private static final String[] CMD = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--main-jar", "hello.jar", - "--main-class", "Hello", - }; - - private static void validateResult(String[] result) throws Exception { - if (result.length != 2) { - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: 0")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - } - - private static void validate() throws Exception { - int retVal = JPackageHelper.execute(null, app); - if (retVal != 0) { - throw new AssertionError( - "Test application exited with error: " + retVal); - } - - File outfile = new File(appWorkingDir + File.separator + appOutput); - if (!outfile.exists()) { - throw new AssertionError(appOutput + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - String[] result = output.split("\n"); - validateResult(result); - } - - private static void testMainClassAttribute() throws Exception { - JPackageHelper.executeCLI(true, CMD); - validate(); - } - - private static void testMainClassAttributeToolProvider() throws Exception { - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageHelper.executeToolProvider(true, CMD); - validate(); - } - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - testMainClassAttribute(); - testMainClassAttributeToolProvider(); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageRuntimeBase.java 2019-11-18 21:51:58.008991100 -0500 +++ /dev/null 2019-11-18 21:51:59.000000000 -0500 @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; - - public class JPackageCreateAppImageRuntimeBase { - private static final String app = JPackagePath.getApp(); - private static final String appWorkingDir = JPackagePath.getAppWorkingDir(); - private static final String runtimeJava = JPackagePath.getRuntimeJava(); - private static final String runtimeJavaOutput = "javaOutput.txt"; - private static final String appOutput = JPackagePath.getAppOutputFile(); - - private static void validateResult(String[] result) throws Exception { - if (result.length != 2) { - throw new AssertionError("Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: 0")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - } - - private static void validate() throws Exception { - int retVal = JPackageHelper.execute(null, app); - if (retVal != 0) { - throw new AssertionError("Test application exited with error: " + retVal); - } - - File outfile = new File(appWorkingDir + File.separator + appOutput); - if (!outfile.exists()) { - throw new AssertionError(appOutput + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - String[] result = output.split("\n"); - validateResult(result); - } - - private static void validateRuntime() throws Exception { - int retVal = JPackageHelper.execute(new File(runtimeJavaOutput), runtimeJava, "--list-modules"); - if (retVal != 0) { - throw new AssertionError("Test application exited with error: " + retVal); - } - - File outfile = new File(runtimeJavaOutput); - if (!outfile.exists()) { - throw new AssertionError(runtimeJavaOutput + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - String[] result = output.split("\n"); - if (result.length != 1) { - throw new AssertionError("Unexpected number of lines: " + result.length); - } - - if (!result[0].startsWith("java.base")) { - throw new AssertionError("Unexpected result: " + result[0]); - } - } - - public static void testCreateAppImage(String [] cmd) throws Exception { - JPackageHelper.executeCLI(true, cmd); - validate(); - validateRuntime(); - } - - public static void testCreateAppImageToolProvider(String [] cmd) throws Exception { - JPackageHelper.executeToolProvider(true, cmd); - validate(); - validateRuntime(); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageRuntimeModuleTest.java 2019-11-18 21:52:06.497819800 -0500 +++ /dev/null 2019-11-18 21:52:07.000000000 -0500 @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create image runtime test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageRuntimeBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageRuntimeModuleTest - */ -public class JPackageCreateAppImageRuntimeModuleTest { - private static final String OUTPUT = "output"; - private static final String [] CMD = { - "create-app-image", - "--runtime-image", "runtime", - "--output", OUTPUT, - "--name", "test", - "--module", "com.hello/com.hello.Hello", - "--module-path", "input"}; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloModule(); - JPackageHelper.createRuntime(); - JPackageCreateAppImageRuntimeBase.testCreateAppImage(CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageRuntimeBase.testCreateAppImageToolProvider(CMD); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageRuntimeTest.java 2019-11-18 21:52:14.829003800 -0500 +++ /dev/null 2019-11-18 21:52:16.000000000 -0500 @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create image runtime test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageRuntimeBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageRuntimeTest - */ -public class JPackageCreateAppImageRuntimeTest { - private static final String OUTPUT = "output"; - private static final String [] CMD = { - "create-app-image", - "--runtime-image", "runtime", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - }; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - JPackageHelper.createRuntime(); - JPackageCreateAppImageRuntimeBase.testCreateAppImage(CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageRuntimeBase.testCreateAppImageToolProvider(CMD); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageTempRootTest.java 2019-11-18 21:52:23.113017500 -0500 +++ /dev/null 2019-11-18 21:52:24.000000000 -0500 @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; - - /* - * @test - * @requires (os.family == "windows") - * @summary jpackage create image to test --temp-root - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageTempRootTest - */ -public class JPackageCreateAppImageTempRootTest { - private static final String OUTPUT = "output"; - private static String buildRoot = null; - private static final String BUILD_ROOT = "buildRoot"; - private static final String BUILD_ROOT_TB = "buildRootToolProvider"; - - private static final String [] CMD = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - }; - - private static final String [] CMD_BUILD_ROOT = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--temp-root", "TBD"}; - - private static void validate(boolean retain) throws Exception { - File br = new File(buildRoot); - if (retain) { - if (!br.exists()) { - throw new AssertionError(br.getAbsolutePath() + " does not exist"); - } - } else { - if (br.exists()) { - throw new AssertionError(br.getAbsolutePath() + " exist"); - } - } - } - - private static void init(boolean toolProvider) { - if (toolProvider) { - buildRoot = BUILD_ROOT_TB; - } else { - buildRoot = BUILD_ROOT; - } - - CMD_BUILD_ROOT[CMD_BUILD_ROOT.length - 1] = buildRoot; - } - - private static void testTempRoot() throws Exception { - init(false); - JPackageHelper.executeCLI(true, CMD); - validate(false); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageHelper.executeCLI(true, CMD_BUILD_ROOT); - validate(true); - } - - private static void testTempRootToolProvider() throws Exception { - init(true); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageHelper.executeToolProvider(true, CMD); - validate(false); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageHelper.executeToolProvider(true, CMD_BUILD_ROOT); - validate(true); - } - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - testTempRoot(); - testTempRootToolProvider(); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageTest.java 2019-11-18 21:52:31.889123800 -0500 +++ /dev/null 2019-11-18 21:52:33.000000000 -0500 @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary jpackage create image test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @build JPackageCreateAppImageBase - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageTest - */ -public class JPackageCreateAppImageTest { - private static final String OUTPUT = "output"; - - private static final String [] CMD = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - }; - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - JPackageCreateAppImageBase.testCreateAppImage(CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD); - } -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageVerboseTest.java 2019-11-18 21:52:40.273993000 -0500 +++ /dev/null 2019-11-18 21:52:41.000000000 -0500 @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create image verbose test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageVerboseTest - */ -public class JPackageCreateAppImageVerboseTest { - private static final String OUTPUT = "output"; - private static final String[] CMD = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - }; - - private static final String[] CMD_VERBOSE = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--verbose"}; - - private static void validate(String result, String resultVerbose) - throws Exception { - String[] r = result.split("\n"); - String[] rv = resultVerbose.split("\n"); - - if (r.length >= rv.length) { - System.err.println("r.length: " + r.length); - System.err.println(result); - System.err.println("rv.length: " + rv.length); - System.err.println(resultVerbose); - throw new AssertionError( - "non-verbose output is less or equal to verbose output"); - } - } - - private static void testCreateAppImage() throws Exception { - String result = JPackageHelper.executeCLI(true, CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - String resultVerbose = JPackageHelper.executeCLI(true, CMD_VERBOSE); - validate(result, resultVerbose); - } - - private static void testCreateAppImageToolProvider() throws Exception { - JPackageHelper.deleteOutputFolder(OUTPUT); - String result = JPackageHelper.executeToolProvider(true, CMD); - JPackageHelper.deleteOutputFolder(OUTPUT); - String resultVerbose = - JPackageHelper.executeToolProvider(true, CMD_VERBOSE); - validate(result, resultVerbose); - } - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - testCreateAppImage(); - testCreateAppImageToolProvider(); - } - -} --- old/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageVersionTest.java 2019-11-18 21:52:48.694648400 -0500 +++ /dev/null 2019-11-18 21:52:50.000000000 -0500 @@ -1,103 +0,0 @@ - -import java.io.File; -import java.nio.file.Files; - -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create image --app-version test - * @library ../helpers - * @build JPackageHelper - * @build JPackagePath - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateAppImageVersionTest - */ -public class JPackageCreateAppImageVersionTest { - private static final String OUTPUT = "output"; - private static final String appCfg = JPackagePath.getAppCfg(); - private static final String VERSION = "2.3"; - private static final String VERSION_DEFAULT = "1.0"; - - private static final String[] CMD = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - }; - - private static final String[] CMD_VERSION = { - "create-app-image", - "--input", "input", - "--output", OUTPUT, - "--name", "test", - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--app-version", VERSION}; - - private static void validate(String version) - throws Exception { - File outfile = new File(appCfg); - if (!outfile.exists()) { - throw new AssertionError(appCfg + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - if (version == null) { - version = VERSION_DEFAULT; - } - - String expected = "app.version=" + version; - if (!output.contains(expected)) { - System.err.println("Expected: " + expected); - throw new AssertionError("Cannot find expected entry in config file"); - } - } - - private static void testVersion() throws Exception { - JPackageHelper.executeCLI(true, CMD); - validate(null); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageHelper.executeCLI(true, CMD_VERSION); - validate(VERSION); - } - - private static void testVersionToolProvider() throws Exception { - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageHelper.executeToolProvider(true, CMD); - validate(null); - JPackageHelper.deleteOutputFolder(OUTPUT); - JPackageHelper.executeToolProvider(true, CMD_VERSION); - validate(VERSION); - } - - public static void main(String[] args) throws Exception { - JPackageHelper.createHelloImageJar(); - testVersion(); - testVersionToolProvider(); - } - -} --- old/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerBase.java 2019-11-18 21:52:57.407233200 -0500 +++ /dev/null 2019-11-18 21:52:59.000000000 -0500 @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT.toLowerCase()); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getLinuxInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - if (EXT.equals("rpm")) { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT; - } else { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - } - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello" }; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerBundleNameBase.java 2019-11-18 21:53:06.188285100 -0500 +++ /dev/null 2019-11-18 21:53:07.000000000 -0500 @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerBundleNameBase { - - private static String TEST_NAME; - private static String BUNDLE_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT.toLowerCase()); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getLinuxInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - BUNDLE_NAME = "jpackage-test-bundle-name"; - EXT = ext; - if (EXT.equals("rpm")) { - OUTPUT = "output" + File.separator + BUNDLE_NAME + "-1.0-1.x86_64." + EXT; - } else { - OUTPUT = "output" + File.separator + BUNDLE_NAME + "-1.0." + EXT; - } - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--linux-bundle-name", BUNDLE_NAME}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerFileAssociationsBase.java 2019-11-18 21:53:15.068366300 -0500 +++ /dev/null 2019-11-18 21:53:16.000000000 -0500 @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.Desktop; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerFileAssociationsBase { - - private static String TEST_NAME; - private static String EXT; - private static String TEST_EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT.toLowerCase()); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void validateAppOutput() throws Exception { - File outFile = new File("appOutput.txt"); - int count = 10; - boolean bOutputCreated = false; - while (count > 0) { - if (!outFile.exists()) { - Thread.sleep(3000); - count--; - } else { - bOutputCreated = true; - break; - } - } - - if (!bOutputCreated) { - throw new AssertionError(outFile.getAbsolutePath() + " was not created"); - } - - String output = Files.readString(outFile.toPath()); - String[] result = output.split("\n"); - if (result.length != 3) { - System.err.println(output); - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: 1")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - - File faFile = new File(TEST_NAME + "." + TEST_EXT); - if (!result[2].trim().equals(faFile.getAbsolutePath())) { - throw new AssertionError("Unexpected result[2]: " + result[2]); - } - } - - private static void verifyInstall() throws Exception { - createFileAssociationsTestFile(); - Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT)); - validateAppOutput(); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - } - - private static void createFileAssociationsTestFile() throws Exception { - try (PrintWriter out = new PrintWriter(new BufferedWriter( - new FileWriter(TEST_NAME + "." + TEST_EXT)))) { - out.println(TEST_NAME); - } - } - - private static void createFileAssociationsProperties() throws Exception { - try (PrintWriter out = new PrintWriter(new BufferedWriter( - new FileWriter("fa.properties")))) { - out.println("extension=" + TEST_EXT); - out.println("mime-type=application/" + TEST_EXT); - out.println("description=jpackage test extention"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - TEST_EXT = "jptest1"; - if (EXT.equals("rpm")) { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT; - } else { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - } - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--file-associations", "fa.properties"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - createFileAssociationsProperties(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerInstallDirBase.java 2019-11-18 21:53:23.568566600 -0500 +++ /dev/null 2019-11-18 21:53:25.000000000 -0500 @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerInstallDirBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT.toLowerCase()); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getLinuxInstalledApp("jpackage", TEST_NAME); - JPackageInstallerHelper.validateApp(app); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getLinuxInstallFolder("jpackage", TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - folderPath = JPackagePath.getLinuxInstallFolder("jpackage", null); - folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - if (EXT.equals("rpm")) { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT; - } else { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - } - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--install-dir", "/opt/jpackage"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerLicenseBase.java 2019-11-18 21:53:31.929591200 -0500 +++ /dev/null 2019-11-18 21:53:33.000000000 -0500 @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerLicenseBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT.toLowerCase()); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getLinuxInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - if (EXT.equals("rpm")) { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT; - } else { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - } - CMD = new String [] { - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--license-file", JPackagePath.getLicenseFilePath()}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerLicenseTypeBase.java 2019-11-18 21:53:40.386941200 -0500 +++ /dev/null 2019-11-18 21:53:41.000000000 -0500 @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerLicenseTypeBase { - - private static String TEST_NAME; - private static String EXT; - private static String JP_LICENSE_TYPE; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT.toLowerCase()); - JPackageInstallerHelper.copyTestResults(files); - } - - private static final String infoResult = "infoResult.txt"; - private static void validatePackage() throws Exception { - int retVal = JPackageHelper.execute(new File(infoResult),"rpm", - "--query", "--package", "--info", OUTPUT.toLowerCase()); - if (retVal != 0) { - throw new AssertionError("rpm exited with error: " + retVal); - } - - File outfile = new File(infoResult); - if (!outfile.exists()) { - throw new AssertionError(infoResult + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - if (!output.contains(JP_LICENSE_TYPE)) { - throw new AssertionError("Unexpected result: " + output); - } - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - validatePackage(); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getLinuxInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - JP_LICENSE_TYPE = "JP_LICENSE_TYPE"; - if (EXT.equals("rpm")) { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT; - } else { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - } - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--linux-rpm-license-type", JP_LICENSE_TYPE}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerMaintainerBase.java 2019-11-18 21:53:48.836431900 -0500 +++ /dev/null 2019-11-18 21:53:50.000000000 -0500 @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerMaintainerBase { - - private static String TEST_NAME; - private static String EMAIL; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT.toLowerCase()); - JPackageInstallerHelper.copyTestResults(files); - } - - private static final String infoResult = "infoResult.txt"; - private static void validatePackage() throws Exception { - int retVal = JPackageHelper.execute(new File(infoResult), "dpkg", - "--info", OUTPUT.toLowerCase()); - if (retVal != 0) { - throw new AssertionError("dpkg exited with error: " + retVal); - } - - File outfile = new File(infoResult); - if (!outfile.exists()) { - throw new AssertionError(infoResult + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - if (!output.contains("Maintainer: " + EMAIL)) { - throw new AssertionError("Unexpected result: " + output); - } - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - validatePackage(); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getLinuxInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EMAIL = "jpackage-test@java.com"; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--linux-deb-maintainer", EMAIL}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerPackageDepsBase.java 2019-11-18 21:53:57.470386200 -0500 +++ /dev/null 2019-11-18 21:53:58.000000000 -0500 @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerPackageDepsBase { - - private static String TEST_NAME; - private static String DEP_NAME; - private static String EXT; - private static String OUTPUT; - private static String OUTPUT_DEP; - private static String[] CMD; - private static String[] CMD_DEP; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT.toLowerCase()); - files.add(OUTPUT_DEP.toLowerCase()); - JPackageInstallerHelper.copyTestResults(files); - } - - private static final String infoResult = "infoResult.txt"; - private static void validatePackage() throws Exception { - if (EXT.equals("rpm")) { - int retVal = JPackageHelper.execute(new File(infoResult),"rpm", - "--query", "--package", "--requires", OUTPUT.toLowerCase()); - if (retVal != 0) { - throw new AssertionError("rpm exited with error: " + retVal); - } - } else { - int retVal = JPackageHelper.execute(new File(infoResult), "dpkg", - "--info", OUTPUT.toLowerCase()); - if (retVal != 0) { - throw new AssertionError("dpkg exited with error: " + retVal); - } - } - - File outfile = new File(infoResult); - if (!outfile.exists()) { - throw new AssertionError(infoResult + " was not created"); - } - - String output = Files.readString(outfile.toPath()); - if (!output.contains(DEP_NAME.toLowerCase())) { - throw new AssertionError("Unexpected result: " + output); - } - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageHelper.executeCLI(true, CMD_DEP); - JPackageInstallerHelper.validateOutput(OUTPUT); - JPackageInstallerHelper.validateOutput(OUTPUT_DEP); - validatePackage(); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getLinuxInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - app = JPackagePath.getLinuxInstalledApp(DEP_NAME); - JPackageInstallerHelper.validateApp(app); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - folderPath = JPackagePath.getLinuxInstallFolder(DEP_NAME); - folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - DEP_NAME = name + "Dep"; - EXT = ext; - if (EXT.equals("rpm")) { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT; - OUTPUT_DEP = "output" + File.separator + DEP_NAME + "-1.0-1.x86_64." + EXT; - } else { - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - OUTPUT_DEP = "output" + File.separator + DEP_NAME + "-1.0." + EXT; - } - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--linux-package-deps", DEP_NAME.toLowerCase()}; - CMD_DEP = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", DEP_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerBundleNameTest.java 2019-11-18 21:54:06.175404600 -0500 +++ /dev/null 2019-11-18 21:54:07.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerBundleNameBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerBundleNameTest - */ -public class JPackageCreateInstallerBundleNameTest { - private static final String TEST_NAME = "JPackageCreateInstallerBundleNameTest"; - private static final String EXT = "deb"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerBundleNameBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerFileAssociationsTest.java 2019-11-18 21:54:14.822911100 -0500 +++ /dev/null 2019-11-18 21:54:16.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerFileAssociationsBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest - */ -public class JPackageCreateInstallerFileAssociationsTest { - private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; - private static final String EXT = "deb"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerInstallDirTest.java 2019-11-18 21:54:23.296263400 -0500 +++ /dev/null 2019-11-18 21:54:24.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerInstallDirBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest - */ -public class JPackageCreateInstallerInstallDirTest { - private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest"; - private static final String EXT = "deb"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerLicenseTest.java 2019-11-18 21:54:31.487278300 -0500 +++ /dev/null 2019-11-18 21:54:33.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerLicenseBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest - */ -public class JPackageCreateInstallerLicenseTest { - private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest"; - private static final String EXT = "deb"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerMaintainerTest.java 2019-11-18 21:54:40.099373100 -0500 +++ /dev/null 2019-11-18 21:54:41.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerMaintainerBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerMaintainerTest - */ -public class JPackageCreateInstallerMaintainerTest { - private static final String TEST_NAME = "JPackageCreateInstallerMaintainerTest"; - private static final String EXT = "deb"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerMaintainerBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerPackageDepsTest.java 2019-11-18 21:54:48.711071500 -0500 +++ /dev/null 2019-11-18 21:54:50.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerPackageDepsBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @ignore - * @run main/othervm/timeout=240 -Xmx512m JPackageCreateInstallerPackageDepsTest - */ -public class JPackageCreateInstallerPackageDepsTest { - private static final String TEST_NAME = "JPackageCreateInstallerPackageDepsTest"; - private static final String EXT = "deb"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerPackageDepsBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerTest.java 2019-11-18 21:54:57.312068700 -0500 +++ /dev/null 2019-11-18 21:54:58.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerTest - */ -public class JPackageCreateInstallerTest { - private static final String TEST_NAME = "JPackageCreateInstallerTest"; - private static final String EXT = "deb"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/deb/install.sh 2019-11-18 21:55:05.748376000 -0500 +++ /dev/null 2019-11-18 21:55:07.000000000 -0500 @@ -1,8 +0,0 @@ -sudo dpkg -i jpackagecreateinstallertest-1.0.deb -sudo dpkg -i jpackagecreateinstallerfileassociationstest-1.0.deb -sudo dpkg -i jpackagecreateinstallerlicensetest-1.0.deb -sudo dpkg -i jpackagecreateinstallerinstalldirtest-1.0.deb -sudo dpkg -i jpackage-test-bundle-name-1.0.deb -sudo dpkg -i jpackagecreateinstallermaintainertest-1.0.deb -sudo dpkg -i jpackagecreateinstallerpackagedepstestdep-1.0.deb -sudo dpkg -i jpackagecreateinstallerpackagedepstest-1.0.deb \ No newline at end of file --- old/test/jdk/tools/jpackage/createinstaller/linux/deb/uninstall.sh 2019-11-18 21:55:14.029715700 -0500 +++ /dev/null 2019-11-18 21:55:15.000000000 -0500 @@ -1,8 +0,0 @@ -sudo dpkg -r jpackagecreateinstallertest -sudo dpkg -r jpackagecreateinstallerfileassociationstest -sudo dpkg -r jpackagecreateinstallerlicensetest -sudo dpkg -r jpackagecreateinstallerinstalldirtest -sudo dpkg -r jpackage-test-bundle-name -sudo dpkg -r jpackagecreateinstallermaintainertest -sudo dpkg -r jpackagecreateinstallerpackagedepstest -sudo dpkg -r jpackagecreateinstallerpackagedepstestdep --- old/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerBundleNameTest.java 2019-11-18 21:55:22.404201400 -0500 +++ /dev/null 2019-11-18 21:55:23.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerBundleNameBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateInstallerBundleNameTest - */ -public class JPackageCreateInstallerBundleNameTest { - private static final String TEST_NAME = "JPackageCreateInstallerBundleNameTest"; - private static final String EXT = "rpm"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerBundleNameBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerFileAssociationsTest.java 2019-11-18 21:55:30.870376900 -0500 +++ /dev/null 2019-11-18 21:55:32.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerFileAssociationsBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest - */ -public class JPackageCreateInstallerFileAssociationsTest { - private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; - private static final String EXT = "rpm"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerInstallDirTest.java 2019-11-18 21:55:39.072571400 -0500 +++ /dev/null 2019-11-18 21:55:40.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerInstallDirBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest - */ -public class JPackageCreateInstallerInstallDirTest { - private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest"; - private static final String EXT = "rpm"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerLicenseTest.java 2019-11-18 21:55:47.645709600 -0500 +++ /dev/null 2019-11-18 21:55:49.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerLicenseBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest - */ -public class JPackageCreateInstallerLicenseTest { - private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest"; - private static final String EXT = "rpm"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerLicenseTypeTest.java 2019-11-18 21:55:56.337018400 -0500 +++ /dev/null 2019-11-18 21:55:57.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerLicenseTypeBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTypeTest - */ -public class JPackageCreateInstallerLicenseTypeTest { - private static final String TEST_NAME = "JPackageCreateInstallerLicenseTypeTest"; - private static final String EXT = "rpm"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerLicenseTypeBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerPackageDepsTest.java 2019-11-18 21:56:04.906337900 -0500 +++ /dev/null 2019-11-18 21:56:06.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerPackageDepsBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @run main/othervm/timeout=240 -Xmx512m JPackageCreateInstallerPackageDepsTest - */ -public class JPackageCreateInstallerPackageDepsTest { - private static final String TEST_NAME = "JPackageCreateInstallerPackageDepsTest"; - private static final String EXT = "rpm"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerPackageDepsBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerTest.java 2019-11-18 21:56:13.268275400 -0500 +++ /dev/null 2019-11-18 21:56:14.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerBase - * @requires (os.family == "linux") - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateInstallerTest - */ -public class JPackageCreateInstallerTest { - private static final String TEST_NAME = "JPackageCreateInstallerTest"; - private static final String EXT = "rpm"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/linux/rpm/install.sh 2019-11-18 21:56:21.704722400 -0500 +++ /dev/null 2019-11-18 21:56:23.000000000 -0500 @@ -1,8 +0,0 @@ -sudo rpm --install jpackagecreateinstallerfileassociationstest-1.0-1.x86_64.rpm -sudo rpm --install jpackagecreateinstallerinstalldirtest-1.0-1.x86_64.rpm -sudo rpm --install jpackagecreateinstallerlicensetest-1.0-1.x86_64.rpm -sudo rpm --install jpackagecreateinstallerlicensetypetest-1.0-1.x86_64.rpm -sudo rpm --install jpackagecreateinstallerpackagedepstestdep-1.0-1.x86_64.rpm -sudo rpm --install jpackagecreateinstallerpackagedepstest-1.0-1.x86_64.rpm -sudo rpm --install jpackagecreateinstallertest-1.0-1.x86_64.rpm -sudo rpm --install jpackage-test-bundle-name-1.0-1.x86_64.rpm --- old/test/jdk/tools/jpackage/createinstaller/linux/rpm/uninstall.sh 2019-11-18 21:56:30.170351300 -0500 +++ /dev/null 2019-11-18 21:56:31.000000000 -0500 @@ -1,8 +0,0 @@ -sudo rpm -e jpackagecreateinstallerfileassociationstest -sudo rpm -e jpackagecreateinstallerinstalldirtest -sudo rpm -e jpackagecreateinstallerlicensetest -sudo rpm -e jpackagecreateinstallerlicensetypetest -sudo rpm -e jpackagecreateinstallerpackagedepstest -sudo rpm -e jpackagecreateinstallerpackagedepstestdep -sudo rpm -e jpackagecreateinstallertest -sudo rpm -e jpackage-test-bundle-name --- old/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerBase.java 2019-11-18 21:56:38.754558300 -0500 +++ /dev/null 2019-11-18 21:56:40.000000000 -0500 @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getOSXInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - } - - private static void verifyUnInstall() throws Exception { - // Not needed on OS X, since we just deleting installed application - // without using generated installer. We keeping this for consistnency - // between platforms. - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerFileAssociationsBase.java 2019-11-18 21:56:47.386785100 -0500 +++ /dev/null 2019-11-18 21:56:48.000000000 -0500 @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.Desktop; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerFileAssociationsBase { - - private static String TEST_NAME; - private static String EXT; - private static String TEST_EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void validateAppOutput() throws Exception { - File outFile = new File("appOutput.txt"); - int count = 10; - boolean bOutputCreated = false; - while (count > 0) { - if (!outFile.exists()) { - Thread.sleep(3000); - count--; - } else { - bOutputCreated = true; - break; - } - } - - if (!bOutputCreated) { - throw new AssertionError(outFile.getAbsolutePath() + " was not created"); - } - - String output = Files.readString(outFile.toPath()); - String[] result = output.split("\n"); - if (result.length != 3) { - System.err.println(output); - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: 1")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - - File faFile = new File(TEST_NAME + "." + TEST_EXT); - if (!result[2].trim().equals(faFile.getAbsolutePath())) { - throw new AssertionError("Unexpected result[2]: " + result[2]); - } - } - - private static void verifyInstall() throws Exception { - createFileAssociationsTestFile(); - Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT)); - validateAppOutput(); - } - - private static void verifyUnInstall() throws Exception { - // Not needed on OS X, since we just deleting installed application - // without using generated installer. We keeping this for consistnency - // between platforms. - } - - private static void createFileAssociationsTestFile() throws Exception { - try (PrintWriter out = new PrintWriter(new BufferedWriter( - new FileWriter(TEST_NAME + "." + TEST_EXT)))) { - out.println(TEST_NAME); - } - } - - private static void createFileAssociationsProperties() throws Exception { - try (PrintWriter out = new PrintWriter(new BufferedWriter( - new FileWriter("fa.properties")))) { - out.println("extension=" + TEST_EXT); - out.println("mime-type=application/" + TEST_EXT); - out.println("description=jpackage test extention"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - TEST_EXT = "jptest1"; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--file-associations", "fa.properties"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - createFileAssociationsProperties(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerInstallDirBase.java 2019-11-18 21:56:55.861947200 -0500 +++ /dev/null 2019-11-18 21:56:57.000000000 -0500 @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerInstallDirBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getOSXInstalledApp("jpackage", TEST_NAME); - JPackageInstallerHelper.validateApp(app); - } - - private static void verifyUnInstall() throws Exception { - // Not needed on OS X, since we just deleting installed application - // without using generated installer. We keeping this for consistnency - // between platforms. - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--install-dir", "/Applications/jpackage"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerLicenseBase.java 2019-11-18 21:57:04.320971700 -0500 +++ /dev/null 2019-11-18 21:57:05.000000000 -0500 @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerLicenseBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getOSXInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - } - - private static void verifyUnInstall() throws Exception { - // Not needed on OS X, since we just deleting installed application - // without using generated installer. We keeping this for consistnency - // between platforms. - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String [] { - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--license-file", JPackagePath.getLicenseFilePath()}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerFileAssociationsTest.java 2019-11-18 21:57:12.834363200 -0500 +++ /dev/null 2019-11-18 21:57:14.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerFileAssociationsBase - * @requires (os.family == "mac") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest - */ -public class JPackageCreateInstallerFileAssociationsTest { - private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; - private static final String EXT = "dmg"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerInstallDirTest.java 2019-11-18 21:57:21.445611200 -0500 +++ /dev/null 2019-11-18 21:57:22.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerInstallDirBase - * @requires (os.family == "mac") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest - */ -public class JPackageCreateInstallerInstallDirTest { - private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest"; - private static final String EXT = "dmg"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerLicenseTest.java 2019-11-18 21:57:29.786050400 -0500 +++ /dev/null 2019-11-18 21:57:31.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerLicenseBase - * @requires (os.family == "mac") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest - */ -public class JPackageCreateInstallerLicenseTest { - private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest"; - private static final String EXT = "dmg"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerTest.java 2019-11-18 21:57:38.324408500 -0500 +++ /dev/null 2019-11-18 21:57:39.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerBase - * @requires (os.family == "mac") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerTest - */ -public class JPackageCreateInstallerTest { - private static final String TEST_NAME = "JPackageCreateInstallerTest"; - private static final String EXT = "dmg"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/dmg/install.sh 2019-11-18 21:57:46.662390700 -0500 +++ /dev/null 2019-11-18 21:57:48.000000000 -0500 @@ -1,21 +0,0 @@ -echo "Note: This script will install DMG files silently. In order to verify UI, each .dmg needs to launched manually via Finder." - -# JPackageCreateInstallerTest -hdiutil attach JPackageCreateInstallerTest-1.0.dmg -sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerTest/JPackageCreateInstallerTest-1.0.pkg -target / -hdiutil detach /Volumes/JPackageCreateInstallerTest/ - -# JPackageCreateInstallerLicenseTest -hdiutil attach JPackageCreateInstallerLicenseTest-1.0.dmg -sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerLicenseTest/JPackageCreateInstallerLicenseTest-1.0.pkg -target / -hdiutil detach /Volumes/JPackageCreateInstallerLicenseTest/ - -# JPackageCreateInstallerFileAssociationsTest -hdiutil attach JPackageCreateInstallerFileAssociationsTest-1.0.dmg -sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerFileAssociationsTest/JPackageCreateInstallerFileAssociationsTest-1.0.pkg -target / -hdiutil detach /Volumes/JPackageCreateInstallerFileAssociationsTest/ - -# JPackageCreateInstallerInstallDirTest -hdiutil attach JPackageCreateInstallerInstallDirTest-1.0.dmg -sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerInstallDirTest/JPackageCreateInstallerInstallDirTest-1.0.pkg -target / -hdiutil detach /Volumes/JPackageCreateInstallerInstallDirTest/ --- old/test/jdk/tools/jpackage/createinstaller/macosx/dmg/uninstall.sh 2019-11-18 21:57:55.208601700 -0500 +++ /dev/null 2019-11-18 21:57:56.000000000 -0500 @@ -1,4 +0,0 @@ -sudo rm -rf /Applications/JPackageCreateInstallerTest.app -sudo rm -rf /Applications/JPackageCreateInstallerLicenseTest.app -sudo rm -rf /Applications/JPackageCreateInstallerFileAssociationsTest.app -sudo rm -rf /Applications/jpackage/JPackageCreateInstallerInstallDirTest.app --- old/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerFileAssociationsTest.java 2019-11-18 21:58:03.697899000 -0500 +++ /dev/null 2019-11-18 21:58:05.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerFileAssociationsBase - * @requires (os.family == "mac") - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest - */ -public class JPackageCreateInstallerFileAssociationsTest { - private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; - private static final String EXT = "pkg"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerInstallDirTest.java 2019-11-18 21:58:12.469052500 -0500 +++ /dev/null 2019-11-18 21:58:14.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerInstallDirBase - * @requires (os.family == "mac") - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest - */ -public class JPackageCreateInstallerInstallDirTest { - private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest"; - private static final String EXT = "pkg"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerLicenseTest.java 2019-11-18 21:58:20.873990400 -0500 +++ /dev/null 2019-11-18 21:58:22.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerLicenseBase - * @requires (os.family == "mac") - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest - */ -public class JPackageCreateInstallerLicenseTest { - private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest"; - private static final String EXT = "pkg"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerTest.java 2019-11-18 21:58:29.120311000 -0500 +++ /dev/null 2019-11-18 21:58:30.000000000 -0500 @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerBase - * @requires (os.family == "mac") - * @modules jdk.jpackage - * @run main/othervm -Xmx512m JPackageCreateInstallerTest - */ -public class JPackageCreateInstallerTest { - private static final String TEST_NAME = "JPackageCreateInstallerTest"; - private static final String EXT = "pkg"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/macosx/pkg/install.sh 2019-11-18 21:58:37.755935600 -0500 +++ /dev/null 2019-11-18 21:58:39.000000000 -0500 @@ -1,5 +0,0 @@ -echo Note: This script will install packages silently. In order to verify UI, each .pkg needs to launched manually via Finder. -sudo /usr/sbin/installer -pkg JPackageCreateInstallerTest-1.0.pkg -target / -sudo /usr/sbin/installer -pkg JPackageCreateInstallerLicenseTest-1.0.pkg -target / -sudo /usr/sbin/installer -pkg JPackageCreateInstallerFileAssociationsTest-1.0.pkg -target / -sudo /usr/sbin/installer -pkg JPackageCreateInstallerInstallDirTest-1.0.pkg -target / --- old/test/jdk/tools/jpackage/createinstaller/macosx/pkg/uninstall.sh 2019-11-18 21:58:46.421477200 -0500 +++ /dev/null 2019-11-18 21:58:47.000000000 -0500 @@ -1,4 +0,0 @@ -sudo rm -rf /Applications/JPackageCreateInstallerTest.app -sudo rm -rf /Applications/JPackageCreateInstallerLicenseTest.app -sudo rm -rf /Applications/JPackageCreateInstallerFileAssociationsTest.app -sudo rm -rf /Applications/jpackage/JPackageCreateInstallerInstallDirTest.app --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerBase.java 2019-11-18 21:58:55.068798600 -0500 +++ /dev/null 2019-11-18 21:58:56.000000000 -0500 @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getWinInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false); - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerFileAssociationsBase.java 2019-11-18 21:59:03.435005600 -0500 +++ /dev/null 2019-11-18 21:59:04.000000000 -0500 @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.Desktop; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerFileAssociationsBase { - - private static String TEST_NAME; - private static String EXT; - private static String TEST_EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void validateAppOutput() throws Exception { - File outFile = new File("appOutput.txt"); - int count = 10; - boolean bOutputCreated = false; - while (count > 0) { - if (!outFile.exists()) { - Thread.sleep(3000); - count--; - } else { - bOutputCreated = true; - break; - } - } - - if (!bOutputCreated) { - throw new AssertionError(outFile.getAbsolutePath() + " was not created"); - } - - String output = Files.readString(outFile.toPath()); - String[] result = output.split("\n"); - if (result.length != 3) { - System.err.println(output); - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: 1")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - - File faFile = new File(TEST_NAME + "." + TEST_EXT); - if (!result[2].trim().equals(faFile.getAbsolutePath())) { - throw new AssertionError("Unexpected result[2]: " + result[2]); - } - } - - private static void verifyInstall() throws Exception { - createFileAssociationsTestFile(); - Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT)); - validateAppOutput(); - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true); - - // Validate registry - String[] values1 = {TEST_NAME}; - JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, values1, true); - String[] values2 = {TEST_EXT}; - JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, values2, true); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false); - - // Validate registry - JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, null, false); - JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, null, false); - } - - private static void createFileAssociationsTestFile() throws Exception { - try (PrintWriter out = new PrintWriter(new BufferedWriter( - new FileWriter(TEST_NAME + "." + TEST_EXT)))) { - out.println(TEST_NAME); - } - } - - private static void createFileAssociationsProperties() throws Exception { - try (PrintWriter out = new PrintWriter(new BufferedWriter( - new FileWriter("fa.properties")))) { - out.println("extension=" + TEST_EXT); - out.println("mime-type=application/" + TEST_EXT); - out.println("description=jpackage test extention"); - } - } - - private static void init(String name, String ext, String installDir, String testExt) { - TEST_NAME = name; - EXT = ext; - TEST_EXT = testExt; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - if (installDir == null) { - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--file-associations", "fa.properties"}; - } else { - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--file-associations", "fa.properties", - "--install-dir", installDir}; - } - } - - public static void run(String name, String ext, String installDir, String testExt) throws Exception { - init(name, ext, installDir, testExt); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - createFileAssociationsProperties(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerInstallDirBase.java 2019-11-18 21:59:11.898089000 -0500 +++ /dev/null 2019-11-18 21:59:13.000000000 -0500 @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerInstallDirBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - private static String INSTALL_DIR; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getWinInstalledApp(INSTALL_DIR, TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false); - - // Validate desktop shortcut - JPackageInstallerHelper.validateDesktopShortcut(TEST_NAME, true); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinInstallFolder(INSTALL_DIR); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false); - - // Validate desktop shortcut - JPackageInstallerHelper.validateDesktopShortcut(TEST_NAME, false); - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - INSTALL_DIR = "TestVendor\\JPackageCreateInstallerInstallDirTestDir"; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--install-dir", INSTALL_DIR, - "--win-shortcut"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerLicenseBase.java 2019-11-18 21:59:20.576892800 -0500 +++ /dev/null 2019-11-18 21:59:22.000000000 -0500 @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerLicenseBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getWinInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false); - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String [] { - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--license-file", JPackagePath.getLicenseFilePath()}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinDirChooserBase.java 2019-11-18 21:59:28.928299000 -0500 +++ /dev/null 2019-11-18 21:59:30.000000000 -0500 @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerWinDirChooserBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getWinInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false); - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--win-dir-chooser"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuBase.java 2019-11-18 21:59:37.668673800 -0500 +++ /dev/null 2019-11-18 21:59:38.000000000 -0500 @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerWinMenuBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getWinInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false); - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--win-menu"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuGroupBase.java 2019-11-18 21:59:46.159986200 -0500 +++ /dev/null 2019-11-18 21:59:47.000000000 -0500 @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerWinMenuGroupBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getWinInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - // Validate start menu - JPackageInstallerHelper.validateStartMenu(TEST_NAME, TEST_NAME, true); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - // Validate start menu - JPackageInstallerHelper.validateStartMenu(TEST_NAME, TEST_NAME, false); - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--win-menu", - "--win-menu-group", TEST_NAME}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinPerUserInstallBase.java 2019-11-18 21:59:54.328154600 -0500 +++ /dev/null 2019-11-18 21:59:55.000000000 -0500 @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerWinPerUserInstallBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getWinUserLocalInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - // Validate start menu - JPackageInstallerHelper.validateUserLocalStartMenu(TEST_NAME, TEST_NAME, true); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinUserLocalInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - // Validate start menu - JPackageInstallerHelper.validateUserLocalStartMenu(TEST_NAME, TEST_NAME, false); - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--win-per-user-install", - "--win-menu", - "--win-menu-group", TEST_NAME}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinRegistryNameBase.java 2019-11-18 22:00:02.802616900 -0500 +++ /dev/null 2019-11-18 22:00:04.000000000 -0500 @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.Desktop; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerWinRegistryNameBase { - - private static String TEST_NAME; - private static String EXT; - private static String TEST_EXT; - private static String WIN_REGISTRY_NAME; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void validateAppOutput() throws Exception { - File outFile = new File("appOutput.txt"); - int count = 10; - boolean bOutputCreated = false; - while (count > 0) { - if (!outFile.exists()) { - Thread.sleep(3000); - count--; - } else { - bOutputCreated = true; - break; - } - } - - if (!bOutputCreated) { - throw new AssertionError(outFile.getAbsolutePath() + " was not created"); - } - - String output = Files.readString(outFile.toPath()); - String[] result = output.split("\n"); - if (result.length != 3) { - System.err.println(output); - throw new AssertionError( - "Unexpected number of lines: " + result.length); - } - - if (!result[0].trim().equals("jpackage test application")) { - throw new AssertionError("Unexpected result[0]: " + result[0]); - } - - if (!result[1].trim().equals("args.length: 1")) { - throw new AssertionError("Unexpected result[1]: " + result[1]); - } - - File faFile = new File(TEST_NAME + "." + TEST_EXT); - if (!result[2].trim().equals(faFile.getAbsolutePath())) { - throw new AssertionError("Unexpected result[2]: " + result[2]); - } - } - - private static void verifyInstall() throws Exception { - createFileAssociationsTestFile(); - Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT)); - validateAppOutput(); - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true); - - // Validate registry - String[] values1 = {WIN_REGISTRY_NAME}; - JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, values1, true); - String[] values2 = {TEST_EXT}; - JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, values2, true); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false); - - // Validate registry - JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, null, false); - JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, null, false); - } - - private static void createFileAssociationsTestFile() throws Exception { - try (PrintWriter out = new PrintWriter(new BufferedWriter( - new FileWriter(TEST_NAME + "." + TEST_EXT)))) { - out.println(TEST_NAME); - } - } - - private static void createFileAssociationsProperties() throws Exception { - try (PrintWriter out = new PrintWriter(new BufferedWriter( - new FileWriter("fa.properties")))) { - out.println("extension=" + TEST_EXT); - out.println("mime-type=application/" + TEST_EXT); - out.println("description=jpackage test extention"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - TEST_EXT = "jptest2"; - WIN_REGISTRY_NAME = "JPWINTESTREGISTRYNAME"; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--file-associations", "fa.properties", - "--win-registry-name", WIN_REGISTRY_NAME}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - createFileAssociationsProperties(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinShortcutBase.java 2019-11-18 22:00:11.206184600 -0500 +++ /dev/null 2019-11-18 22:00:12.000000000 -0500 @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerWinShortcutBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT; - private static String[] CMD; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD); - JPackageInstallerHelper.validateOutput(OUTPUT); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getWinInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false); - - // Validate desktop shortcut - JPackageInstallerHelper.validateDesktopShortcut(TEST_NAME, true); - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - - // Validate start menu - JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false); - - // Validate desktop shortcut - JPackageInstallerHelper.validateDesktopShortcut(TEST_NAME, false); - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--win-shortcut"}; - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinUpgradeUUIDBase.java 2019-11-18 22:00:19.822733500 -0500 +++ /dev/null 2019-11-18 22:00:21.000000000 -0500 @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; - -public class JPackageCreateInstallerWinUpgradeUUIDBase { - - private static String TEST_NAME; - private static String EXT; - private static String OUTPUT_1; - private static String[] CMD_1; - private static String OUTPUT_2; - private static String[] CMD_2; - private static final String FILE_1 = "file1.txt"; - private static final String FILE_2 = "file2.txt"; - - private static void copyResults() throws Exception { - List files = new ArrayList<>(); - files.add(OUTPUT_1); - files.add(OUTPUT_2); - JPackageInstallerHelper.copyTestResults(files); - } - - private static void testCreateInstaller() throws Exception { - JPackageHelper.executeCLI(true, CMD_1); - JPackageInstallerHelper.validateOutput(OUTPUT_1); - JPackageHelper.executeCLI(true, CMD_2); - JPackageInstallerHelper.validateOutput(OUTPUT_2); - copyResults(); - } - - private static void verifyInstall() throws Exception { - String app = JPackagePath.getWinInstalledApp(TEST_NAME); - JPackageInstallerHelper.validateApp(app); - - String file1Path = JPackagePath.getWinInstalledAppFolder(TEST_NAME) + File.separator + FILE_1; - File file1 = new File(file1Path); - if (EXT.equals("msi")) { - if (file1.exists()) { - throw new AssertionError("Unexpected file does exist: " - + file1.getAbsolutePath()); - } - } else if (EXT.equals("exe")) { - if (!file1.exists()) { - throw new AssertionError("Unexpected file does not exist: " - + file1.getAbsolutePath()); - } - } else { - throw new AssertionError("Unknown installer type: " + EXT); - } - - String file2Path = JPackagePath.getWinInstalledAppFolder(TEST_NAME) + File.separator + FILE_2; - File file2 = new File(file2Path); - if (!file2.exists()) { - throw new AssertionError("Unexpected file does not exist: " - + file2.getAbsolutePath()); - } - } - - private static void verifyUnInstall() throws Exception { - String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME); - File folder = new File(folderPath); - if (folder.exists()) { - throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist"); - } - } - - private static void init(String name, String ext) { - TEST_NAME = name; - EXT = ext; - OUTPUT_1 = "output" + File.separator + TEST_NAME + "-1.0." + EXT; - CMD_1 = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--app-version", "1.0", - "--win-upgrade-uuid", "F0B18E75-52AD-41A2-BC86-6BE4FCD50BEB"}; - OUTPUT_2 = "output" + File.separator + TEST_NAME + "-2.0." + EXT; - CMD_2 = new String[]{ - "create-installer", - "--installer-type", EXT, - "--input", "input", - "--output", "output", - "--name", TEST_NAME, - "--main-jar", "hello.jar", - "--main-class", "Hello", - "--app-version", "2.0", - "--win-upgrade-uuid", "F0B18E75-52AD-41A2-BC86-6BE4FCD50BEB"}; - } - - private static void createInputFile(String name, String context) throws Exception { - try (PrintWriter out = new PrintWriter( - new BufferedWriter(new FileWriter("input" + File.separator + name)))) { - out.println(context); - } - } - - public static void run(String name, String ext) throws Exception { - init(name, ext); - - if (JPackageInstallerHelper.isVerifyInstall()) { - verifyInstall(); - } else if (JPackageInstallerHelper.isVerifyUnInstall()) { - verifyUnInstall(); - } else { - JPackageHelper.createHelloInstallerJar(); - createInputFile(FILE_1, FILE_1); - createInputFile(FILE_2, FILE_2); - testCreateInstaller(); - } - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerFileAssociationsInstallDirTest.java 2019-11-18 22:00:28.526936900 -0500 +++ /dev/null 2019-11-18 22:00:30.000000000 -0500 @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerFileAssociationsBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsInstallDirTest - */ -public class JPackageCreateInstallerFileAssociationsInstallDirTest { - private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsInstallDirTest"; - private static final String EXT = "exe"; - private static final String INSTALL_DIR - = "TestVendor\\JPackageCreateInstallerFileAssociationsInstallDirTestDir"; - private static final String TEST_EXT = "jptest3"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT, INSTALL_DIR, TEST_EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerFileAssociationsTest.java 2019-11-18 22:00:36.994649700 -0500 +++ /dev/null 2019-11-18 22:00:38.000000000 -0500 @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerFileAssociationsBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest - */ -public class JPackageCreateInstallerFileAssociationsTest { - private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; - private static final String EXT = "exe"; - private static final String TEST_EXT = "jptest1"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT, null, TEST_EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerInstallDirTest.java 2019-11-18 22:00:45.527806900 -0500 +++ /dev/null 2019-11-18 22:00:47.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer install dir test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerInstallDirBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest - */ -public class JPackageCreateInstallerInstallDirTest { - private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest"; - private static final String EXT = "exe"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerLicenseTest.java 2019-11-18 22:00:54.093094100 -0500 +++ /dev/null 2019-11-18 22:00:55.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerLicenseBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest - */ -public class JPackageCreateInstallerLicenseTest { - private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest"; - private static final String EXT = "exe"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerTest.java 2019-11-18 22:01:02.553523800 -0500 +++ /dev/null 2019-11-18 22:01:04.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerTest - */ -public class JPackageCreateInstallerTest { - private static final String TEST_NAME = "JPackageCreateInstallerTest"; - private static final String EXT = "exe"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinDirChooserTest.java 2019-11-18 22:01:11.124917600 -0500 +++ /dev/null 2019-11-18 22:01:12.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinDirChooserBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinDirChooserTest - */ -public class JPackageCreateInstallerWinDirChooserTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinDirChooserTest"; - private static final String EXT = "exe"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinDirChooserBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuGroupTest.java 2019-11-18 22:01:19.886944200 -0500 +++ /dev/null 2019-11-18 22:01:21.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinMenuGroupBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuGroupTest - */ -public class JPackageCreateInstallerWinMenuGroupTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinMenuGroupTest"; - private static final String EXT = "exe"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinMenuGroupBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuTest.java 2019-11-18 22:01:28.535811100 -0500 +++ /dev/null 2019-11-18 22:01:30.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinMenuBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuTest - */ -public class JPackageCreateInstallerWinMenuTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinMenuTest"; - private static final String EXT = "exe"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinMenuBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinPerUserInstallTest.java 2019-11-18 22:01:37.062494000 -0500 +++ /dev/null 2019-11-18 22:01:38.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinPerUserInstallBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinPerUserInstallTest - */ -public class JPackageCreateInstallerWinPerUserInstallTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinPerUserInstallTest"; - private static final String EXT = "exe"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinPerUserInstallBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinRegistryNameTest.java 2019-11-18 22:01:45.633206800 -0500 +++ /dev/null 2019-11-18 22:01:46.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinRegistryNameBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinRegistryNameTest - */ -public class JPackageCreateInstallerWinRegistryNameTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinRegistryNameTest"; - private static final String EXT = "exe"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinRegistryNameBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinShortcutTest.java 2019-11-18 22:01:53.915007900 -0500 +++ /dev/null 2019-11-18 22:01:55.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinShortcutBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinShortcutTest - */ -public class JPackageCreateInstallerWinShortcutTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinShortcutTest"; - private static final String EXT = "exe"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinShortcutBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinUpgradeUUIDTest.java 2019-11-18 22:02:02.250047500 -0500 +++ /dev/null 2019-11-18 22:02:03.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinUpgradeUUIDBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinUpgradeUUIDTest - */ -public class JPackageCreateInstallerWinUpgradeUUIDTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinUpgradeUUIDTest"; - private static final String EXT = "exe"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinUpgradeUUIDBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/install.bat 2019-11-18 22:02:10.872832800 -0500 +++ /dev/null 2019-11-18 22:02:12.000000000 -0500 @@ -1,16 +0,0 @@ -JPackageCreateInstallerTest-1.0.exe -JPackageCreateInstallerWinMenuTest-1.0.exe -JPackageCreateInstallerWinMenuGroupTest-1.0.exe -JPackageCreateInstallerWinPerUserInstallTest-1.0.exe -JPackageCreateInstallerFileAssociationsTest-1.0.exe -ECHO Make sure that installer asks to select installation location. Install to default one. -JPackageCreateInstallerWinDirChooserTest-1.0.exe -JPackageCreateInstallerWinRegistryNameTest-1.0.exe -JPackageCreateInstallerWinShortcutTest-1.0.exe -ECHO Make sure that installer shows license -JPackageCreateInstallerLicenseTest-1.0.exe -JPackageCreateInstallerWinUpgradeUUIDTest-1.0.exe -JPackageCreateInstallerWinUpgradeUUIDTest-2.0.exe -JPackageCreateInstallerInstallDirTest-1.0.exe -JPackageCreateInstallerFileAssociationsInstallDirTest-1.0.exe -PAUSE --- old/test/jdk/tools/jpackage/createinstaller/windows/exe/uninstall.bat 2019-11-18 22:02:19.228343900 -0500 +++ /dev/null 2019-11-18 22:02:20.000000000 -0500 @@ -1,13 +0,0 @@ -"%ProgramFiles%\JPackageCreateInstallerTest\unins000.exe" -"%ProgramFiles%\JPackageCreateInstallerWinMenuTest\unins000.exe" -"%ProgramFiles%\JPackageCreateInstallerWinMenuGroupTest\unins000.exe" -"%localappdata%\JPackageCreateInstallerWinPerUserInstallTest\unins000.exe" -"%ProgramFiles%\JPackageCreateInstallerFileAssociationsTest\unins000.exe" -"%ProgramFiles%\JPackageCreateInstallerWinDirChooserTest\unins000.exe" -"%ProgramFiles%\JPackageCreateInstallerWinRegistryNameTest\unins000.exe" -"%ProgramFiles%\JPackageCreateInstallerWinShortcutTest\unins000.exe" -"%ProgramFiles%\JPackageCreateInstallerLicenseTest\unins000.exe" -"%ProgramFiles%\JPackageCreateInstallerWinUpgradeUUIDTest\unins000.exe" -"%ProgramFiles%\TestVendor\JPackageCreateInstallerInstallDirTestDir\unins000.exe" -"%ProgramFiles%\TestVendor\JPackageCreateInstallerFileAssociationsInstallDirTestDir\unins000.exe" -PAUSE \ No newline at end of file --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerFileAssociationsInstallDirTest.java 2019-11-18 22:02:27.848668000 -0500 +++ /dev/null 2019-11-18 22:02:29.000000000 -0500 @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerFileAssociationsBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsInstallDirTest - */ -public class JPackageCreateInstallerFileAssociationsInstallDirTest { - private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsInstallDirTest"; - private static final String EXT = "msi"; - private static final String INSTALL_DIR - = "TestVendor\\JPackageCreateInstallerFileAssociationsInstallDirTestDir"; - private static final String TEST_EXT = "jptest3"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT, INSTALL_DIR, TEST_EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerFileAssociationsTest.java 2019-11-18 22:02:36.334131400 -0500 +++ /dev/null 2019-11-18 22:02:37.000000000 -0500 @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerFileAssociationsBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest - */ -public class JPackageCreateInstallerFileAssociationsTest { - private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; - private static final String EXT = "msi"; - private static final String TEST_EXT = "jptest1"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT, null, TEST_EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerInstallDirTest.java 2019-11-18 22:02:44.876836200 -0500 +++ /dev/null 2019-11-18 22:02:46.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer install dir test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerInstallDirBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest - */ -public class JPackageCreateInstallerInstallDirTest { - private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest"; - private static final String EXT = "msi"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerLicenseTest.java 2019-11-18 22:02:53.435999400 -0500 +++ /dev/null 2019-11-18 22:02:54.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerLicenseBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest - */ -public class JPackageCreateInstallerLicenseTest { - private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest"; - private static final String EXT = "msi"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerTest.java 2019-11-18 22:03:02.296712900 -0500 +++ /dev/null 2019-11-18 22:03:03.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerTest - */ -public class JPackageCreateInstallerTest { - private static final String TEST_NAME = "JPackageCreateInstallerTest"; - private static final String EXT = "msi"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinDirChooserTest.java 2019-11-18 22:03:10.532314300 -0500 +++ /dev/null 2019-11-18 22:03:11.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinDirChooserBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinDirChooserTest - */ -public class JPackageCreateInstallerWinDirChooserTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinDirChooserTest"; - private static final String EXT = "msi"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinDirChooserBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuGroupTest.java 2019-11-18 22:03:19.092670700 -0500 +++ /dev/null 2019-11-18 22:03:20.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinMenuGroupBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuGroupTest - */ -public class JPackageCreateInstallerWinMenuGroupTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinMenuGroupTest"; - private static final String EXT = "msi"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinMenuGroupBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuTest.java 2019-11-18 22:03:27.683218000 -0500 +++ /dev/null 2019-11-18 22:03:29.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinMenuBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuTest - */ -public class JPackageCreateInstallerWinMenuTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinMenuTest"; - private static final String EXT = "msi"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinMenuBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinPerUserInstallTest.java 2019-11-18 22:03:36.107727000 -0500 +++ /dev/null 2019-11-18 22:03:37.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinPerUserInstallBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinPerUserInstallTest - */ -public class JPackageCreateInstallerWinPerUserInstallTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinPerUserInstallTest"; - private static final String EXT = "msi"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinPerUserInstallBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinRegistryNameTest.java 2019-11-18 22:03:44.653419700 -0500 +++ /dev/null 2019-11-18 22:03:46.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinRegistryNameBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinRegistryNameTest - */ -public class JPackageCreateInstallerWinRegistryNameTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinRegistryNameTest"; - private static final String EXT = "msi"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinRegistryNameBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinShortcutTest.java 2019-11-18 22:03:53.348618300 -0500 +++ /dev/null 2019-11-18 22:03:54.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinShortcutBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinShortcutTest - */ -public class JPackageCreateInstallerWinShortcutTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinShortcutTest"; - private static final String EXT = "msi"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinShortcutBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinUpgradeUUIDTest.java 2019-11-18 22:04:01.838941900 -0500 +++ /dev/null 2019-11-18 22:04:03.000000000 -0500 @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary jpackage create installer test - * @library ../../../helpers - * @library ../base - * @build JPackageHelper - * @build JPackagePath - * @build JPackageInstallerHelper - * @build JPackageCreateInstallerWinUpgradeUUIDBase - * @requires (os.family == "windows") - * @modules jdk.jpackage - * @ignore - * @run main/othervm -Xmx512m JPackageCreateInstallerWinUpgradeUUIDTest - */ -public class JPackageCreateInstallerWinUpgradeUUIDTest { - private static final String TEST_NAME = "JPackageCreateInstallerWinUpgradeUUIDTest"; - private static final String EXT = "msi"; - - public static void main(String[] args) throws Exception { - JPackageCreateInstallerWinUpgradeUUIDBase.run(TEST_NAME, EXT); - } -} --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/install.bat 2019-11-18 22:04:10.536006300 -0500 +++ /dev/null 2019-11-18 22:04:11.000000000 -0500 @@ -1,16 +0,0 @@ -JPackageCreateInstallerTest-1.0.msi -JPackageCreateInstallerWinMenuTest-1.0.msi -JPackageCreateInstallerWinMenuGroupTest-1.0.msi -JPackageCreateInstallerWinPerUserInstallTest-1.0.msi -JPackageCreateInstallerFileAssociationsTest-1.0.msi -ECHO Make sure that installer asks to select installation location. Install to default one. -JPackageCreateInstallerWinDirChooserTest-1.0.msi -JPackageCreateInstallerWinRegistryNameTest-1.0.msi -JPackageCreateInstallerWinShortcutTest-1.0.msi -ECHO Make sure that installer shows license -JPackageCreateInstallerLicenseTest-1.0.msi -JPackageCreateInstallerWinUpgradeUUIDTest-1.0.msi -JPackageCreateInstallerWinUpgradeUUIDTest-2.0.msi -JPackageCreateInstallerInstallDirTest-1.0.msi -JPackageCreateInstallerFileAssociationsInstallDirTest-1.0.msi -PAUSE --- old/test/jdk/tools/jpackage/createinstaller/windows/msi/uninstall.bat 2019-11-18 22:04:18.760675300 -0500 +++ /dev/null 2019-11-18 22:04:20.000000000 -0500 @@ -1,13 +0,0 @@ -MSIEXEC /uninstall JPackageCreateInstallerTest-1.0.msi -MSIEXEC /uninstall JPackageCreateInstallerWinMenuTest-1.0.msi -MSIEXEC /uninstall JPackageCreateInstallerWinMenuGroupTest-1.0.msi -MSIEXEC /uninstall JPackageCreateInstallerWinPerUserInstallTest-1.0.msi -MSIEXEC /uninstall JPackageCreateInstallerFileAssociationsTest-1.0.msi -MSIEXEC /uninstall JPackageCreateInstallerWinDirChooserTest-1.0.msi -MSIEXEC /uninstall JPackageCreateInstallerWinRegistryNameTest-1.0.msi -MSIEXEC /uninstall JPackageCreateInstallerWinShortcutTest-1.0.msi -MSIEXEC /uninstall JPackageCreateInstallerLicenseTest-1.0.msi -MSIEXEC /uninstall JPackageCreateInstallerWinUpgradeUUIDTest-2.0.msi -MSIEXEC /uninstall JPackageCreateInstallerInstallDirTest-1.0.msi -MSIEXEC /uninstall JPackageCreateInstallerFileAssociationsInstallDirTest-1.0.msi -PAUSE \ No newline at end of file --- old/test/jdk/tools/jpackage/jdk/jpackage/internal/DeployParamsTest.java 2019-11-18 22:04:27.566845700 -0500 +++ /dev/null 2019-11-18 22:04:29.000000000 -0500 @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import jdk.jpackage.internal.Arguments; -import jdk.jpackage.internal.DeployParams; -import jdk.jpackage.internal.PackagerException; -import java.io.File; - -/* - * @test - * @bug 8211285 - * @summary DeployParamsTest - * @modules jdk.jpackage - * @modules jdk.jpackage/jdk.jpackage.internal - * @run main/othervm -Xmx512m DeployParamsTest - */ -public class DeployParamsTest { - - private static File testRoot = null; - - private static void setUp() { - testRoot = new File("deployParamsTest"); - System.out.println("DeployParamsTest: " + testRoot.getAbsolutePath()); - testRoot.mkdir(); - } - - private static void tearDown() { - if (testRoot != null) { - testRoot.delete(); - } - } - - private static void testValidateAppName1() throws Exception { - DeployParams params = getParamsAppName(); - - setAppName(params, "Test"); - params.validate(); - - setAppName(params, "Test Name"); - params.validate(); - - setAppName(params, "Test - Name !!!"); - params.validate(); - } - - private static void testValidateAppName2() throws Exception { - DeployParams params = getParamsAppName(); - - setAppName(params, "Test\nName"); - appName2TestHelper(params); - - setAppName(params, "Test\rName"); - appName2TestHelper(params); - - setAppName(params, "TestName\\"); - appName2TestHelper(params); - - setAppName(params, "Test \" Name"); - appName2TestHelper(params); - } - - private static void appName2TestHelper(DeployParams params) throws Exception { - try { - params.validate(); - } catch (PackagerException pe) { - if (!pe.getMessage().startsWith("Error: Invalid Application name")) { - throw new Exception("Unexpected PackagerException received: " + pe); - } - - return; // Done - } - - throw new Exception("Expecting PackagerException"); - } - - // Returns deploy params initialized to pass all validation, except for - // app name - private static DeployParams getParamsAppName() { - DeployParams params = new DeployParams(); - - params.setOutput(testRoot); - params.addResource(testRoot, new File(testRoot, "test.jar")); - params.addBundleArgument(Arguments.CLIOptions.APPCLASS.getId(), "TestClass"); - params.addBundleArgument(Arguments.CLIOptions.MAIN_JAR.getId(), "test.jar"); - - return params; - } - - private static void setAppName(DeployParams params, String appName) { - params.addBundleArgument(Arguments.CLIOptions.NAME.getId(), appName); - } - - public static void main(String[] args) throws Exception { - setUp(); - - try { - testValidateAppName1(); - testValidateAppName2(); - } finally { - tearDown(); - } - } - -}