1 /* 2 * Copyright (c) 2017, 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 package jdk.jpackage.internal; 26 27 import java.io.*; 28 import java.nio.file.Files; 29 import java.nio.file.Path; 30 import java.nio.file.Paths; 31 import java.text.MessageFormat; 32 import java.util.*; 33 34 public class WinExeBundler extends AbstractBundler { 35 36 static { 37 System.loadLibrary("jpackage"); 38 } 39 40 private static final ResourceBundle I18N = ResourceBundle.getBundle( 41 "jdk.jpackage.internal.resources.WinResources"); 42 43 public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER 44 = new WindowsBundlerParam<>( 45 "win.app.bundler", 46 WinAppBundler.class, 47 params -> new WinAppBundler(), 48 null); 49 50 public static final BundlerParamInfo<File> EXE_IMAGE_DIR 51 = new WindowsBundlerParam<>( 52 "win.exe.imageDir", 53 File.class, 54 params -> { 55 File imagesRoot = IMAGES_ROOT.fetchFrom(params); 56 if (!imagesRoot.exists()) { 57 imagesRoot.mkdirs(); 58 } 59 return new File(imagesRoot, "win-exe.image"); 60 }, 61 (s, p) -> null); 62 63 private final static String EXE_WRAPPER_NAME = "msiwrapper.exe"; 64 65 @Override 66 public String getName() { 67 return getString("exe.bundler.name"); 68 } 69 70 @Override 71 public String getID() { 72 return "exe"; 73 } 74 75 @Override 76 public String getBundleType() { 77 return "INSTALLER"; 78 } 79 80 @Override 81 public File execute(Map<String, ? super Object> params, 82 File outputParentDir) throws PackagerException { 83 return bundle(params, outputParentDir); 84 } 85 86 @Override 87 public boolean supported(boolean platformInstaller) { 88 return WinMsiBundler.isSupported(); 89 } 90 91 @Override 92 public boolean validate(Map<String, ? super Object> params) 93 throws ConfigException { 94 return new WinMsiBundler().validate(params); 95 } 96 97 public File bundle(Map<String, ? super Object> params, File outdir) 98 throws PackagerException { 99 100 IOUtils.writableOutputDir(outdir.toPath()); 101 102 File exeImageDir = EXE_IMAGE_DIR.fetchFrom(params); 103 104 // Write msi to temporary directory. 105 File msi = new WinMsiBundler().bundle(params, exeImageDir); 106 107 try { 108 return buildEXE(msi, outdir); 109 } catch (IOException ex) { 110 Log.verbose(ex); 111 throw new PackagerException(ex); 112 } 113 } 114 115 private File buildEXE(File msi, File outdir) 116 throws IOException { 117 118 Log.verbose(MessageFormat.format( 119 getString("message.outputting-to-location"), 120 outdir.getAbsolutePath())); 121 122 // Copy template msi wrapper next to msi file 123 String exePath = msi.getAbsolutePath(); 124 exePath = exePath.substring(0, exePath.lastIndexOf('.')) + ".exe"; 125 try (InputStream is = getResourceAsStream(EXE_WRAPPER_NAME)) { 126 Files.copy(is, Path.of(exePath)); 127 } 128 // Embed msi in msi wrapper exe. 129 embedMSI(exePath, msi.getAbsolutePath()); 130 131 Path dstExePath = Paths.get(outdir.getAbsolutePath(), 132 Path.of(exePath).getFileName().toString()); 133 Files.deleteIfExists(dstExePath); 134 135 Files.copy(Path.of(exePath), dstExePath); 136 137 Log.verbose(MessageFormat.format( 138 getString("message.output-location"), 139 outdir.getAbsolutePath())); 140 141 return dstExePath.toFile(); 142 } 143 144 private static String getString(String key) 145 throws MissingResourceException { 146 return I18N.getString(key); 147 } 148 149 private static native int embedMSI(String exePath, String msiPath); 150 }