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 com.oracle.tools.packager.linux;
27
28 import com.oracle.tools.packager.AbstractImageBundler;
29 import com.oracle.tools.packager.BundlerParamInfo;
30 import com.oracle.tools.packager.JreUtils;
31 import com.oracle.tools.packager.JreUtils.Rule;
32 import com.oracle.tools.packager.StandardBundlerParam;
33 import com.oracle.tools.packager.Log;
34 import com.oracle.tools.packager.ConfigException;
35 import com.oracle.tools.packager.IOUtils;
36 import com.oracle.tools.packager.RelativeFileSet;
37 import com.oracle.tools.packager.UnsupportedPlatformException;
38 import com.sun.javafx.tools.packager.bundlers.BundleParams;
39
40 import java.io.ByteArrayOutputStream;
41 import java.io.File;
42 import java.io.FileNotFoundException;
43 import java.io.IOException;
44 import java.io.PrintStream;
45 import java.net.MalformedURLException;
46 import java.net.URL;
47 import java.text.MessageFormat;
48 import java.util.*;
49
50 import static com.oracle.tools.packager.StandardBundlerParam.*;
51
52 public class LinuxAppBundler extends AbstractImageBundler {
53
54 private static final ResourceBundle I18N =
55 ResourceBundle.getBundle(LinuxAppBundler.class.getName());
56
57 protected static final String LINUX_BUNDLER_PREFIX =
58 BUNDLER_PREFIX + "linux" + File.separator;
59 private static final String EXECUTABLE_NAME = "JavaAppLauncher";
60 private static final String LIBRARY_NAME = "libpackager.so";
61
62 public static final BundlerParamInfo<File> ICON_PNG = new StandardBundlerParam<>(
63 I18N.getString("param.icon-png.name"),
64 I18N.getString("param.icon-png.description"),
65 "icon.png",
66 File.class,
67 params -> {
68 File f = ICON.fetchFrom(params);
69 if (f != null && !f.getName().toLowerCase().endsWith(".png")) {
70 Log.info(MessageFormat.format(I18N.getString("message.icon-not-png"), f));
71 return null;
72 }
73 return f;
74 },
75 (s, p) -> new File(s));
76
77 public static final BundlerParamInfo<URL> RAW_EXECUTABLE_URL = new StandardBundlerParam<>(
78 I18N.getString("param.raw-executable-url.name"),
79 I18N.getString("param.raw-executable-url.description"),
80 "linux.launcher.url",
134 } else {
135 throw new ConfigException(re);
136 }
137 }
138 }
139
140 //used by chained bundlers to reuse validation logic
141 boolean doValidate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
142 if (!System.getProperty("os.name").toLowerCase().startsWith("linux")) {
143 throw new UnsupportedPlatformException();
144 }
145
146 imageBundleValidation(p);
147
148 if (RAW_EXECUTABLE_URL.fetchFrom(p) == null) {
149 throw new ConfigException(
150 I18N.getString("error.no-linux-resources"),
151 I18N.getString("error.no-linux-resources.advice"));
152 }
153
154 //validate required inputs
155 testRuntime(LINUX_RUNTIME.fetchFrom(p), new String[] {
156 "lib/[^/]+/[^/]+/libjvm.so", // most reliable
157 "lib/rt.jar", // fallback canary for JDK 8
158 });
159 if (USE_FX_PACKAGING.fetchFrom(p)) {
160 testRuntime(LINUX_RUNTIME.fetchFrom(p), new String[] {"lib/ext/jfxrt.jar", "lib/jfxrt.jar"});
161 }
162
163 return true;
164 }
165
166 //it is static for the sake of sharing with "installer" bundlers
167 // that may skip calls to validate/bundle in this class!
168 public static File getRootDir(File outDir, Map<String, ? super Object> p) {
169 return new File(outDir, APP_FS_NAME.fetchFrom(p));
170 }
171
172 public static String getLauncherName(Map<String, ? super Object> p) {
173 return APP_FS_NAME.fetchFrom(p);
174 }
175
176 public static String getLauncherCfgName(Map<String, ? super Object> p) {
177 return "app/" + APP_FS_NAME.fetchFrom(p) +".cfg";
178 }
179
180 File doBundle(Map<String, ? super Object> p, File outputDirectory, boolean dependentTask) {
181 Map<String, ? super Object> originalParams = new HashMap<>(p);
182 try {
183 if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) {
184 throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-create-output-dir"), outputDirectory.getAbsolutePath()));
185 }
186 if (!outputDirectory.canWrite()) {
187 throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-write-to-output-dir"), outputDirectory.getAbsolutePath()));
188 }
189
190 // Create directory structure
191 File rootDirectory = getRootDir(outputDirectory, p);
192 IOUtils.deleteRecursive(rootDirectory);
193 rootDirectory.mkdirs();
194
195 if (!dependentTask) {
196 Log.info(MessageFormat.format(I18N.getString("message.creating-bundle-location"), rootDirectory.getAbsolutePath()));
197 }
198
199 File runtimeDirectory = new File(rootDirectory, "runtime");
200
201 File appDirectory = new File(rootDirectory, "app");
202 appDirectory.mkdirs();
203
204 // create the primary launcher
205 createLauncherForEntryPoint(p, rootDirectory);
206
207 // Copy library to the launcher folder
208 IOUtils.copyFromURL(
209 LinuxResources.class.getResource(LIBRARY_NAME),
210 new File(rootDirectory, LIBRARY_NAME));
211
212 // create the secondary launchers, if any
213 List<Map<String, ? super Object>> entryPoints = StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(p);
214 for (Map<String, ? super Object> entryPoint : entryPoints) {
215 Map<String, ? super Object> tmp = new HashMap<>(originalParams);
216 tmp.putAll(entryPoint);
217 createLauncherForEntryPoint(tmp, rootDirectory);
218 }
219
220 // Copy runtime to PlugIns folder
221 copyRuntime(p, runtimeDirectory);
222
223 // Copy class path entries to Java folder
224 copyApplication(p, appDirectory);
225
226 // Copy icon to Resources folder
227 //FIXME copyIcon(resourcesDirectory);
228
229 return rootDirectory;
230 } catch (IOException ex) {
231 Log.info("Exception: "+ex);
232 Log.debug(ex);
233 return null;
234 }
235 }
236
237 private void createLauncherForEntryPoint(Map<String, ? super Object> p, File rootDir) throws IOException {
238 // Copy executable to Linux folder
239 File executableFile = new File(rootDir, getLauncherName(p));
240 IOUtils.copyFromURL(
241 RAW_EXECUTABLE_URL.fetchFrom(p),
242 executableFile);
243
244 executableFile.setExecutable(true, false);
245 executableFile.setWritable(true, true); //for str
246
247 // Generate launcher .cfg file
248 if (LAUNCHER_CFG_FORMAT.fetchFrom(p).equals(CFG_FORMAT_PROPERTIES)) {
249 writeCfgFile(p, rootDir);
250 } else {
251 writeCfgFile(p, new File(rootDir, getLauncherCfgName(p)), getRuntimeLocation(p));
252 }
253 }
254
255 private void copyApplication(Map<String, ? super Object> params, File appDirectory) throws IOException {
256 List<RelativeFileSet> appResourcesList = APP_RESOURCES_LIST.fetchFrom(params);
257 if (appResourcesList == null) {
258 throw new RuntimeException("Null app resources?");
259 }
260 for (RelativeFileSet appResources : appResourcesList) {
261 if (appResources == null) {
262 throw new RuntimeException("Null app resources?");
263 }
264 File srcdir = appResources.getBaseDirectory();
265 for (String fname : appResources.getIncludedFiles()) {
266 IOUtils.copyFile(
267 new File(srcdir, fname), new File(appDirectory, fname));
268 }
269 }
270 }
271
272 private String getRuntimeLocation(Map<String, ? super Object> params) {
273 if (LINUX_RUNTIME.fetchFrom(params) == null) {
274 return "";
275 } else {
276 return "$APPDIR/runtime";
277 }
278 }
279
280 private void writeCfgFile(Map<String, ? super Object> params, File rootDir) throws FileNotFoundException {
281 File cfgFile = new File(rootDir, getLauncherCfgName(params));
282
283 cfgFile.delete();
284 PrintStream out = new PrintStream(cfgFile);
285 out.println("app.runtime=" + getRuntimeLocation(params));
286 out.println("app.mainjar=" + MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next());
287 out.println("app.version=" + VERSION.fetchFrom(params));
288
289 //use '/' in the class name (instead of '.' to simplify native code
290 out.println("app.mainclass=" +
291 MAIN_CLASS.fetchFrom(params).replaceAll("\\.", "/"));
292
293 StringBuilder macroedPath = new StringBuilder();
294 for (String s : CLASSPATH.fetchFrom(params).split("[ ;:]+")) {
295 macroedPath.append(s);
296 macroedPath.append(":");
297 }
298 macroedPath.deleteCharAt(macroedPath.length() - 1);
299 out.println("app.classpath=" + macroedPath.toString());
300
301 List<String> jvmargs = JVM_OPTIONS.fetchFrom(params);
302 int idx = 1;
303 for (String a : jvmargs) {
304 out.println("jvmarg."+idx+"="+a);
305 idx++;
306 }
307 Map<String, String> jvmProps = JVM_PROPERTIES.fetchFrom(params);
308 for (Map.Entry<String, String> entry : jvmProps.entrySet()) {
309 out.println("jvmarg."+idx+"=-D"+entry.getKey()+"="+entry.getValue());
310 idx++;
311 }
312
313 String preloader = PRELOADER_CLASS.fetchFrom(params);
314 if (preloader != null) {
315 out.println("jvmarg."+idx+"=-Djavafx.preloader="+preloader);
316 }
317
318 //app.id required for setting user preferences (Java Preferences API)
319 out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params));
320 out.println("app.identifier=" + IDENTIFIER.fetchFrom(params));
321
322 Map<String, String> overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params);
323 idx = 1;
324 for (Map.Entry<String, String> arg: overridableJVMOptions.entrySet()) {
325 if (arg.getKey() == null || arg.getValue() == null) {
326 Log.info(I18N.getString("message.jvm-user-arg-is-null"));
327 }
328 else {
329 out.println("jvmuserarg."+idx+".name="+arg.getKey());
330 out.println("jvmuserarg."+idx+".value="+arg.getValue());
331 }
332 idx++;
333 }
334
335 // add command line args
336 List<String> args = ARGUMENTS.fetchFrom(params);
337 idx = 1;
338 for (String a : args) {
339 out.println("arg."+idx+"="+a);
340 idx++;
341 }
342
343 out.close();
344 }
345
346 private void copyRuntime(Map<String, ? super Object> params, File runtimeDirectory) throws IOException {
347 RelativeFileSet runtime = LINUX_RUNTIME.fetchFrom(params);
348 if (runtime == null) {
349 //request to use system runtime
350 return;
351 }
352 runtimeDirectory.mkdirs();
353
354 File srcdir = runtime.getBaseDirectory();
355 Set<String> filesToCopy = runtime.getIncludedFiles();
356 for (String fname : filesToCopy) {
357 IOUtils.copyFile(
358 new File(srcdir, fname), new File(runtimeDirectory, fname));
359 }
360 }
361
362 @Override
363 public String getName() {
364 return I18N.getString("bundler.name");
365 }
366
367 @Override
368 public String getDescription() {
369 return I18N.getString("bundler.description");
370 }
371
372 @Override
373 public String getID() {
374 return "linux.app";
375 }
376
377 @Override
378 public String getBundleType() {
379 return "IMAGE";
380 }
381
389 APP_NAME,
390 APP_RESOURCES,
391 // APP_RESOURCES_LIST, // ??
392 ARGUMENTS,
393 CLASSPATH,
394 JVM_OPTIONS,
395 JVM_PROPERTIES,
396 LINUX_RUNTIME,
397 MAIN_CLASS,
398 MAIN_JAR,
399 PREFERENCES_ID,
400 PRELOADER_CLASS,
401 USER_JVM_OPTIONS,
402 VERSION
403 );
404 }
405
406 @Override
407 public File execute(Map<String, ? super Object> params, File outputParentDir) {
408 return doBundle(params, outputParentDir, false);
409 }
410
411 @Override
412 protected String getCacheLocation(Map<String, ? super Object> params) {
413 return "$CACHEDIR/";
414 }
415
416 @Override
417 public void extractRuntimeFlags(Map<String, ? super Object> params) {
418 if (params.containsKey(".runtime.autodetect")) return;
419
420 params.put(".runtime.autodetect", "attempted");
421 RelativeFileSet runtime = LINUX_RUNTIME.fetchFrom(params);
422 String commandline;
423 if (runtime == null) {
424 //System JRE, report nothing useful
425 params.put(".runtime.autodetect", "systemjre");
426 } else {
427 File runtimePath = runtime.getBaseDirectory();
428 File launcherPath = new File(runtimePath, "bin/java");
429
430 ProcessBuilder pb = new ProcessBuilder(launcherPath.getAbsolutePath(), "-version");
431 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
432 try (PrintStream pout = new PrintStream(baos)) {
433 IOUtils.exec(pb, Log.isDebug(), true, pout);
434 }
435
436 commandline = baos.toString();
437 } catch (IOException e) {
438 e.printStackTrace();
439 params.put(".runtime.autodetect", "failed");
440 return;
441 }
442 AbstractImageBundler.extractFlagsFromVersion(params, commandline);
443 params.put(".runtime.autodetect", "succeeded");
444 }
445 }
446 }
|
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 com.oracle.tools.packager.linux;
27
28 import com.oracle.tools.packager.AbstractImageBundler;
29 import com.oracle.tools.packager.BundlerParamInfo;
30 import com.oracle.tools.packager.ConfigException;
31 import com.oracle.tools.packager.IOUtils;
32 import com.oracle.tools.packager.JLinkBundlerHelper;
33 import com.oracle.tools.packager.JreUtils;
34 import com.oracle.tools.packager.JreUtils.Rule;
35 import com.oracle.tools.packager.Log;
36 import com.oracle.tools.packager.RelativeFileSet;
37 import com.oracle.tools.packager.StandardBundlerParam;
38 import com.oracle.tools.packager.UnsupportedPlatformException;
39 import com.sun.javafx.tools.packager.bundlers.BundleParams;
40 import jdk.tools.jlink.builder.ImageBuilder;
41 import jdk.packager.builders.linux.LinuxAppImageBuilder;
42
43 import java.io.File;
44 import java.io.IOException;
45 import java.net.MalformedURLException;
46 import java.net.URL;
47 import java.text.MessageFormat;
48 import java.util.Arrays;
49 import java.util.Collection;
50 import java.util.Map;
51 import java.util.ResourceBundle;
52
53 import static com.oracle.tools.packager.StandardBundlerParam.*;
54
55 public class LinuxAppBundler extends AbstractImageBundler {
56
57 private static final ResourceBundle I18N =
58 ResourceBundle.getBundle(LinuxAppBundler.class.getName());
59
60 protected static final String LINUX_BUNDLER_PREFIX =
61 BUNDLER_PREFIX + "linux" + File.separator;
62 private static final String EXECUTABLE_NAME = "JavaAppLauncher";
63
64 public static final BundlerParamInfo<File> ICON_PNG = new StandardBundlerParam<>(
65 I18N.getString("param.icon-png.name"),
66 I18N.getString("param.icon-png.description"),
67 "icon.png",
68 File.class,
69 params -> {
70 File f = ICON.fetchFrom(params);
71 if (f != null && !f.getName().toLowerCase().endsWith(".png")) {
72 Log.info(MessageFormat.format(I18N.getString("message.icon-not-png"), f));
73 return null;
74 }
75 return f;
76 },
77 (s, p) -> new File(s));
78
79 public static final BundlerParamInfo<URL> RAW_EXECUTABLE_URL = new StandardBundlerParam<>(
80 I18N.getString("param.raw-executable-url.name"),
81 I18N.getString("param.raw-executable-url.description"),
82 "linux.launcher.url",
136 } else {
137 throw new ConfigException(re);
138 }
139 }
140 }
141
142 //used by chained bundlers to reuse validation logic
143 boolean doValidate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
144 if (!System.getProperty("os.name").toLowerCase().startsWith("linux")) {
145 throw new UnsupportedPlatformException();
146 }
147
148 imageBundleValidation(p);
149
150 if (RAW_EXECUTABLE_URL.fetchFrom(p) == null) {
151 throw new ConfigException(
152 I18N.getString("error.no-linux-resources"),
153 I18N.getString("error.no-linux-resources.advice"));
154 }
155
156 return true;
157 }
158
159 //it is static for the sake of sharing with "installer" bundlers
160 // that may skip calls to validate/bundle in this class!
161 public static File getRootDir(File outDir, Map<String, ? super Object> p) {
162 return new File(outDir, APP_FS_NAME.fetchFrom(p));
163 }
164
165 public static String getLauncherCfgName(Map<String, ? super Object> p) {
166 return "app/" + APP_FS_NAME.fetchFrom(p) +".cfg";
167 }
168
169 File doBundle(Map<String, ? super Object> p, File outputDirectory, boolean dependentTask) {
170 try {
171 if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) {
172 throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-create-output-dir"), outputDirectory.getAbsolutePath()));
173 }
174 if (!outputDirectory.canWrite()) {
175 throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-write-to-output-dir"), outputDirectory.getAbsolutePath()));
176 }
177
178 // Create directory structure
179 File rootDirectory = getRootDir(outputDirectory, p);
180 IOUtils.deleteRecursive(rootDirectory);
181 rootDirectory.mkdirs();
182
183 if (!dependentTask) {
184 Log.info(MessageFormat.format(I18N.getString("message.creating-bundle-location"), rootDirectory.getAbsolutePath()));
185 }
186
187 if (!p.containsKey(JLinkBundlerHelper.JLINK_BUILDER.getID())) {
188 p.put(JLinkBundlerHelper.JLINK_BUILDER.getID(), "linuxapp-image-builder");
189 }
190
191 ImageBuilder imageBuilder = new LinuxAppImageBuilder(p, outputDirectory.toPath());
192 JLinkBundlerHelper.execute(p, outputDirectory, imageBuilder);
193
194 return rootDirectory;
195 } catch (IOException ex) {
196 Log.info("Exception: "+ex);
197 Log.debug(ex);
198 return null;
199 }
200 }
201
202 @Override
203 public String getName() {
204 return I18N.getString("bundler.name");
205 }
206
207 @Override
208 public String getDescription() {
209 return I18N.getString("bundler.description");
210 }
211
212 @Override
213 public String getID() {
214 return "linux.app";
215 }
216
217 @Override
218 public String getBundleType() {
219 return "IMAGE";
220 }
221
229 APP_NAME,
230 APP_RESOURCES,
231 // APP_RESOURCES_LIST, // ??
232 ARGUMENTS,
233 CLASSPATH,
234 JVM_OPTIONS,
235 JVM_PROPERTIES,
236 LINUX_RUNTIME,
237 MAIN_CLASS,
238 MAIN_JAR,
239 PREFERENCES_ID,
240 PRELOADER_CLASS,
241 USER_JVM_OPTIONS,
242 VERSION
243 );
244 }
245
246 @Override
247 public File execute(Map<String, ? super Object> params, File outputParentDir) {
248 return doBundle(params, outputParentDir, false);
249 }
250 }
|