1 /* 2 * Copyright (c) 2015, 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.io.IOException; 30 import java.io.InputStream; 31 import java.nio.file.Files; 32 import java.nio.file.Path; 33 import java.nio.file.StandardCopyOption; 34 import java.text.MessageFormat; 35 import java.util.HashMap; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Objects; 39 import java.util.ResourceBundle; 40 41 import static jdk.jpackage.internal.StandardBundlerParam.*; 42 43 public class LinuxAppImageBuilder extends AbstractAppImageBuilder { 44 45 private static final ResourceBundle I18N = ResourceBundle.getBundle( 46 "jdk.jpackage.internal.resources.LinuxResources"); 47 48 private static final String LIBRARY_NAME = "libapplauncher.so"; 49 private final static String DEFAULT_ICON = "java32.png"; 50 51 private final Path root; 52 private final Path appDir; 53 private final Path appModsDir; 54 private final Path runtimeDir; 55 private final Path binDir; 56 private final Path mdir; 57 58 public static final BundlerParamInfo<File> ICON_PNG = 59 new StandardBundlerParam<>( 60 "icon.png", 61 File.class, 62 params -> { 63 File f = ICON.fetchFrom(params); 64 if (f != null && !f.getName().toLowerCase().endsWith(".png")) { 65 Log.error(MessageFormat.format(I18N.getString( 66 "message.icon-not-png"), f)); 67 return null; 68 } 69 return f; 70 }, 71 (s, p) -> new File(s)); 72 73 public LinuxAppImageBuilder(Map<String, Object> params, Path imageOutDir) 74 throws IOException { 75 super(params, 76 imageOutDir.resolve(APP_NAME.fetchFrom(params) + "/runtime")); 77 78 Objects.requireNonNull(imageOutDir); 79 80 this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params)); 81 this.appDir = root.resolve("app"); 82 this.appModsDir = appDir.resolve("mods"); 83 this.runtimeDir = root.resolve("runtime"); 84 this.binDir = root.resolve("bin"); 85 this.mdir = runtimeDir.resolve("lib"); 86 Files.createDirectories(appDir); 87 Files.createDirectories(runtimeDir); 88 } 89 90 public LinuxAppImageBuilder(String appName, Path imageOutDir) 91 throws IOException { 92 super(null, imageOutDir.resolve(appName)); 93 94 Objects.requireNonNull(imageOutDir); 95 96 this.root = imageOutDir.resolve(appName); 97 this.appDir = null; 98 this.appModsDir = null; 99 this.runtimeDir = null; 100 this.binDir = null; 101 this.mdir = null; 102 } 103 104 private void writeEntry(InputStream in, Path dstFile) throws IOException { 105 Files.createDirectories(dstFile.getParent()); 106 Files.copy(in, dstFile); 107 } 108 109 // it is static for the sake of sharing with "installer" bundlers 110 // that may skip calls to validate/bundle in this class! 111 public static File getRootDir(File outDir, 112 Map<String, ? super Object> params) { 113 return new File(outDir, APP_NAME.fetchFrom(params)); 114 } 115 116 public static String getLauncherRelativePath( 117 Map<String, ? super Object> params) { 118 return "bin" + File.separator + APP_NAME.fetchFrom(params); 119 } 120 121 private static String getLauncherName(Map<String, ? super Object> params) { 122 return APP_NAME.fetchFrom(params); 123 } 124 125 public static String getLauncherCfgName( 126 Map<String, ? super Object> params) { 127 return "app" + File.separator + APP_NAME.fetchFrom(params) + ".cfg"; 128 } 129 130 @Override 131 public Path getAppDir() { 132 return appDir; 133 } 134 135 @Override 136 public Path getAppModsDir() { 137 return appModsDir; 138 } 139 140 @Override 141 public void prepareApplicationFiles(Map<String, ? super Object> params) 142 throws IOException { 143 Map<String, ? super Object> originalParams = new HashMap<>(params); 144 145 try { 146 IOUtils.writableOutputDir(root); 147 IOUtils.writableOutputDir(binDir); 148 } catch (PackagerException pe) { 149 throw new RuntimeException(pe); 150 } 151 152 // create the primary launcher 153 createLauncherForEntryPoint(params); 154 155 // Copy library to the launcher folder 156 try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { 157 writeEntry(is_lib, binDir.resolve(LIBRARY_NAME)); 158 } 159 160 // create the additional launchers, if any 161 List<Map<String, ? super Object>> entryPoints 162 = StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); 163 for (Map<String, ? super Object> entryPoint : entryPoints) { 164 createLauncherForEntryPoint( 165 AddLauncherArguments.merge(originalParams, entryPoint)); 166 } 167 168 // Copy class path entries to Java folder 169 copyApplication(params); 170 171 // Copy icon to Resources folder 172 copyIcon(params); 173 } 174 175 @Override 176 public void prepareJreFiles(Map<String, ? super Object> params) 177 throws IOException {} 178 179 private void createLauncherForEntryPoint( 180 Map<String, ? super Object> params) throws IOException { 181 // Copy executable to Linux folder 182 Path executableFile = binDir.resolve(getLauncherName(params)); 183 try (InputStream is_launcher = 184 getResourceAsStream("jpackageapplauncher")) { 185 writeEntry(is_launcher, executableFile); 186 } 187 188 executableFile.toFile().setExecutable(true, false); 189 executableFile.toFile().setWritable(true, true); 190 191 writeCfgFile(params, root.resolve(getLauncherCfgName(params)).toFile()); 192 } 193 194 private void copyIcon(Map<String, ? super Object> params) 195 throws IOException { 196 197 File icon = ICON_PNG.fetchFrom(params); 198 File iconTarget = binDir.resolve(APP_NAME.fetchFrom(params) + ".png").toFile(); 199 200 InputStream in = locateResource( 201 iconTarget.getName(), 202 "icon", 203 DEFAULT_ICON, 204 icon, 205 VERBOSE.fetchFrom(params), 206 RESOURCE_DIR.fetchFrom(params)); 207 208 Files.copy(in, iconTarget.toPath(), StandardCopyOption.REPLACE_EXISTING); 209 } 210 211 private void copyApplication(Map<String, ? super Object> params) 212 throws IOException { 213 for (RelativeFileSet appResources : 214 APP_RESOURCES_LIST.fetchFrom(params)) { 215 if (appResources == null) { 216 throw new RuntimeException("Null app resources?"); 217 } 218 File srcdir = appResources.getBaseDirectory(); 219 for (String fname : appResources.getIncludedFiles()) { 220 copyEntry(appDir, srcdir, fname); 221 } 222 } 223 } 224 225 }