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 final static String DEFAULT_ICON = "java32.png"; 50 51 private final ApplicationLayout appLayout; 52 53 public static final BundlerParamInfo<File> ICON_PNG = 54 new StandardBundlerParam<>( 55 "icon.png", 56 File.class, 57 params -> { 58 File f = ICON.fetchFrom(params); 59 if (f != null && !f.getName().toLowerCase().endsWith(".png")) { 60 Log.error(MessageFormat.format(I18N.getString( 61 "message.icon-not-png"), f)); 62 return null; 63 } 64 return f; 65 }, 66 (s, p) -> new File(s)); 67 68 private static ApplicationLayout createAppLayout(Map<String, Object> params, 69 Path imageOutDir) { 70 return ApplicationLayout.linuxApp().resolveAt( 71 imageOutDir.resolve(APP_NAME.fetchFrom(params))); 72 } 73 74 public LinuxAppImageBuilder(Map<String, Object> params, Path imageOutDir) 75 throws IOException { 76 super(params, createAppLayout(params, imageOutDir).runtimeDirectory()); 77 78 appLayout = createAppLayout(params, imageOutDir); 79 } 80 81 private void writeEntry(InputStream in, Path dstFile) throws IOException { 82 Files.createDirectories(dstFile.getParent()); 83 Files.copy(in, dstFile); 84 } 85 86 public static String getLauncherName(Map<String, ? super Object> params) { 87 return APP_NAME.fetchFrom(params); 88 } 89 90 private Path getLauncherCfgPath(Map<String, ? super Object> params) { 91 return appLayout.appDirectory().resolve( 92 APP_NAME.fetchFrom(params) + ".cfg"); 93 } 94 95 @Override 96 public Path getAppDir() { 97 return appLayout.appDirectory(); 98 } 99 100 @Override 101 public Path getAppModsDir() { 102 return appLayout.appModsDirectory(); 103 } 104 105 @Override 106 protected String getCfgAppDir() { 107 return Path.of("$APPDIR").resolve( 108 ApplicationLayout.linuxApp().appDirectory()).toString() + File.separator; 109 } 110 111 @Override 112 protected String getCfgRuntimeDir() { 113 return Path.of("$APPDIR").resolve( 114 ApplicationLayout.linuxApp().runtimeDirectory()).toString(); 115 } 116 117 @Override 118 public void prepareApplicationFiles(Map<String, ? super Object> params) 119 throws IOException { 120 Map<String, ? super Object> originalParams = new HashMap<>(params); 121 122 appLayout.roots().stream().forEach(dir -> { 123 try { 124 IOUtils.writableOutputDir(dir); 125 } catch (PackagerException pe) { 126 throw new RuntimeException(pe); 127 } 128 }); 129 130 // create the primary launcher 131 createLauncherForEntryPoint(params); 132 133 // Copy library to the launcher folder 134 try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { 135 writeEntry(is_lib, appLayout.dllDirectory().resolve(LIBRARY_NAME)); 136 } 137 138 // create the additional launchers, if any 139 List<Map<String, ? super Object>> entryPoints 140 = StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); 141 for (Map<String, ? super Object> entryPoint : entryPoints) { 142 createLauncherForEntryPoint( 143 AddLauncherArguments.merge(originalParams, entryPoint)); 144 } 145 146 // Copy class path entries to Java folder 147 copyApplication(params); 148 149 // Copy icon to Resources folder 150 copyIcon(params); 151 } 152 153 @Override 154 public void prepareJreFiles(Map<String, ? super Object> params) 155 throws IOException {} 156 157 private void createLauncherForEntryPoint( 158 Map<String, ? super Object> params) throws IOException { 159 // Copy executable to launchers folder 160 Path executableFile = appLayout.launchersDirectory().resolve(getLauncherName(params)); 161 try (InputStream is_launcher = 162 getResourceAsStream("jpackageapplauncher")) { 163 writeEntry(is_launcher, executableFile); 164 } 165 166 executableFile.toFile().setExecutable(true, false); 167 executableFile.toFile().setWritable(true, true); 168 169 writeCfgFile(params, getLauncherCfgPath(params).toFile()); 170 } 171 172 private void copyIcon(Map<String, ? super Object> params) 173 throws IOException { 174 175 File icon = ICON_PNG.fetchFrom(params); 176 File iconTarget = appLayout.destktopIntegrationDirectory().resolve( 177 APP_NAME.fetchFrom(params) + ".png").toFile(); 178 179 InputStream in = locateResource( 180 iconTarget.getName(), 181 "icon", 182 DEFAULT_ICON, 183 icon, 184 VERBOSE.fetchFrom(params), 185 RESOURCE_DIR.fetchFrom(params)); 186 187 Files.copy(in, iconTarget.toPath(), StandardCopyOption.REPLACE_EXISTING); 188 } 189 190 private void copyApplication(Map<String, ? super Object> params) 191 throws IOException { 192 for (RelativeFileSet appResources : 193 APP_RESOURCES_LIST.fetchFrom(params)) { 194 if (appResources == null) { 195 throw new RuntimeException("Null app resources?"); 196 } 197 File srcdir = appResources.getBaseDirectory(); 198 for (String fname : appResources.getIncludedFiles()) { 199 copyEntry(appLayout.appDirectory(), srcdir, fname); 200 } 201 } 202 } 203 204 }