1 /* 2 * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.jpackage.internal; 27 28 import java.io.File; 29 import java.nio.file.Path; 30 import java.text.MessageFormat; 31 import java.util.Arrays; 32 import java.util.Collection; 33 import java.util.Map; 34 import java.util.ResourceBundle; 35 36 import static jdk.jpackage.internal.WindowsBundlerParam.*; 37 import static jdk.jpackage.internal.WinMsiBundler.WIN_APP_IMAGE; 38 39 public class WinAppBundler extends AbstractImageBundler { 40 41 private static final ResourceBundle I18N = ResourceBundle.getBundle( 42 "jdk.jpackage.internal.resources.WinResources"); 43 44 static final BundlerParamInfo<File> ICON_ICO = 45 new StandardBundlerParam<>( 46 "icon.ico", 47 File.class, 48 params -> { 49 File f = ICON.fetchFrom(params); 50 if (f != null && !f.getName().toLowerCase().endsWith(".ico")) { 51 Log.error(MessageFormat.format( 52 I18N.getString("message.icon-not-ico"), f)); 53 return null; 54 } 55 return f; 56 }, 57 (s, p) -> new File(s)); 58 59 @Override 60 public boolean validate(Map<String, ? super Object> params) 61 throws ConfigException { 62 try { 63 if (params == null) throw new ConfigException( 64 I18N.getString("error.parameters-null"), 65 I18N.getString("error.parameters-null.advice")); 66 67 return doValidate(params); 68 } catch (RuntimeException re) { 69 if (re.getCause() instanceof ConfigException) { 70 throw (ConfigException) re.getCause(); 71 } else { 72 throw new ConfigException(re); 73 } 74 } 75 } 76 77 // to be used by chained bundlers, e.g. by EXE bundler to avoid 78 // skipping validation if p.type does not include "image" 79 private boolean doValidate(Map<String, ? super Object> p) 80 throws ConfigException { 81 82 imageBundleValidation(p); 83 84 if (StandardBundlerParam.getPredefinedAppImage(p) != null) { 85 return true; 86 } 87 88 // Make sure that jpackage.exe exists. 89 File tool = new File( 90 System.getProperty("java.home") + "\\bin\\jpackage.exe"); 91 92 if (!tool.exists()) { 93 throw new ConfigException( 94 I18N.getString("error.no-windows-resources"), 95 I18N.getString("error.no-windows-resources.advice")); 96 } 97 98 return true; 99 } 100 101 private static boolean usePredefineAppName(Map<String, ? super Object> p) { 102 return (PREDEFINED_APP_IMAGE.fetchFrom(p) != null); 103 } 104 105 private static String appName; 106 synchronized static String getAppName( 107 Map<String, ? super Object> p) { 108 // If we building from predefined app image, then we should use names 109 // from image and not from CLI. 110 if (usePredefineAppName(p)) { 111 if (appName == null) { 112 // Use WIN_APP_IMAGE here, since we already copy pre-defined 113 // image to WIN_APP_IMAGE 114 File appImageDir = WIN_APP_IMAGE.fetchFrom(p); 115 116 File appDir = new File(appImageDir.toString() + "\\app"); 117 File [] files = appDir.listFiles( 118 (File dir, String name) -> name.endsWith(".cfg")); 119 if (files == null || files.length == 0) { 120 String name = APP_NAME.fetchFrom(p); 121 Path exePath = appImageDir.toPath().resolve(name + ".exe"); 122 Path icoPath = appImageDir.toPath().resolve(name + ".ico"); 123 if (exePath.toFile().exists() && 124 icoPath.toFile().exists()) { 125 return name; 126 } else { 127 throw new RuntimeException(MessageFormat.format( 128 I18N.getString("error.cannot-find-launcher"), 129 appImageDir)); 130 } 131 } else { 132 appName = files[0].getName(); 133 int index = appName.indexOf("."); 134 if (index != -1) { 135 appName = appName.substring(0, index); 136 } 137 if (files.length > 1) { 138 Log.error(MessageFormat.format(I18N.getString( 139 "message.multiple-launchers"), appName)); 140 } 141 } 142 return appName; 143 } else { 144 return appName; 145 } 146 } 147 148 return APP_NAME.fetchFrom(p); 149 } 150 151 public static String getLauncherRelativePath( 152 Map<String, ? super Object> p) { 153 return getAppName(p) + ".exe"; 154 } 155 156 public boolean bundle(Map<String, ? super Object> p, File outputDirectory) 157 throws PackagerException { 158 return doBundle(p, outputDirectory, false) != null; 159 } 160 161 File doBundle(Map<String, ? super Object> p, File outputDirectory, 162 boolean dependentTask) throws PackagerException { 163 if (StandardBundlerParam.isRuntimeInstaller(p)) { 164 return PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); 165 } else { 166 return doAppBundle(p, outputDirectory, dependentTask); 167 } 168 } 169 170 File doAppBundle(Map<String, ? super Object> p, File outputDirectory, 171 boolean dependentTask) throws PackagerException { 172 try { 173 File rootDirectory = createRoot(p, outputDirectory, dependentTask, 174 APP_NAME.fetchFrom(p)); 175 AbstractAppImageBuilder appBuilder = 176 new WindowsAppImageBuilder(p, outputDirectory.toPath()); 177 if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) { 178 JLinkBundlerHelper.execute(p, appBuilder); 179 } else { 180 StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder); 181 } 182 if (!dependentTask) { 183 Log.verbose(MessageFormat.format( 184 I18N.getString("message.result-dir"), 185 outputDirectory.getAbsolutePath())); 186 } 187 return rootDirectory; 188 } catch (PackagerException pe) { 189 throw pe; 190 } catch (Exception e) { 191 Log.verbose(e); 192 throw new PackagerException(e); 193 } 194 } 195 196 @Override 197 public String getName() { 198 return I18N.getString("app.bundler.name"); 199 } 200 201 @Override 202 public String getID() { 203 return "windows.app"; 204 } 205 206 @Override 207 public String getBundleType() { 208 return "IMAGE"; 209 } 210 211 @Override 212 public File execute(Map<String, ? super Object> params, 213 File outputParentDir) throws PackagerException { 214 return doBundle(params, outputParentDir, false); 215 } 216 217 @Override 218 public boolean supported(boolean platformInstaller) { 219 return true; 220 } 221 222 @Override 223 public boolean isDefault() { 224 return false; 225 } 226 227 }