--- old/make/CompileJavaModules.gmk 2019-05-02 13:41:22.844696500 -0400 +++ new/make/CompileJavaModules.gmk 2019-05-02 13:41:20.776832100 -0400 @@ -380,6 +380,15 @@ ################################################################################ +jdk.jpackage_ADD_JAVAC_FLAGS += -parameters -XDstringConcat=inline + +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.jconsole_COPY += .gif .png jdk.jconsole_CLEAN_FILES += $(wildcard \ --- old/make/common/Modules.gmk 2019-05-02 13:41:39.835580900 -0400 +++ new/make/common/Modules.gmk 2019-05-02 13:41:37.776341300 -0400 @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. +# 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 @@ -128,6 +128,7 @@ JRE_TOOL_MODULES += \ jdk.jdwp.agent \ jdk.pack \ + jdk.jpackage \ jdk.scripting.nashorn.shell \ # @@ -168,6 +169,7 @@ jdk.naming.rmi \ jdk.net \ jdk.pack \ + jdk.jpackage \ jdk.rmic \ jdk.scripting.nashorn \ jdk.sctp \ @@ -227,6 +229,13 @@ endif ################################################################################ +# jpackage is only on windows, macosx, and linux + +ifeq ($(filter $(OPENJDK_TARGET_OS), windows macosx linux), ) + MODULES_FILTER += jdk.jpackage +endif + +################################################################################ # Module list macros # Use append so that the custom extension may add to these variables --- old/make/common/NativeCompilation.gmk 2019-05-02 13:41:56.841849200 -0400 +++ new/make/common/NativeCompilation.gmk 2019-05-02 13:41:54.735808700 -0400 @@ -397,6 +397,7 @@ # ARFLAGS the archiver flags to be used # OBJECT_DIR the directory where we store the object files # OUTPUT_DIR the directory where the resulting binary is put +# SYMBOLS_DIR the directory where the debug symbols are put, defaults to OUTPUT_DIR # INCLUDES only pick source from these directories # EXCLUDES do not pick source from these directories # INCLUDE_FILES only compile exactly these files! @@ -511,8 +512,6 @@ $$(call SetIfEmpty, $1_SYSROOT_CFLAGS, $$($$($1_TOOLCHAIN)_SYSROOT_CFLAGS)) $$(call SetIfEmpty, $1_SYSROOT_LDFLAGS, $$($$($1_TOOLCHAIN)_SYSROOT_LDFLAGS)) - # Make sure the dirs exist. - $$(call MakeDir, $$($1_OBJECT_DIR) $$($1_OUTPUT_DIR)) $$(foreach d, $$($1_SRC), $$(if $$(wildcard $$d), , \ $$(error SRC specified to SetupNativeCompilation $1 contains missing directory $$d))) @@ -883,30 +882,31 @@ ifeq ($$($1_COPY_DEBUG_SYMBOLS), true) ifneq ($$($1_DEBUG_SYMBOLS), false) + $$(call SetIfEmpty, $1_SYMBOLS_DIR, $$($1_OUTPUT_DIR)) # Only copy debug symbols for dynamic libraries and programs. ifneq ($$($1_TYPE), STATIC_LIBRARY) # Generate debuginfo files. ifeq ($(call isTargetOs, windows), true) - $1_EXTRA_LDFLAGS += -debug "-pdb:$$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).pdb" \ - "-map:$$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).map" - $1_DEBUGINFO_FILES := $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).pdb \ - $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).map + $1_EXTRA_LDFLAGS += -debug "-pdb:$$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).pdb" \ + "-map:$$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).map" + $1_DEBUGINFO_FILES := $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).pdb \ + $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).map else ifeq ($(call isTargetOs, linux solaris), true) - $1_DEBUGINFO_FILES := $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).debuginfo + $1_DEBUGINFO_FILES := $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).debuginfo # Setup the command line creating debuginfo files, to be run after linking. # It cannot be run separately since it updates the original target file $1_CREATE_DEBUGINFO_CMDS := \ $$($1_OBJCOPY) --only-keep-debug $$($1_TARGET) $$($1_DEBUGINFO_FILES) $$(NEWLINE) \ - $(CD) $$($1_OUTPUT_DIR) && \ + $(CD) $$($1_SYMBOLS_DIR) && \ $$($1_OBJCOPY) --add-gnu-debuglink=$$($1_DEBUGINFO_FILES) $$($1_TARGET) else ifeq ($(call isTargetOs, macosx), true) $1_DEBUGINFO_FILES := \ - $$($1_OUTPUT_DIR)/$$($1_BASENAME).dSYM/Contents/Info.plist \ - $$($1_OUTPUT_DIR)/$$($1_BASENAME).dSYM/Contents/Resources/DWARF/$$($1_BASENAME) + $$($1_SYMBOLS_DIR)/$$($1_BASENAME).dSYM/Contents/Info.plist \ + $$($1_SYMBOLS_DIR)/$$($1_BASENAME).dSYM/Contents/Resources/DWARF/$$($1_BASENAME) $1_CREATE_DEBUGINFO_CMDS := \ - $(DSYMUTIL) --out $$($1_OUTPUT_DIR)/$$($1_BASENAME).dSYM $$($1_TARGET) + $(DSYMUTIL) --out $$($1_SYMBOLS_DIR)/$$($1_BASENAME).dSYM $$($1_TARGET) endif # Since the link rule creates more than one file that we want to track, @@ -928,14 +928,14 @@ $1 += $$($1_DEBUGINFO_FILES) ifeq ($$($1_ZIP_EXTERNAL_DEBUG_SYMBOLS), true) - $1_DEBUGINFO_ZIP := $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).diz + $1_DEBUGINFO_ZIP := $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).diz $1 += $$($1_DEBUGINFO_ZIP) # The dependency on TARGET is needed for debuginfo files # to be rebuilt properly. $$($1_DEBUGINFO_ZIP): $$($1_DEBUGINFO_FILES) $$($1_TARGET) - $(CD) $$($1_OUTPUT_DIR) && \ - $(ZIPEXE) -q -r $$@ $$(subst $$($1_OUTPUT_DIR)/,, $$($1_DEBUGINFO_FILES)) + $(CD) $$($1_SYMBOLS_DIR) && \ + $(ZIPEXE) -q -r $$@ $$(subst $$($1_SYMBOLS_DIR)/,, $$($1_DEBUGINFO_FILES)) endif endif # !STATIC_LIBRARY @@ -971,6 +971,7 @@ $$($1_TARGET): $$($1_TARGET_DEPS) $$(call LogInfo, Building static library $$($1_BASENAME)) + $$(call MakeDir, $$($1_OUTPUT_DIR) $$($1_SYMBOLS_DIR)) $$(call ExecuteWithLog, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, \ $$($1_AR) $$($1_ARFLAGS) $(AR_OUT_OPTION)$$($1_TARGET) $$($1_ALL_OBJS) \ $$($1_RES)) @@ -1072,7 +1073,9 @@ # Keep as much as possible on one execution line for best performance # on Windows $$(call LogInfo, Linking $$($1_BASENAME)) + $$(call MakeDir, $$($1_OUTPUT_DIR) $$($1_SYMBOLS_DIR)) ifeq ($(call isTargetOs, windows), true) + $$(call ExecuteWithLog, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, \ $$($1_LD) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $$($1_SYSROOT_LDFLAGS) \ $(LD_OUT_OPTION)$$($1_TARGET) $$($1_LD_OBJ_ARG) $$($1_RES) $$(GLOBAL_LIBS) \ --- old/test/jdk/tools/launcher/HelpFlagsTest.java 2019-05-02 13:42:13.834567800 -0400 +++ new/test/jdk/tools/launcher/HelpFlagsTest.java 2019-05-02 13:42:11.715526000 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -156,13 +156,12 @@ new ToolHelpSpec("jstatd", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help new ToolHelpSpec("keytool", 1, 1, 1, 0, 1, 0, 1), // none, prints help message anyways. new ToolHelpSpec("pack200", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented. - new ToolHelpSpec("rmic", 0, 0, 0, 0, 0, 0, 1), // none, pirnts help message anyways. + new ToolHelpSpec("rmic", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways. new ToolHelpSpec("rmid", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways. 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. - // Oracle proprietary tools: - new ToolHelpSpec("javapackager",0, 0, 0, 0, 1, 0, 255), // -help accepted but not documented. + new ToolHelpSpec("jpackage", 0, 1, 1, 0, 0, 1, 255), // -h, --help, }; // Returns true if the file is not a tool. --- old/test/jdk/tools/launcher/VersionCheck.java 2019-05-02 13:42:30.892763500 -0400 +++ new/test/jdk/tools/launcher/VersionCheck.java 2019-05-02 13:42:28.811502800 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -62,7 +62,7 @@ "jmc", "jmc.ini", "jweblauncher", - "packager", + "jpackage", "ssvagent", "unpack200", }; @@ -108,7 +108,7 @@ "klist", "ktab", "pack200", - "packager", + "jpackage", "rmic", "rmid", "rmiregistry", --- /dev/null 2019-05-02 13:42:49.000000000 -0400 +++ new/make/launcher/Launcher-jdk.jpackage.gmk 2019-05-02 13:42:45.548042400 -0400 @@ -0,0 +1,78 @@ +# +# 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 + --- /dev/null 2019-05-02 13:43:01.000000000 -0400 +++ new/make/lib/Lib-jdk.jpackage.gmk 2019-05-02 13:42:57.397179300 -0400 @@ -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. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please 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-05-02 13:43:13.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java 2019-05-02 13:43:09.518528300 -0400 @@ -0,0 +1,205 @@ +/* + * 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-05-02 13:43:25.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java 2019-05-02 13:43:21.517909200 -0400 @@ -0,0 +1,248 @@ +/* + * 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); + } + } + } + +} --- /dev/null 2019-05-02 13:43:37.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java 2019-05-02 13:43:33.535680600 -0400 @@ -0,0 +1,880 @@ +/* + * 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; + } + } +} --- /dev/null 2019-05-02 13:43:49.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java 2019-05-02 13:43:45.472870400 -0400 @@ -0,0 +1,725 @@ +/* + * 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; + } + } +} --- /dev/null 2019-05-02 13:44:01.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties 2019-05-02 13:43:57.530053300 -0400 @@ -0,0 +1,78 @@ +# +# 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. --- /dev/null 2019-05-02 13:44:13.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties 2019-05-02 13:44:09.512611100 -0400 @@ -0,0 +1,78 @@ +# +# 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. --- /dev/null 2019-05-02 13:44:24.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties 2019-05-02 13:44:21.380960000 -0400 @@ -0,0 +1,78 @@ +# +# 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. --- /dev/null 2019-05-02 13:44:36.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.control 2019-05-02 13:44:33.229517000 -0400 @@ -0,0 +1,10 @@ +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-05-02 13:44:48.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.copyright 2019-05-02 13:44:45.158878300 -0400 @@ -0,0 +1,8 @@ + +Copyright: + + APPLICATION_COPYRIGHT + +License: + + APPLICATION_LICENSE_TEXT --- /dev/null 2019-05-02 13:45:00.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop 2019-05-02 13:44:57.071065600 -0400 @@ -0,0 +1,9 @@ +[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 --- /dev/null 2019-05-02 13:45:12.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postinst 2019-05-02 13:45:09.025009200 -0400 @@ -0,0 +1,61 @@ +#!/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 --- /dev/null 2019-05-02 13:45:24.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postrm 2019-05-02 13:45:21.016000600 -0400 @@ -0,0 +1,44 @@ +#!/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-05-02 13:45:36.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst 2019-05-02 13:45:32.953911100 -0400 @@ -0,0 +1,35 @@ +#!/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-05-02 13:45:48.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm 2019-05-02 13:45:44.941494600 -0400 @@ -0,0 +1,45 @@ +#!/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-05-02 13:46:00.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec 2019-05-02 13:45:56.916854200 -0400 @@ -0,0 +1,60 @@ +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 --- /dev/null 2019-05-02 13:46:12.000000000 -0400 +++ new/src/jdk.jpackage/linux/classes/module-info.java.extra 2019-05-02 13:46:08.860878700 -0400 @@ -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.jpackage.internal.Bundler with + jdk.jpackage.internal.LinuxAppBundler, + jdk.jpackage.internal.LinuxDebBundler, + jdk.jpackage.internal.LinuxRpmBundler; + --- /dev/null 2019-05-02 13:46:24.000000000 -0400 +++ new/src/jdk.jpackage/linux/native/jpackageapplauncher/launcher.cpp 2019-05-02 13:46:20.743184900 -0400 @@ -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 += "/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-05-02 13:46:36.000000000 -0400 +++ new/src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.cpp 2019-05-02 13:46:32.606865300 -0400 @@ -0,0 +1,1083 @@ +/* + * 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-05-02 13:46:48.000000000 -0400 +++ new/src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.h 2019-05-02 13:46:44.532743100 -0400 @@ -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. + */ + +#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-05-02 13:47:00.000000000 -0400 +++ new/src/jdk.jpackage/linux/native/libapplauncher/PlatformDefs.h 2019-05-02 13:46:56.422636200 -0400 @@ -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 --- /dev/null 2019-05-02 13:47:11.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java 2019-05-02 13:47:08.324580900 -0400 @@ -0,0 +1,382 @@ +/* + * 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-05-02 13:47:23.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java 2019-05-02 13:47:20.284888600 -0400 @@ -0,0 +1,957 @@ +/* + * 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-05-02 13:47:35.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java 2019-05-02 13:47:32.195357300 -0400 @@ -0,0 +1,368 @@ +/* + * 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 + } +} --- /dev/null 2019-05-02 13:47:47.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java 2019-05-02 13:47:44.037985200 -0400 @@ -0,0 +1,212 @@ +/* + * 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-05-02 13:47:59.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificate.java 2019-05-02 13:47:55.835812400 -0400 @@ -0,0 +1,163 @@ +/* + * 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-05-02 13:48:11.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java 2019-05-02 13:48:07.791642800 -0400 @@ -0,0 +1,483 @@ +/* + * 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; + } +} --- /dev/null 2019-05-02 13:48:23.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java 2019-05-02 13:48:19.597470800 -0400 @@ -0,0 +1,579 @@ +/* + * 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-05-02 13:48:35.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/DMGsetup.scpt 2019-05-02 13:48:31.442098900 -0400 @@ -0,0 +1,15 @@ +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 + --- /dev/null 2019-05-02 13:48:46.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info-lite.plist.template 2019-05-02 13:48:43.304728800 -0400 @@ -0,0 +1,38 @@ + + + + + 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-05-02 13:48:58.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info.plist.template 2019-05-02 13:48:55.118156300 -0400 @@ -0,0 +1,54 @@ + + + + + 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 + + --- /dev/null 2019-05-02 13:49:10.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore.entitlements 2019-05-02 13:49:07.075986900 -0400 @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + --- /dev/null 2019-05-02 13:49:22.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore_Inherit.entitlements 2019-05-02 13:49:18.843613600 -0400 @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + + --- /dev/null 2019-05-02 13:49:34.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties 2019-05-02 13:49:30.659041300 -0400 @@ -0,0 +1,104 @@ +# +# 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-05-02 13:49:46.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties 2019-05-02 13:49:42.554470700 -0400 @@ -0,0 +1,104 @@ +# +# 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-05-02 13:49:57.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties 2019-05-02 13:49:54.335697500 -0400 @@ -0,0 +1,104 @@ +# +# 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-05-02 13:50:09.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Runtime-Info.plist.template 2019-05-02 13:50:06.151125200 -0400 @@ -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 + + --- /dev/null 2019-05-02 13:50:21.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/launchd.plist.template 2019-05-02 13:50:18.026953900 -0400 @@ -0,0 +1,14 @@ + + + + + Label + DEPLOY_DAEMON_IDENTIFIER + ProgramArguments + + DEPLOY_DAEMON_LAUNCHER_PATH + + RunAtLoad + KeepAlive + + --- /dev/null 2019-05-02 13:50:33.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist 2019-05-02 13:50:29.810180900 -0400 @@ -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 + + + + --- /dev/null 2019-05-02 13:50:45.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/postinstall.template 2019-05-02 13:50:41.753411300 -0400 @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +chown root:wheel "INSTALL_LOCATION" +chmod a+rX "INSTALL_LOCATION" + +exit 0 --- /dev/null 2019-05-02 13:50:57.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/preinstall.template 2019-05-02 13:50:53.604842600 -0400 @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +if [ ! -d "INSTALL_LOCATION" ] +then + mkdir -p "INSTALL_LOCATION" +fi + +exit 0 --- /dev/null 2019-05-02 13:51:09.000000000 -0400 +++ new/src/jdk.jpackage/macosx/classes/module-info.java.extra 2019-05-02 13:51:05.400669600 -0400 @@ -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.jpackage.internal.Bundler with + jdk.jpackage.internal.MacAppBundler, + jdk.jpackage.internal.MacAppStoreBundler, + jdk.jpackage.internal.MacDmgBundler, + jdk.jpackage.internal.MacPkgBundler; + --- /dev/null 2019-05-02 13:51:20.000000000 -0400 +++ new/src/jdk.jpackage/macosx/native/jpackageapplauncher/main.m 2019-05-02 13:51:17.249298100 -0400 @@ -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; +} --- /dev/null 2019-05-02 13:51:32.000000000 -0400 +++ new/src/jdk.jpackage/macosx/native/libapplauncher/MacPlatform.h 2019-05-02 13:51:29.138726900 -0400 @@ -0,0 +1,72 @@ +/* + * 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-05-02 13:51:44.000000000 -0400 +++ new/src/jdk.jpackage/macosx/native/libapplauncher/MacPlatform.mm 2019-05-02 13:51:40.966754600 -0400 @@ -0,0 +1,507 @@ +/* + * 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-05-02 13:51:56.000000000 -0400 +++ new/src/jdk.jpackage/macosx/native/libapplauncher/PlatformDefs.h 2019-05-02 13:51:52.923585100 -0400 @@ -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-05-02 13:52:08.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractAppImageBuilder.java 2019-05-02 13:52:04.753613000 -0400 @@ -0,0 +1,263 @@ +/* + * 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(); + } + +} --- /dev/null 2019-05-02 13:52:20.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractBundler.java 2019-05-02 13:52:16.598241100 -0400 @@ -0,0 +1,194 @@ +/* + * 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()); + } + } +} --- /dev/null 2019-05-02 13:52:32.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractImageBundler.java 2019-05-02 13:52:28.436269800 -0400 @@ -0,0 +1,144 @@ +/* + * 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; + } + +} --- /dev/null 2019-05-02 13:52:43.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java 2019-05-02 13:52:40.313098600 -0400 @@ -0,0 +1,187 @@ +/* + * 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-05-02 13:52:55.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java 2019-05-02 13:52:52.126526100 -0400 @@ -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.jpackage.internal; + +@FunctionalInterface +interface ArgAction { + void execute(); +} --- /dev/null 2019-05-02 13:53:07.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java 2019-05-02 13:53:04.144757700 -0400 @@ -0,0 +1,886 @@ +/* + * 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-05-02 13:53:19.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BasicBundlers.java 2019-05-02 13:53:15.940584700 -0400 @@ -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.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-05-02 13:53:31.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundleParams.java 2019-05-02 13:53:27.787213000 -0400 @@ -0,0 +1,373 @@ +/* + * 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); + } + } + +} --- /dev/null 2019-05-02 13:53:43.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundler.java 2019-05-02 13:53:39.754643300 -0400 @@ -0,0 +1,133 @@ +/* + * 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-05-02 13:53:55.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerParamInfo.java 2019-05-02 13:53:51.722073600 -0400 @@ -0,0 +1,139 @@ +/* + * 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-05-02 13:54:07.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerType.java 2019-05-02 13:54:03.614502700 -0400 @@ -0,0 +1,32 @@ +/* + * 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 +} --- /dev/null 2019-05-02 13:54:19.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundlers.java 2019-05-02 13:54:15.675337200 -0400 @@ -0,0 +1,143 @@ +/* + * 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-05-02 13:54:31.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CLIHelp.java 2019-05-02 13:54:27.739369600 -0400 @@ -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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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-05-02 13:54:43.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ConfigException.java 2019-05-02 13:54:39.675599300 -0400 @@ -0,0 +1,45 @@ +/* + * 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-05-02 13:54:55.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java 2019-05-02 13:54:51.614829300 -0400 @@ -0,0 +1,581 @@ +/* + * 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-05-02 13:55:07.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/EnumeratedBundlerParam.java 2019-05-02 13:55:03.445857300 -0400 @@ -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.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-05-02 13:55:19.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java 2019-05-02 13:55:15.388087600 -0400 @@ -0,0 +1,312 @@ +/* + * 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; + } +} --- /dev/null 2019-05-02 13:55:30.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/InvalidBundlerParamException.java 2019-05-02 13:55:27.250316200 -0400 @@ -0,0 +1,33 @@ +/* + * 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-05-02 13:55:42.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkBundlerHelper.java 2019-05-02 13:55:39.108544400 -0400 @@ -0,0 +1,464 @@ +/* + * 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-05-02 13:55:54.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JPackageToolProvider.java 2019-05-02 13:55:51.105576400 -0400 @@ -0,0 +1,51 @@ +/* + * 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-05-02 13:56:06.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java 2019-05-02 13:56:03.040207200 -0400 @@ -0,0 +1,195 @@ +/* + * 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-05-02 13:56:18.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModFile.java 2019-05-02 13:56:14.929636000 -0400 @@ -0,0 +1,126 @@ +/* + * 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-05-02 13:56:30.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleManager.java 2019-05-02 13:56:26.849265300 -0400 @@ -0,0 +1,122 @@ +/* + * 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; + } +} --- /dev/null 2019-05-02 13:56:42.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagerException.java 2019-05-02 13:56:38.741694400 -0400 @@ -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.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-05-02 13:56:54.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Param.java 2019-05-02 13:56:50.636123700 -0400 @@ -0,0 +1,40 @@ +/* + * 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; + } + +} --- /dev/null 2019-05-02 13:57:06.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Platform.java 2019-05-02 13:57:02.662356100 -0400 @@ -0,0 +1,105 @@ +/* + * 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-05-02 13:57:18.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RelativeFileSet.java 2019-05-02 13:57:14.504984000 -0400 @@ -0,0 +1,117 @@ +/* + * 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-05-02 13:57:30.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java 2019-05-02 13:57:26.506615200 -0400 @@ -0,0 +1,771 @@ +/* + * 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-05-02 13:57:42.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/UnsupportedPlatformException.java 2019-05-02 13:57:38.430244900 -0400 @@ -0,0 +1,30 @@ +/* + * 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; +} --- /dev/null 2019-05-02 13:57:53.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ValidOptions.java 2019-05-02 13:57:50.385075200 -0400 @@ -0,0 +1,143 @@ +/* + * 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-05-02 13:58:05.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/VersionExtractor.java 2019-05-02 13:58:02.346906200 -0400 @@ -0,0 +1,91 @@ +/* + * 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; + } +} --- /dev/null 2019-05-02 13:58:17.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties 2019-05-02 13:58:14.267535600 -0400 @@ -0,0 +1,279 @@ +# +# 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-05-02 13:58:29.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties 2019-05-02 13:58:26.175565000 -0400 @@ -0,0 +1,279 @@ +# +# 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-05-02 13:58:41.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties 2019-05-02 13:58:38.114795000 -0400 @@ -0,0 +1,279 @@ +# +# 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-05-02 13:58:53.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties 2019-05-02 13:58:50.051024700 -0400 @@ -0,0 +1,97 @@ +# +# 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}. --- /dev/null 2019-05-02 13:59:05.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties 2019-05-02 13:59:01.926853400 -0400 @@ -0,0 +1,97 @@ +# +# 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}. --- /dev/null 2019-05-02 13:59:17.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties 2019-05-02 13:59:13.856483700 -0400 @@ -0,0 +1,97 @@ +# +# 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}. --- /dev/null 2019-05-02 13:59:29.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java 2019-05-02 13:59:25.780113400 -0400 @@ -0,0 +1,30 @@ +/* + * 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-05-02 13:59:41.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/main/CommandLine.java 2019-05-02 13:59:37.640743100 -0400 @@ -0,0 +1,316 @@ +/* + * 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-05-02 13:59:53.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java 2019-05-02 13:59:49.780778500 -0400 @@ -0,0 +1,129 @@ +/* + * 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; + } + +} --- /dev/null 2019-05-02 14:00:35.000000000 -0400 +++ new/src/jdk.jpackage/share/classes/module-info.java 2019-05-02 14:00:31.646385000 -0400 @@ -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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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"}. + * + * @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; +} --- /dev/null 2019-05-02 14:00:47.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/FileAttributes.h 2019-05-02 14:00:43.526214100 -0400 @@ -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 + --- /dev/null 2019-05-02 14:00:59.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/FilePath.h 2019-05-02 14:00:55.400042600 -0400 @@ -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 --- /dev/null 2019-05-02 14:01:10.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Helpers.cpp 2019-05-02 14:01:07.386073500 -0400 @@ -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; +} --- /dev/null 2019-05-02 14:01:22.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Helpers.h 2019-05-02 14:01:19.184900800 -0400 @@ -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 --- /dev/null 2019-05-02 14:01:34.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/IniFile.cpp 2019-05-02 14:01:31.089929900 -0400 @@ -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; + + 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-05-02 14:01:46.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/IniFile.h 2019-05-02 14:01:43.046760400 -0400 @@ -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 --- /dev/null 2019-05-02 14:01:58.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.cpp 2019-05-02 14:01:55.019191200 -0400 @@ -0,0 +1,303 @@ +/* + * 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-05-02 14:02:10.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.h 2019-05-02 14:02:06.878419500 -0400 @@ -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 --- /dev/null 2019-05-02 14:02:22.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Library.cpp 2019-05-02 14:02:18.765848100 -0400 @@ -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; +} --- /dev/null 2019-05-02 14:02:34.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Library.h 2019-05-02 14:02:30.668877000 -0400 @@ -0,0 +1,100 @@ +/* + * 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-05-02 14:02:46.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Macros.cpp 2019-05-02 14:02:42.587506200 -0400 @@ -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. + */ + +#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-05-02 14:02:58.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Macros.h 2019-05-02 14:02:54.506135400 -0400 @@ -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 --- /dev/null 2019-05-02 14:03:10.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Messages.cpp 2019-05-02 14:03:06.503766200 -0400 @@ -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; +} --- /dev/null 2019-05-02 14:03:22.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Messages.h 2019-05-02 14:03:18.437995700 -0400 @@ -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 --- /dev/null 2019-05-02 14:03:34.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/OrderedMap.h 2019-05-02 14:03:30.401432400 -0400 @@ -0,0 +1,239 @@ +/* + * 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-05-02 14:03:45.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Package.cpp 2019-05-02 14:03:42.321061700 -0400 @@ -0,0 +1,556 @@ +/* + * 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-05-02 14:03:57.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Package.h 2019-05-02 14:03:54.239690900 -0400 @@ -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 --- /dev/null 2019-05-02 14:04:09.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Platform.cpp 2019-05-02 14:04:06.189520700 -0400 @@ -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; +} --- /dev/null 2019-05-02 14:04:21.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Platform.h 2019-05-02 14:04:18.123750200 -0400 @@ -0,0 +1,266 @@ +/* + * 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-05-02 14:04:33.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/PlatformString.cpp 2019-05-02 14:04:29.917577000 -0400 @@ -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; +} --- /dev/null 2019-05-02 14:04:45.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/PlatformString.h 2019-05-02 14:04:41.945408300 -0400 @@ -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 --- /dev/null 2019-05-02 14:04:57.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/Properties.h 2019-05-02 14:04:53.770435700 -0400 @@ -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 + --- /dev/null 2019-05-02 14:05:09.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/PropertyFile.cpp 2019-05-02 14:05:05.704665200 -0400 @@ -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; +} --- /dev/null 2019-05-02 14:05:21.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/PropertyFile.h 2019-05-02 14:05:17.638894700 -0400 @@ -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 --- /dev/null 2019-05-02 14:05:33.000000000 -0400 +++ new/src/jdk.jpackage/share/native/libapplauncher/main.cpp 2019-05-02 14:05:29.510723000 -0400 @@ -0,0 +1,181 @@ +/* + * 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-05-02 14:05:44.000000000 -0400 +++ new/src/jdk.jpackage/unix/native/libapplauncher/FileAttribute.h 2019-05-02 14:05:41.366951000 -0400 @@ -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 --- /dev/null 2019-05-02 14:05:56.000000000 -0400 +++ new/src/jdk.jpackage/unix/native/libapplauncher/FileAttributes.cpp 2019-05-02 14:05:53.302180600 -0400 @@ -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(); + } + } +} --- /dev/null 2019-05-02 14:06:08.000000000 -0400 +++ new/src/jdk.jpackage/unix/native/libapplauncher/FilePath.cpp 2019-05-02 14:06:05.158408600 -0400 @@ -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); +} --- /dev/null 2019-05-02 14:06:20.000000000 -0400 +++ new/src/jdk.jpackage/unix/native/libapplauncher/PosixPlatform.cpp 2019-05-02 14:06:17.030236900 -0400 @@ -0,0 +1,320 @@ +/* + * 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-05-02 14:06:33.000000000 -0400 +++ new/src/jdk.jpackage/unix/native/libapplauncher/PosixPlatform.h 2019-05-02 14:06:29.701680900 -0400 @@ -0,0 +1,85 @@ +/* + * 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-05-02 14:06:48.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java 2019-05-02 14:06:44.474593300 -0400 @@ -0,0 +1,253 @@ +/* + * 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-05-02 14:07:02.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java 2019-05-02 14:06:58.399479200 -0400 @@ -0,0 +1,869 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:07:15.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java 2019-05-02 14:07:11.223736000 -0400 @@ -0,0 +1,1204 @@ +/* + * 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); + } + + } +} --- /dev/null 2019-05-02 14:07:27.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsAppImageBuilder.java 2019-05-02 14:07:23.413983000 -0400 @@ -0,0 +1,444 @@ +/* + * 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-05-02 14:07:40.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsBundlerParam.java 2019-05-02 14:07:35.955635200 -0400 @@ -0,0 +1,116 @@ +/* + * 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-05-02 14:07:53.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsDefender.java 2019-05-02 14:07:49.347497400 -0400 @@ -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.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-05-02 14:08:05.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsRegistry.java 2019-05-02 14:08:02.031765700 -0400 @@ -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.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-05-02 14:08:18.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinLauncher.template 2019-05-02 14:08:14.667029100 -0400 @@ -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-05-02 14:08:31.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties 2019-05-02 14:08:27.264288700 -0400 @@ -0,0 +1,90 @@ +# +# 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}". + --- /dev/null 2019-05-02 14:08:43.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties 2019-05-02 14:08:39.913553500 -0400 @@ -0,0 +1,90 @@ +# +# 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}". + --- /dev/null 2019-05-02 14:08:56.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties 2019-05-02 14:08:52.501812200 -0400 @@ -0,0 +1,90 @@ +# +# 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}". + --- /dev/null 2019-05-02 14:09:08.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.iss 2019-05-02 14:09:05.069068800 -0400 @@ -0,0 +1,74 @@ +;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; --- /dev/null 2019-05-02 14:09:21.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.iss 2019-05-02 14:09:17.640325800 -0400 @@ -0,0 +1,57 @@ +;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; --- /dev/null 2019-05-02 14:09:33.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.wxs 2019-05-02 14:09:30.133575000 -0400 @@ -0,0 +1,46 @@ + + + + + +UPGRADE_BLOCK + + + + + + + + + + WIX36_ONLY_START + + WIX36_ONLY_END + + + +UI_BLOCK + + --- /dev/null 2019-05-02 14:09:46.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.wxs 2019-05-02 14:09:42.762837800 -0400 @@ -0,0 +1,53 @@ + + + + + + UPGRADE_BLOCK + + + + + + + + + + WIX36_ONLY_START + + WIX36_ONLY_END + + + + CA_BLOCK + + INVALID_INSTALL_DIR_DLG_BLOCK + UI_BLOCK + + + + ADD_LAUNCHER_ICONS + + --- /dev/null 2019-05-02 14:09:59.000000000 -0400 +++ new/src/jdk.jpackage/windows/classes/module-info.java.extra 2019-05-02 14:09:55.284089800 -0400 @@ -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.jpackage.internal.Bundler with + jdk.jpackage.internal.WinAppBundler, + jdk.jpackage.internal.WinExeBundler, + jdk.jpackage.internal.WinMsiBundler; + --- /dev/null 2019-05-02 14:10:11.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/jpackageapplauncher/WinLauncher.cpp 2019-05-02 14:10:07.900284900 -0400 @@ -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; +} + --- /dev/null 2019-05-02 14:10:23.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp 2019-05-02 14:10:19.886525100 -0400 @@ -0,0 +1,34 @@ +/* + * 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-05-02 14:10:35.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libapplauncher/FileAttribute.h 2019-05-02 14:10:31.797763400 -0400 @@ -0,0 +1,50 @@ +/* + * 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-05-02 14:10:47.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libapplauncher/FilePath.cpp 2019-05-02 14:10:43.769002900 -0400 @@ -0,0 +1,473 @@ +/* + * 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-05-02 14:10:59.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libapplauncher/PlatformDefs.h 2019-05-02 14:10:55.915065400 -0400 @@ -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 --- /dev/null 2019-05-02 14:11:12.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.cpp 2019-05-02 14:11:08.502324000 -0400 @@ -0,0 +1,761 @@ +/* + * 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-05-02 14:11:24.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.h 2019-05-02 14:11:21.137587400 -0400 @@ -0,0 +1,172 @@ +/* + * 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-05-02 14:11:37.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libjpackage/ByteBuffer.cpp 2019-05-02 14:11:33.713844900 -0400 @@ -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); + } +} --- /dev/null 2019-05-02 14:11:50.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libjpackage/ByteBuffer.h 2019-05-02 14:11:46.217095100 -0400 @@ -0,0 +1,56 @@ +/* + * 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-05-02 14:12:02.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libjpackage/IconSwap.cpp 2019-05-02 14:11:58.751348400 -0400 @@ -0,0 +1,203 @@ +/* + * 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-05-02 14:12:15.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libjpackage/IconSwap.h 2019-05-02 14:12:11.292602400 -0400 @@ -0,0 +1,35 @@ +/* + * 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-05-02 14:12:27.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libjpackage/Utils.cpp 2019-05-02 14:12:24.061879200 -0400 @@ -0,0 +1,80 @@ +/* + * 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-05-02 14:12:40.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libjpackage/Utils.h 2019-05-02 14:12:36.796152500 -0400 @@ -0,0 +1,39 @@ +/* + * 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-05-02 14:12:53.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libjpackage/VersionInfoSwap.cpp 2019-05-02 14:12:49.402413000 -0400 @@ -0,0 +1,284 @@ +/* + * 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-05-02 14:13:05.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libjpackage/VersionInfoSwap.h 2019-05-02 14:13:02.001672800 -0400 @@ -0,0 +1,52 @@ +/* + * 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-05-02 14:13:18.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libjpackage/WindowsRegistry.cpp 2019-05-02 14:13:14.555928100 -0400 @@ -0,0 +1,161 @@ +/* + * 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-05-02 14:13:30.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libjpackage/jpackage.cpp 2019-05-02 14:13:27.096182000 -0400 @@ -0,0 +1,83 @@ +/* + * 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-05-02 14:13:43.000000000 -0400 +++ new/src/jdk.jpackage/windows/native/libwixhelper/libwixhelper.cpp 2019-05-02 14:13:39.745446800 -0400 @@ -0,0 +1,91 @@ +/* + * 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-05-02 14:13:56.000000000 -0400 +++ new/test/jdk/tools/jpackage/JPackageHelpTest.java 2019-05-02 14:13:52.312703400 -0400 @@ -0,0 +1,117 @@ +/* + * 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(); + } + +} --- /dev/null 2019-05-02 14:14:08.000000000 -0400 +++ new/test/jdk/tools/jpackage/JPackageInvalidArgTest.java 2019-05-02 14:14:04.918963900 -0400 @@ -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 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-05-02 14:14:21.000000000 -0400 +++ new/test/jdk/tools/jpackage/JPackageMissingArgumentsTest.java 2019-05-02 14:14:17.472704900 -0400 @@ -0,0 +1,167 @@ +/* + * 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-05-02 14:14:33.000000000 -0400 +++ new/test/jdk/tools/jpackage/JPackageNoArgTest.java 2019-05-02 14:14:30.030215900 -0400 @@ -0,0 +1,78 @@ +/* + * 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(); + } + +} --- /dev/null 2019-05-02 14:14:46.000000000 -0400 +++ new/test/jdk/tools/jpackage/JPackageVersionTest.java 2019-05-02 14:14:42.682745900 -0400 @@ -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 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(); + } + +} --- /dev/null 2019-05-02 14:14:59.000000000 -0400 +++ new/test/jdk/tools/jpackage/apps/com.hello/com/hello/Hello.java 2019-05-02 14:14:55.281265100 -0400 @@ -0,0 +1,64 @@ +/* + * 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 com.hello; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; + +public class Hello { + + private static final String MSG = "jpackage test application"; + private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1 + + public static void main(String[] args) { + String outputFile = "appOutput.txt"; + File file = new File(outputFile); + + try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) { + System.out.println(MSG); + out.println(MSG); + + System.out.println("args.length: " + args.length); + out.println("args.length: " + args.length); + + for (String arg : args) { + System.out.println(arg); + out.println(arg); + } + + 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); + out.println("-Dparam" + index + "=" + value); + } + } + } catch (Exception ex) { + System.err.println(ex.toString()); + } + } + +} --- /dev/null 2019-05-02 14:15:11.000000000 -0400 +++ new/test/jdk/tools/jpackage/apps/com.hello/module-info.java 2019-05-02 14:15:07.912790900 -0400 @@ -0,0 +1,26 @@ +/* + * 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. + */ + +module com.hello { + exports com.hello; +} --- /dev/null 2019-05-02 14:15:24.000000000 -0400 +++ new/test/jdk/tools/jpackage/apps/com.other/com/other/Other.java 2019-05-02 14:15:20.468301500 -0400 @@ -0,0 +1,64 @@ +/* + * 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 com.other; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; + +public class Other { + + private static final String MSG = "other jpackage test application"; + private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1 + + public static void main(String[] args) { + String outputFile = "appOutput.txt"; + File file = new File(outputFile); + + try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) { + System.out.println(MSG); + out.println(MSG); + + System.out.println("args.length: " + args.length); + out.println("args.length: " + args.length); + + for (String arg : args) { + System.out.println(arg); + out.println(arg); + } + + 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); + out.println("-Dparam" + index + "=" + value); + } + } + } catch (Exception ex) { + System.err.println(ex.toString()); + } + } + +} --- /dev/null 2019-05-02 14:15:36.000000000 -0400 +++ new/test/jdk/tools/jpackage/apps/com.other/module-info.java 2019-05-02 14:15:33.109829300 -0400 @@ -0,0 +1,26 @@ +/* + * 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. + */ + +module com.other { + exports com.other; +} --- /dev/null 2019-05-02 14:15:49.000000000 -0400 +++ new/test/jdk/tools/jpackage/apps/image/Hello.java 2019-05-02 14:15:45.723351500 -0400 @@ -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.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; + +public class Hello { + + private static final String MSG = "jpackage test application"; + private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1 + + public static void main(String[] args) { + printToStdout(args); + printToFile(args); + } + + private static void printToStdout(String[] args) { + System.out.println(MSG); + + System.out.println("args.length: " + args.length); + + for (String arg : args) { + System.out.println(arg); + } + + 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); + } + } + } + + 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); + + out.println("args.length: " + args.length); + + for (String arg : args) { + out.println(arg); + } + + for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) { + String value = System.getProperty("param" + index); + if (value != null) { + out.println("-Dparam" + index + "=" + value); + } + } + } catch (Exception ex) { + System.err.println(ex.getMessage()); + } + } +} --- /dev/null 2019-05-02 14:16:02.000000000 -0400 +++ new/test/jdk/tools/jpackage/apps/installer/Hello.java 2019-05-02 14:15:58.283863100 -0400 @@ -0,0 +1,113 @@ +/* + * 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.awt.Desktop; +import java.awt.desktop.OpenFilesEvent; +import java.awt.desktop.OpenFilesHandler; +import java.util.List; + +public class Hello implements OpenFilesHandler { + + private static final String MSG = "jpackage test application"; + private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1 + private static List files; + + public static void main(String[] args) { + if(Desktop.getDesktop().isSupported(Desktop.Action.APP_OPEN_FILE)) { + Desktop.getDesktop().setOpenFileHandler(new Hello()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + printToStdout(args); + if (args.length == 1 || (files != null && files.size() == 1)) { // Called via file association + printToFile(args); + } + } + + private static void printToStdout(String[] args) { + System.out.println(MSG); + + System.out.println("args.length: " + (files == null ? args.length : args.length + files.size())); + + for (String arg : args) { + System.out.println(arg); + } + + if (files != null) { + for (File file : files) { + System.out.println(file.getAbsolutePath()); + } + } + + 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); + } + } + } + + private static void printToFile(String[] args) { + File inputFile = files == null ? new File(args[0]) : files.get(0); + String outputFile = inputFile.getParent() + File.separator + "appOutput.txt"; + File file = new File(outputFile); + + try (PrintWriter out + = new PrintWriter(new BufferedWriter(new FileWriter(file)))) { + out.println(MSG); + + out.println("args.length: " + (files == null ? args.length : args.length + files.size())); + + for (String arg : args) { + out.println(arg); + } + + if (files != null) { + for (File f : files) { + out.println(f.getAbsolutePath()); + } + } + + for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) { + String value = System.getProperty("param" + index); + if (value != null) { + out.println("-Dparam" + index + "=" + value); + } + } + } catch (Exception ex) { + System.err.println(ex.getMessage()); + } + } + + @Override + public void openFiles(OpenFilesEvent e) { + files = e.getFiles(); + } +} --- /dev/null 2019-05-02 14:16:14.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddLauncherBase.java 2019-05-02 14:16:10.977401300 -0400 @@ -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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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-05-02 14:16:27.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddLauncherModuleTest.java 2019-05-02 14:16:23.454896300 -0400 @@ -0,0 +1,52 @@ +/* + * 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-05-02 14:16:39.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddLauncherTest.java 2019-05-02 14:16:36.060416900 -0400 @@ -0,0 +1,53 @@ +/* + * 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-05-02 14:16:52.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddModulesTest.java 2019-05-02 14:16:48.794963300 -0400 @@ -0,0 +1,76 @@ +/* + * 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); + } + +} --- /dev/null 2019-05-02 14:17:05.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageArgumentsBase.java 2019-05-02 14:17:01.369477700 -0400 @@ -0,0 +1,122 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:17:17.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageArgumentsModuleTest.java 2019-05-02 14:17:13.948993100 -0400 @@ -0,0 +1,52 @@ +/* + * 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); + } + +} --- /dev/null 2019-05-02 14:17:30.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageArgumentsTest.java 2019-05-02 14:17:26.474497700 -0400 @@ -0,0 +1,53 @@ +/* + * 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); + } + +} --- /dev/null 2019-05-02 14:17:42.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageBase.java 2019-05-02 14:17:38.960994500 -0400 @@ -0,0 +1,73 @@ +/* + * 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-05-02 14:17:55.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageErrorTest.java 2019-05-02 14:17:51.445490900 -0400 @@ -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. + */ + + /* + * @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-05-02 14:18:07.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageIconTest.java 2019-05-02 14:18:03.949991300 -0400 @@ -0,0 +1,126 @@ +/* + * 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(); + } + +} --- /dev/null 2019-05-02 14:18:21.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJavaOptionsBase.java 2019-05-02 14:18:16.990598900 -0400 @@ -0,0 +1,149 @@ +/* + * 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-05-02 14:18:33.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJavaOptionsModuleTest.java 2019-05-02 14:18:30.120224300 -0400 @@ -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 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-05-02 14:18:46.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJavaOptionsTest.java 2019-05-02 14:18:42.858771500 -0400 @@ -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 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-05-02 14:18:59.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageMainClassAttributeTest.java 2019-05-02 14:18:55.529305100 -0400 @@ -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.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(); + } + +} --- /dev/null 2019-05-02 14:19:11.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageMainClassErrorTest.java 2019-05-02 14:19:07.786756100 -0400 @@ -0,0 +1,75 @@ +/* + * 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)); + } + +} --- /dev/null 2019-05-02 14:19:23.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModularJarTest.java 2019-05-02 14:19:20.021466300 -0400 @@ -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. + */ + + /* + * @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); + } + +} --- /dev/null 2019-05-02 14:19:35.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModuleMainClassErrorTest.java 2019-05-02 14:19:32.207684800 -0400 @@ -0,0 +1,86 @@ +/* + * 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)); + } + +} --- /dev/null 2019-05-02 14:19:48.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModulePathTest.java 2019-05-02 14:19:44.886952600 -0400 @@ -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. + */ + + /* + * @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); + } + +} --- /dev/null 2019-05-02 14:20:01.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModuleTest.java 2019-05-02 14:19:57.439207700 -0400 @@ -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 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); + } + +} --- /dev/null 2019-05-02 14:20:13.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageNoNameTest.java 2019-05-02 14:20:10.113475000 -0400 @@ -0,0 +1,99 @@ +/* + * 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(); + } + +} --- /dev/null 2019-05-02 14:20:56.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageRuntimeBase.java 2019-05-02 14:20:52.640727300 -0400 @@ -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.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(); + } + +} --- /dev/null 2019-05-02 14:21:09.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageRuntimeModuleTest.java 2019-05-02 14:21:05.315994700 -0400 @@ -0,0 +1,52 @@ +/* + * 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); + } + +} --- /dev/null 2019-05-02 14:21:21.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageRuntimeTest.java 2019-05-02 14:21:18.003263300 -0400 @@ -0,0 +1,54 @@ +/* + * 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); + } + +} --- /dev/null 2019-05-02 14:21:34.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageTempRootTest.java 2019-05-02 14:21:30.521515000 -0400 @@ -0,0 +1,108 @@ +/* + * 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(); + } + +} --- /dev/null 2019-05-02 14:21:46.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageTest.java 2019-05-02 14:21:43.106773400 -0400 @@ -0,0 +1,52 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:21:59.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageVerboseTest.java 2019-05-02 14:21:55.568019400 -0400 @@ -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. + */ + +/* + * @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(); + } + +} --- /dev/null 2019-05-02 14:22:12.000000000 -0400 +++ new/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageVersionTest.java 2019-05-02 14:22:08.156278100 -0400 @@ -0,0 +1,103 @@ + +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(); + } + +} --- /dev/null 2019-05-02 14:22:24.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerBase.java 2019-05-02 14:22:20.890551400 -0400 @@ -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 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(); + } + } +} --- /dev/null 2019-05-02 14:22:37.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerBundleNameBase.java 2019-05-02 14:22:33.737836000 -0400 @@ -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.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(); + } + } +} --- /dev/null 2019-05-02 14:22:50.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerFileAssociationsBase.java 2019-05-02 14:22:46.379100000 -0400 @@ -0,0 +1,156 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:23:05.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerInstallDirBase.java 2019-05-02 14:23:01.428655200 -0400 @@ -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.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(); + } + } +} --- /dev/null 2019-05-02 14:23:19.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerLicenseBase.java 2019-05-02 14:23:15.031015300 -0400 @@ -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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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(); + } + } +} --- /dev/null 2019-05-02 14:23:31.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerLicenseTypeBase.java 2019-05-02 14:23:27.895301600 -0400 @@ -0,0 +1,114 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:23:44.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerMaintainerBase.java 2019-05-02 14:23:40.538565800 -0400 @@ -0,0 +1,110 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:23:57.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerPackageDepsBase.java 2019-05-02 14:23:53.321844000 -0400 @@ -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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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(); + } + } +} --- /dev/null 2019-05-02 14:24:10.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerBundleNameTest.java 2019-05-02 14:24:06.143126000 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:24:52.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerFileAssociationsTest.java 2019-05-02 14:24:48.833394600 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:25:05.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerInstallDirTest.java 2019-05-02 14:25:01.747685900 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:25:18.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerLicenseTest.java 2019-05-02 14:25:14.553966400 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:25:31.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerMaintainerTest.java 2019-05-02 14:25:27.480258900 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:25:44.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerPackageDepsTest.java 2019-05-02 14:25:40.276538400 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:25:57.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerTest.java 2019-05-02 14:25:53.147825400 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:26:09.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/deb/install.sh 2019-05-02 14:26:06.088119300 -0400 @@ -0,0 +1,8 @@ +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 --- /dev/null 2019-05-02 14:26:22.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/deb/uninstall.sh 2019-05-02 14:26:18.940404400 -0400 @@ -0,0 +1,8 @@ +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 --- /dev/null 2019-05-02 14:27:06.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerBundleNameTest.java 2019-05-02 14:27:02.044714400 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:27:19.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerFileAssociationsTest.java 2019-05-02 14:27:15.210030800 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:27:32.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerInstallDirTest.java 2019-05-02 14:27:28.084318100 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:27:44.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerLicenseTest.java 2019-05-02 14:27:41.021611700 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:27:57.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerLicenseTypeTest.java 2019-05-02 14:27:53.871896600 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:28:10.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerPackageDepsTest.java 2019-05-02 14:28:06.756184900 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:28:23.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerTest.java 2019-05-02 14:28:19.760485200 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:28:36.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/rpm/install.sh 2019-05-02 14:28:32.618770900 -0400 @@ -0,0 +1,8 @@ +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 --- /dev/null 2019-05-02 14:28:49.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/linux/rpm/uninstall.sh 2019-05-02 14:28:45.422051100 -0400 @@ -0,0 +1,8 @@ +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 --- /dev/null 2019-05-02 14:29:02.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerBase.java 2019-05-02 14:28:58.244333200 -0400 @@ -0,0 +1,84 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:29:15.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerFileAssociationsBase.java 2019-05-02 14:29:11.036612300 -0400 @@ -0,0 +1,150 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:29:28.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerInstallDirBase.java 2019-05-02 14:29:24.067915300 -0400 @@ -0,0 +1,85 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:30:10.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerLicenseBase.java 2019-05-02 14:30:07.057213800 -0400 @@ -0,0 +1,86 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:30:24.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerFileAssociationsTest.java 2019-05-02 14:30:20.081516100 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:30:36.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerInstallDirTest.java 2019-05-02 14:30:32.993807200 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:30:49.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerLicenseTest.java 2019-05-02 14:30:45.782085900 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:31:02.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerTest.java 2019-05-02 14:30:58.616369200 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:31:15.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/dmg/install.sh 2019-05-02 14:31:11.450652500 -0400 @@ -0,0 +1,21 @@ +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/ --- /dev/null 2019-05-02 14:31:28.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/dmg/uninstall.sh 2019-05-02 14:31:24.327940100 -0400 @@ -0,0 +1,4 @@ +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 --- /dev/null 2019-05-02 14:31:41.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerFileAssociationsTest.java 2019-05-02 14:31:37.194226600 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:31:53.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerInstallDirTest.java 2019-05-02 14:31:50.036510700 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:32:06.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerLicenseTest.java 2019-05-02 14:32:02.919798900 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:32:19.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerTest.java 2019-05-02 14:32:15.840090800 -0400 @@ -0,0 +1,44 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:32:32.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/pkg/install.sh 2019-05-02 14:32:28.681374800 -0400 @@ -0,0 +1,5 @@ +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 / --- /dev/null 2019-05-02 14:32:45.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/macosx/pkg/uninstall.sh 2019-05-02 14:32:41.572663800 -0400 @@ -0,0 +1,4 @@ +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 --- /dev/null 2019-05-02 14:32:58.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerBase.java 2019-05-02 14:32:54.411947600 -0400 @@ -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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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(); + } + } +} --- /dev/null 2019-05-02 14:33:11.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerFileAssociationsBase.java 2019-05-02 14:33:07.342240500 -0400 @@ -0,0 +1,181 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:33:24.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerInstallDirBase.java 2019-05-02 14:33:20.247530900 -0400 @@ -0,0 +1,102 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:33:37.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerLicenseBase.java 2019-05-02 14:33:33.093815400 -0400 @@ -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.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(); + } + } +} --- /dev/null 2019-05-02 14:33:49.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinDirChooserBase.java 2019-05-02 14:33:45.963102200 -0400 @@ -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.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(); + } + } +} --- /dev/null 2019-05-02 14:34:02.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuBase.java 2019-05-02 14:33:58.852391000 -0400 @@ -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.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(); + } + } +} --- /dev/null 2019-05-02 14:34:15.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuGroupBase.java 2019-05-02 14:34:11.696675300 -0400 @@ -0,0 +1,94 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:34:28.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinPerUserInstallBase.java 2019-05-02 14:34:24.614967000 -0400 @@ -0,0 +1,95 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:34:41.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinRegistryNameBase.java 2019-05-02 14:34:37.441249500 -0400 @@ -0,0 +1,171 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:34:54.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinShortcutBase.java 2019-05-02 14:34:50.314536700 -0400 @@ -0,0 +1,99 @@ +/* + * 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(); + } + } +} --- /dev/null 2019-05-02 14:35:07.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinUpgradeUUIDBase.java 2019-05-02 14:35:03.208826000 -0400 @@ -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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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(); + } + } +} --- /dev/null 2019-05-02 14:35:20.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerFileAssociationsInstallDirTest.java 2019-05-02 14:35:16.318136800 -0400 @@ -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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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); + } +} --- /dev/null 2019-05-02 14:35:33.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerFileAssociationsTest.java 2019-05-02 14:35:29.116416500 -0400 @@ -0,0 +1,46 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:35:46.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerInstallDirTest.java 2019-05-02 14:35:42.055710300 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:35:58.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerLicenseTest.java 2019-05-02 14:35:55.049009500 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:36:11.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerTest.java 2019-05-02 14:36:07.946299100 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:36:24.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinDirChooserTest.java 2019-05-02 14:36:20.936598000 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:37:07.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuGroupTest.java 2019-05-02 14:37:04.019905900 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:37:20.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuTest.java 2019-05-02 14:37:16.929196700 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:37:33.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinPerUserInstallTest.java 2019-05-02 14:37:29.988502500 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:38:16.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinRegistryNameTest.java 2019-05-02 14:38:12.947798000 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:38:29.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinShortcutTest.java 2019-05-02 14:38:25.819085000 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:38:42.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinUpgradeUUIDTest.java 2019-05-02 14:38:38.766379600 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:38:55.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/install.bat 2019-05-02 14:38:51.650667900 -0400 @@ -0,0 +1,16 @@ +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 --- /dev/null 2019-05-02 14:39:08.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/exe/uninstall.bat 2019-05-02 14:39:04.611963900 -0400 @@ -0,0 +1,13 @@ +"%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 --- /dev/null 2019-05-02 14:39:21.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerFileAssociationsInstallDirTest.java 2019-05-02 14:39:17.611263700 -0400 @@ -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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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); + } +} --- /dev/null 2019-05-02 14:39:34.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerFileAssociationsTest.java 2019-05-02 14:39:30.462548700 -0400 @@ -0,0 +1,46 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:39:47.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerInstallDirTest.java 2019-05-02 14:39:43.420844400 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:40:00.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerLicenseTest.java 2019-05-02 14:39:56.310133200 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:40:43.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerTest.java 2019-05-02 14:40:39.103412100 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:40:55.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinDirChooserTest.java 2019-05-02 14:40:52.037705400 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:41:08.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuGroupTest.java 2019-05-02 14:41:04.927994300 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:41:21.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuTest.java 2019-05-02 14:41:17.825283900 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:41:34.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinPerUserInstallTest.java 2019-05-02 14:41:30.698571100 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:41:47.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinRegistryNameTest.java 2019-05-02 14:41:43.929894100 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:42:00.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinShortcutTest.java 2019-05-02 14:41:56.946195600 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:42:13.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinUpgradeUUIDTest.java 2019-05-02 14:42:09.874488300 -0400 @@ -0,0 +1,45 @@ +/* + * 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); + } +} --- /dev/null 2019-05-02 14:42:26.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/install.bat 2019-05-02 14:42:22.885789300 -0400 @@ -0,0 +1,16 @@ +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 --- /dev/null 2019-05-02 14:42:39.000000000 -0400 +++ new/test/jdk/tools/jpackage/createinstaller/windows/msi/uninstall.bat 2019-05-02 14:42:35.776078200 -0400 @@ -0,0 +1,13 @@ +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 --- /dev/null 2019-05-02 14:42:52.000000000 -0400 +++ new/test/jdk/tools/jpackage/helpers/JPackageHelper.java 2019-05-02 14:42:48.664366900 -0400 @@ -0,0 +1,580 @@ +/* + * 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.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +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; + +import java.util.spi.ToolProvider; + +public class JPackageHelper { + + private static final boolean VERBOSE = false; + private static final String OS = System.getProperty("os.name").toLowerCase(); + private static final String JAVA_HOME = System.getProperty("java.home"); + public static final String TEST_SRC_ROOT; + public static final String TEST_SRC; + private static final Path BIN_DIR = Path.of(JAVA_HOME, "bin"); + private static final Path JPACKAGE; + private static final Path JAVAC; + private static final Path JAR; + private static final Path JLINK; + + static { + if (OS.startsWith("win")) { + JPACKAGE = BIN_DIR.resolve("jpackage.exe"); + JAVAC = BIN_DIR.resolve("javac.exe"); + JAR = BIN_DIR.resolve("jar.exe"); + JLINK = BIN_DIR.resolve("jlink.exe"); + } else { + JPACKAGE = BIN_DIR.resolve("jpackage"); + JAVAC = BIN_DIR.resolve("javac"); + JAR = BIN_DIR.resolve("jar"); + JLINK = BIN_DIR.resolve("jlink"); + } + + // 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 + ".."; + } 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"); + } + } + } + + TEST_SRC = System.getProperty("test.src"); + } + + static final ToolProvider JPACKAGE_TOOL = + ToolProvider.findFirst("jpackage").orElseThrow( + () -> new RuntimeException("jpackage tool not found")); + + public static int execute(File out, String... command) throws Exception { + if (VERBOSE) { + System.out.print("Execute command: "); + for (String c : command) { + System.out.print(c); + System.out.print(" "); + } + System.out.println(); + } + + ProcessBuilder builder = new ProcessBuilder(command); + if (out != null) { + builder.redirectErrorStream(true); + builder.redirectOutput(out); + } + + Process process = builder.start(); + return process.waitFor(); + } + + public static Process executeNoWait(File out, String... command) throws Exception { + if (VERBOSE) { + System.out.print("Execute command: "); + for (String c : command) { + System.out.print(c); + System.out.print(" "); + } + System.out.println(); + } + + ProcessBuilder builder = new ProcessBuilder(command); + if (out != null) { + builder.redirectErrorStream(true); + builder.redirectOutput(out); + } + + return builder.start(); + } + + private static String[] getCommand(String... args) { + String[] command; + if (args == null) { + command = new String[1]; + } else { + command = new String[args.length + 1]; + } + + int index = 0; + command[index] = JPACKAGE.toString(); + + if (args != null) { + for (String arg : args) { + index++; + command[index] = arg; + } + } + + return command; + } + + 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 (OS.startsWith("win")) { + Files.setAttribute(file, "dos:readonly", false); + } + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, + BasicFileAttributes attr) throws IOException { + if (OS.startsWith("win")) { + 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 deleteOutputFolder(String output) throws IOException { + File outputFolder = new File(output); + System.out.println("AMDEBUG output: " + outputFolder.getAbsolutePath()); + try { + deleteRecursive(outputFolder); + } catch (IOException ioe) { + System.out.println("IOException: " + ioe); + ioe.printStackTrace(); + deleteRecursive(outputFolder); + } + } + + public static String executeCLI(boolean retValZero, String... args) throws Exception { + int retVal; + File outfile = new File("output.log"); + try { + String[] command = getCommand(args); + retVal = execute(outfile, command); + } catch (Exception ex) { + if (outfile.exists()) { + System.err.println(Files.readString(outfile.toPath())); + } + throw ex; + } + + String output = Files.readString(outfile.toPath()); + if (retValZero) { + if (retVal != 0) { + System.err.println(output); + throw new AssertionError("jpackage exited with error: " + retVal); + } + } else { + if (retVal == 0) { + System.err.println(output); + throw new AssertionError("jpackage exited without error: " + retVal); + } + } + + if (VERBOSE) { + System.out.println("output ="); + System.out.println(output); + } + + return output; + } + + public static String executeToolProvider(boolean retValZero, String... args) throws Exception { + StringWriter writer = new StringWriter(); + PrintWriter pw = new PrintWriter(writer); + int retVal = JPACKAGE_TOOL.run(pw, pw, args); + String output = writer.toString(); + + if (retValZero) { + if (retVal != 0) { + System.err.println(output); + throw new AssertionError("jpackage exited with error: " + retVal); + } + } else { + if (retVal == 0) { + System.err.println(output); + throw new AssertionError("jpackage exited without error"); + } + } + + if (VERBOSE) { + System.out.println("output ="); + System.out.println(output); + } + + return output; + } + + 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"))); + } + + public static void createHelloImageJar() throws Exception { + createJar(false, "Hello", "image"); + } + + public static void createHelloImageJarWithMainClass() throws Exception { + createJar(true, "Hello", "image"); + } + + public static void createHelloInstallerJar() throws Exception { + createJar(false, "Hello", "installer"); + } + + public static void createHelloInstallerJarWithMainClass() throws Exception { + createJar(true, "Hello", "installer"); + } + + private static void createJar(boolean mainClassAttribute, String name, + String testType) throws Exception { + int retVal; + + File input = new File("input"); + if (!input.exists()) { + input.mkdir(); + } + + Files.copy(Path.of(TEST_SRC_ROOT + File.separator + "apps" + File.separator + + testType + File.separator + name + ".java"), Path.of(name + ".java")); + + File javacLog = new File("javac.log"); + try { + retVal = execute(javacLog, JAVAC.toString(), name + ".java"); + } catch (Exception ex) { + if (javacLog.exists()) { + System.err.println(Files.readString(javacLog.toPath())); + } + throw ex; + } + + if (retVal != 0) { + if (javacLog.exists()) { + System.err.println(Files.readString(javacLog.toPath())); + } + 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("-c"); + args.add("-v"); + args.add("-f"); + args.add("input" + File.separator + name.toLowerCase() + ".jar"); + if (mainClassAttribute) { + args.add("-e"); + args.add(name); + } + args.add(name + ".class"); + retVal = execute(jarLog, args.stream().toArray(String[]::new)); + } catch (Exception ex) { + if (jarLog.exists()) { + System.err.println(Files.readString(jarLog.toPath())); + } + throw ex; + } + + if (retVal != 0) { + if (jarLog.exists()) { + System.err.println(Files.readString(jarLog.toPath())); + } + throw new AssertionError("jar exited with error: " + retVal); + } + } + + public static void createHelloModule() throws Exception { + createModule("Hello.java", "input", "hello"); + } + + public static void createOtherModule() throws Exception { + createModule("Other.java", "input-other", "other"); + } + + private static void createModule(String javaFile, String inputDir, + String aName) throws Exception { + int retVal; + + File input = new File(inputDir); + if (!input.exists()) { + input.mkdir(); + } + + File module = new File("module" + File.separator + "com." + aName); + if (!module.exists()) { + module.mkdirs(); + } + + File javacLog = new File("javac.log"); + try { + List args = new ArrayList<>(); + args.add(JAVAC.toString()); + args.add("-d"); + args.add("module" + File.separator + "com." + aName); + args.add(TEST_SRC_ROOT + File.separator + "apps" + File.separator + + "com." + aName + File.separator + "module-info.java"); + args.add(TEST_SRC_ROOT + File.separator + "apps" + + File.separator + "com." + aName + File.separator + "com" + + File.separator + aName + File.separator + javaFile); + retVal = execute(javacLog, args.stream().toArray(String[]::new)); + } catch (Exception ex) { + if (javacLog.exists()) { + System.err.println(Files.readString(javacLog.toPath())); + } + throw ex; + } + + if (retVal != 0) { + if (javacLog.exists()) { + System.err.println(Files.readString(javacLog.toPath())); + } + 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())); + } + throw ex; + } + + if (retVal != 0) { + if (jarLog.exists()) { + System.err.println(Files.readString(jarLog.toPath())); + } + throw new AssertionError("jar exited with error: " + retVal); + } + } + + public static void createRuntime() throws Exception { + int retVal; + + File jlinkLog = new File("jlink.log"); + try { + List args = new ArrayList<>(); + args.add(JLINK.toString()); + args.add("--output"); + args.add("runtime"); + args.add("--add-modules"); + args.add("java.base"); + retVal = execute(jlinkLog, args.stream().toArray(String[]::new)); + } catch (Exception ex) { + if (jlinkLog.exists()) { + System.err.println(Files.readString(jlinkLog.toPath())); + } + throw ex; + } + + if (retVal != 0) { + if (jlinkLog.exists()) { + System.err.println(Files.readString(jlinkLog.toPath())); + } + throw new AssertionError("jlink exited with error: " + retVal); + } + } + + public static String listToArgumentsMap(List arguments, boolean toolProvider) { + if (arguments.isEmpty()) { + return ""; + } + + String argsStr = ""; + for (int i = 0; i < arguments.size(); i++) { + String arg = arguments.get(i); + argsStr += quote(arg, toolProvider); + if ((i + 1) != arguments.size()) { + argsStr += " "; + } + } + + if (!toolProvider && isWindows()) { + if (argsStr.contains(" ")) { + if (argsStr.contains("\"")) { + argsStr = escapeQuote(argsStr, toolProvider); + } + argsStr = "\"" + argsStr + "\""; + } + } + return argsStr; + } + + private static String quote(String in, boolean toolProvider) { + if (in == null) { + return null; + } + + if (in.isEmpty()) { + return ""; + } + + if (!in.contains("=")) { + // Not a property + if (in.contains(" ")) { + in = escapeQuote(in, toolProvider); + 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, toolProvider); + + return param + "=" + "\"" + value + "\""; + } + + private static String escapeQuote(String in, boolean toolProvider) { + 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; + } + +} --- /dev/null 2019-05-02 14:43:05.000000000 -0400 +++ new/test/jdk/tools/jpackage/helpers/JPackageInstallerHelper.java 2019-05-02 14:43:01.631663500 -0400 @@ -0,0 +1,243 @@ +/* + * 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.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.List; + +public class JPackageInstallerHelper { + private static final String JPACKAGE_TEST_OUTPUT = "jpackage.test.output"; + private static final String JPACKAGE_VERIFY_INSTALL = "jpackage.verify.install"; + private static final String JPACKAGE_VERIFY_UNINSTALL = "jpackage.verify.uninstall"; + private static String testOutput; + private static final boolean isTestOutputSet; + private static final boolean isVerifyInstall; + private static final boolean isVerifyUnInstall; + + static { + String out = System.getProperty(JPACKAGE_TEST_OUTPUT); + isTestOutputSet = (out != null); + if (isTestOutputSet) { + File file = new File(out); + if (!file.exists()) { + throw new AssertionError(file.getAbsolutePath() + " does not exist"); + } + + if (!file.isDirectory()) { + throw new AssertionError(file.getAbsolutePath() + " is not a directory"); + } + + if (!file.canWrite()) { + throw new AssertionError(file.getAbsolutePath() + " is not writable"); + } + + if (out.endsWith(File.separator)) { + out = out.substring(0, out.length() - 2); + } + + testOutput = out; + } + + isVerifyInstall = (System.getProperty(JPACKAGE_VERIFY_INSTALL) != null); + isVerifyUnInstall = (System.getProperty(JPACKAGE_VERIFY_UNINSTALL) != null); + } + + public static boolean isTestOutputSet() { + return isTestOutputSet; + } + + public static boolean isVerifyInstall() { + return isVerifyInstall; + } + + public static boolean isVerifyUnInstall() { + return isVerifyUnInstall; + } + + public static void copyTestResults(List files) throws Exception { + if (!isTestOutputSet()) { + return; + } + + File dest = new File(testOutput); + if (!dest.exists()) { + dest.mkdirs(); + } + + if (JPackageHelper.isWindows()) { + files.add(JPackagePath.getTestSrc() + File.separator + "install.bat"); + files.add(JPackagePath.getTestSrc() + File.separator + "uninstall.bat"); + } else { + files.add(JPackagePath.getTestSrc() + File.separator + "install.sh"); + files.add(JPackagePath.getTestSrc() + File.separator + "uninstall.sh"); + } + + for (String file : files) { + Path source = Path.of(file); + Path target = Path.of(dest.toPath() + File.separator + source.getFileName()); + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + } + } + + public static void validateApp(String app) throws Exception { + File outFile = new File("appOutput.txt"); + if (outFile.exists()) { + outFile.delete(); + } + + 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.getAbsolutePath() + " was not created"); + } + + String output = Files.readString(outFile.toPath()); + String[] result = output.split("\n"); + if (result.length != 2) { + 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: 0")) { + throw new AssertionError("Unexpected result[1]: " + result[1]); + } + } + + public static void validateOutput(String output) throws Exception { + File file = new File(output); + if (!file.exists()) { + // Try lower case in case of OS is case sensitive + file = new File(output.toLowerCase()); + if (!file.exists()) { + throw new AssertionError("Cannot find " + file.getAbsolutePath()); + } + } + } + + 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); + } + } + } +} --- /dev/null 2019-05-02 14:43:18.000000000 -0400 +++ new/test/jdk/tools/jpackage/helpers/JPackagePath.java 2019-05-02 14:43:14.516951900 -0400 @@ -0,0 +1,292 @@ +/* + * 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; + +/** + * 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; + } + + // Return path to calling test + public static String getTestSrc() { + return JPackageHelper.TEST_SRC; + } + + // 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"); + } + } + + // 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"); + } + } + + // Returns path to generate test application without --name argument + public static String getAppNoName() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "Hello" + File.separator + "Hello.exe"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "Hello.app" + File.separator + "Contents" + + File.separator + "MacOS" + File.separator + "Hello"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "Hello" + File.separator + "Hello"; + } 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 app working directory (where test application generates its output) + public static String getAppWorkingDir() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "test" + File.separator + "app"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "test.app" + File.separator + "Contents" + + File.separator + "Java"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "test" + File.separator + "app"; + } else { + throw new AssertionError("Cannot detect platform"); + } + } + + // 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"); + } + } + + // Returns path to app working directory without --name (where test application generates its output) + public static String getAppWorkingDirNoName() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "Hello" + File.separator + "app"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "Hello.app" + File.separator + "Contents" + + File.separator + "Java"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "Hello" + File.separator + "app"; + } else { + throw new AssertionError("Cannot detect platform"); + } + } + + // Returns path including executable to java in image runtime folder + public static String getRuntimeJava() { + 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"); + } + } + + // Returns output file name generate by test application + public static String getAppOutputFile() { + return "appOutput.txt"; + } + + // Returns path to bin folder in image runtime + public static String getRuntimeBin() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "test" + + File.separator + "runtime" + File.separator + "bin"; + } 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"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "test" + + File.separator + "runtime" + File.separator + "bin"; + } 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; + } + + 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; + } + + // Returs path to test license file + public static String getLicenseFilePath() { + 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"; + } +} --- /dev/null 2019-05-02 14:43:31.000000000 -0400 +++ new/test/jdk/tools/jpackage/jdk/jpackage/internal/DeployParamsTest.java 2019-05-02 14:43:27.465246600 -0400 @@ -0,0 +1,124 @@ +/* + * 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(); + } + } + +} --- /dev/null 2019-05-02 14:43:44.000000000 -0400 +++ new/test/jdk/tools/jpackage/resources/license.txt 2019-05-02 14:43:40.261526100 -0400 @@ -0,0 +1 @@ +jpackage test license file (just some sample text). --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java 2019-05-02 14:43:55.649064700 -0400 +++ /dev/null 2019-05-02 14:43:57.000000000 -0400 @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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.tools.jlink.internal.packager; - - -import jdk.tools.jlink.builder.DefaultImageBuilder; -import jdk.tools.jlink.internal.Jlink; -import jdk.tools.jlink.internal.JlinkTask; -import jdk.tools.jlink.plugin.Plugin; - -import java.io.File; -import java.io.IOException; -import java.lang.module.ModuleFinder; -import java.nio.ByteOrder; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * AppRuntimeImageBuilder is a private API used only by the Java Packager to generate - * a Java runtime image using jlink. AppRuntimeImageBuilder encapsulates the - * arguments that jlink requires to generate this image. To create the image call the - * build() method. - */ -public final class AppRuntimeImageBuilder { - private Path outputDir = null; - private Map launchers = Collections.emptyMap(); - private List modulePath = null; - private Set addModules = null; - private Set limitModules = null; - private String excludeFileList = null; - private Map userArguments = null; - private Boolean stripNativeCommands = null; - - public AppRuntimeImageBuilder() {} - - public void setOutputDir(Path value) { - outputDir = value; - } - - public void setLaunchers(Map value) { - launchers = value; - } - - public void setModulePath(List value) { - modulePath = value; - } - - public void setAddModules(Set value) { - addModules = value; - } - - public void setLimitModules(Set value) { - limitModules = value; - } - - public void setExcludeFileList(String value) { - excludeFileList = value; - } - - public void setStripNativeCommands(boolean value) { - stripNativeCommands = value; - } - - public void setUserArguments(Map value) { - userArguments = value; - } - - public void build() throws IOException { - // jlink main arguments - Jlink.JlinkConfiguration jlinkConfig = - new Jlink.JlinkConfiguration(new File("").toPath(), // Unused - addModules, - ByteOrder.nativeOrder(), - moduleFinder(modulePath, - limitModules, addModules)); - - // plugin configuration - List plugins = new ArrayList(); - - if (stripNativeCommands) { - plugins.add(Jlink.newPlugin( - "strip-native-commands", - Collections.singletonMap("strip-native-commands", "on"), - null)); - } - - if (excludeFileList != null && !excludeFileList.isEmpty()) { - plugins.add(Jlink.newPlugin( - "exclude-files", - Collections.singletonMap("exclude-files", excludeFileList), - null)); - } - - // add user supplied jlink arguments - for (Map.Entry entry : userArguments.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - plugins.add(Jlink.newPlugin(key, - Collections.singletonMap(key, value), - null)); - } - - // build the image - Jlink.PluginsConfiguration pluginConfig = new Jlink.PluginsConfiguration( - plugins, new DefaultImageBuilder(outputDir, launchers), null); - Jlink jlink = new Jlink(); - jlink.build(jlinkConfig, pluginConfig); - } - - /* - * Returns a ModuleFinder that limits observability to the given root - * modules, their transitive dependences, plus a set of other modules. - */ - public static ModuleFinder moduleFinder(List modulepaths, - Set roots, - Set otherModules) { - return JlinkTask.newModuleFinder(modulepaths, roots, otherModules); - } -}