From bd07ffb1a906a1aee6a91c1c6954118afed48519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pereda?= Date: Thu, 2 Sep 2021 23:10:00 +0200 Subject: [PATCH] Add Web profile (#995) * Add Web Profile * Remove debug file * Extract files from META-INF/substrate/web * Add nativerun goal * Use release version of webscheduler Co-authored-by: jose.pereda --- build.gradle | 1 + .../java/com/gluonhq/substrate/Constants.java | 12 +- .../substrate/SubstrateDispatcher.java | 6 +- .../com/gluonhq/substrate/model/Triplet.java | 7 +- .../target/WebTargetConfiguration.java | 318 ++++++++++++++++++ .../substrate/util/web/AheadOfTimeBase.java | 215 ++++++++++++ src/main/java/module-info.java | 3 + 7 files changed, 558 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/gluonhq/substrate/target/WebTargetConfiguration.java create mode 100644 src/main/java/com/gluonhq/substrate/util/web/AheadOfTimeBase.java diff --git a/build.gradle b/build.gradle index 525d83d19..11bb15183 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,7 @@ dependencies { implementation 'com.googlecode.plist:dd-plist:1.22' implementation 'org.bouncycastle:bcpkix-jdk15on:1.49' implementation 'org.graalvm.nativeimage:svm:21.1.0' + implementation 'org.apidesign.bck2brwsr:aot:0.51' testImplementation gradleTestKit() testImplementation "org.junit.jupiter:junit-jupiter-api:$jUnitVersion" diff --git a/src/main/java/com/gluonhq/substrate/Constants.java b/src/main/java/com/gluonhq/substrate/Constants.java index 63b735173..d97d755b5 100644 --- a/src/main/java/com/gluonhq/substrate/Constants.java +++ b/src/main/java/com/gluonhq/substrate/Constants.java @@ -48,6 +48,7 @@ public class Constants { public static final String VENDOR_APPLE = "apple"; public static final String VENDOR_LINUX = "linux"; public static final String VENDOR_MICROSOFT = "microsoft"; + public static final String VENDOR_WEB = "web"; /** * Triplet OS @@ -57,6 +58,7 @@ public class Constants { public static final String OS_LINUX = "linux"; public static final String OS_WINDOWS = "windows"; public static final String OS_ANDROID = "android"; + public static final String OS_WEB = "web"; /** * Predefined Profiles @@ -68,7 +70,8 @@ public enum Profile { WINDOWS, // (x86_64-windows-windows) IOS, // (aarch64-apple-ios) IOS_SIM, // (x86_64-apple-ios) - ANDROID // (aarch64-linux-android); + ANDROID, // (aarch64-linux-android); + WEB // (x86_64-web-web) }; /** @@ -100,6 +103,7 @@ public enum Profile { public static final String PROFILE_IOS_SIM = "ios-sim"; public static final String PROFILE_ANDROID = "android"; public static final String PROFILE_LINUX_AARCH64 = "linux-aarch64"; + public static final String PROFILE_WEB = "web"; public static final String DEFAULT_JAVA_STATIC_SDK_VERSION = "11-ea+10"; public static final String DEFAULT_JAVAFX_STATIC_SDK_VERSION = "17-ea+14"; @@ -150,6 +154,12 @@ public enum Profile { public static final String ANDROID_NATIVE_FOLDER = "/native/android/"; public static final String ANDROID_PROJECT_NAME = "android_project"; + public static final String META_INF_SUBSTRATE_WEB = "META-INF/substrate/web/"; + public static final String WEB_NATIVE_FOLDER = "/native/web/"; + public static final String WEB_AOT_CLASSIFIER = "bck2brwsr"; + public static final String WEB_AOT_VERSION = "0.51"; + public static final String WEB_INDEX_HTML = "index.html"; + public static final String META_INF_SUBSTRATE_CONFIG = "META-INF/substrate/config/"; public static final String USER_INIT_BUILD_TIME_FILE = "initbuildtime"; public static final String USER_INIT_BUILD_TIME_ARCHOS_FILE = "initbuildtime-${archOs}"; diff --git a/src/main/java/com/gluonhq/substrate/SubstrateDispatcher.java b/src/main/java/com/gluonhq/substrate/SubstrateDispatcher.java index b51670d77..98b99b2bd 100644 --- a/src/main/java/com/gluonhq/substrate/SubstrateDispatcher.java +++ b/src/main/java/com/gluonhq/substrate/SubstrateDispatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Gluon + * Copyright (c) 2019, 2021, Gluon * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +35,7 @@ import com.gluonhq.substrate.target.IosTargetConfiguration; import com.gluonhq.substrate.target.LinuxTargetConfiguration; import com.gluonhq.substrate.target.TargetConfiguration; +import com.gluonhq.substrate.target.WebTargetConfiguration; import com.gluonhq.substrate.target.WindowsTargetConfiguration; import com.gluonhq.substrate.util.Logger; import com.gluonhq.substrate.util.ProcessRunner; @@ -385,7 +386,7 @@ public SubstrateDispatcher(Path buildRoot, ProjectConfiguration config) throws I } private TargetConfiguration getTargetConfiguration(Triplet targetTriplet) throws IOException { - if (!config.getHostTriplet().canCompileTo(targetTriplet)) { + if (!Constants.OS_WEB.equals(targetTriplet.getOs()) && !config.getHostTriplet().canCompileTo(targetTriplet)) { throw new IllegalArgumentException("We currently can't compile to " + targetTriplet + " when running on " + config.getHostTriplet()); } switch (targetTriplet.getOs()) { @@ -394,6 +395,7 @@ private TargetConfiguration getTargetConfiguration(Triplet targetTriplet) throws case Constants.OS_WINDOWS: return new WindowsTargetConfiguration(paths, config); case Constants.OS_IOS : return new IosTargetConfiguration(paths, config); case Constants.OS_ANDROID: return new AndroidTargetConfiguration(paths, config); + case Constants.OS_WEB : return new WebTargetConfiguration(paths, config); default : return null; } } diff --git a/src/main/java/com/gluonhq/substrate/model/Triplet.java b/src/main/java/com/gluonhq/substrate/model/Triplet.java index 37f5729a0..f7019199e 100644 --- a/src/main/java/com/gluonhq/substrate/model/Triplet.java +++ b/src/main/java/com/gluonhq/substrate/model/Triplet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Gluon + * Copyright (c) 2019, 2021, Gluon * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -125,6 +125,11 @@ public Triplet(Constants.Profile profile) { this.vendor = VENDOR_LINUX; this.os = OS_ANDROID; break; + case WEB: + this.arch = ARCH_AMD64; + this.vendor = VENDOR_WEB; + this.os = OS_WEB; + break; default: throw new IllegalArgumentException("Triplet for profile "+profile+" is not supported yet"); } diff --git a/src/main/java/com/gluonhq/substrate/target/WebTargetConfiguration.java b/src/main/java/com/gluonhq/substrate/target/WebTargetConfiguration.java new file mode 100644 index 000000000..5035d753b --- /dev/null +++ b/src/main/java/com/gluonhq/substrate/target/WebTargetConfiguration.java @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2021, Gluon + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL GLUON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.gluonhq.substrate.target; + +import com.gluonhq.substrate.Constants; +import com.gluonhq.substrate.model.ClassPath; +import com.gluonhq.substrate.model.InternalProjectConfiguration; +import com.gluonhq.substrate.model.ProcessPaths; +import com.gluonhq.substrate.util.FileOps; +import com.gluonhq.substrate.util.Logger; +import com.gluonhq.substrate.util.ProcessRunner; +import com.gluonhq.substrate.util.web.AheadOfTimeBase; +import org.apidesign.vm4brwsr.ObfuscationLevel; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import static com.gluonhq.substrate.Constants.META_INF_SUBSTRATE_WEB; +import static com.gluonhq.substrate.Constants.WEB_INDEX_HTML; + +public class WebTargetConfiguration extends AbstractTargetConfiguration { + + public static final List WEB_AOT_DEPENDENCIES = List.of( + "com.gluonhq:webscheduler:1.0.6", + "com.gluonhq.compat:javadate:1.1", + "com.gluonhq.compat:javafunctions:1.1", + "com.gluonhq.compat:javanio:1.2", + "org.apidesign.bck2brwsr:emul:" + Constants.WEB_AOT_VERSION, + "org.apidesign.bck2brwsr:emul:" + Constants.WEB_AOT_VERSION + ":" + Constants.WEB_AOT_CLASSIFIER, + "org.apidesign.bck2brwsr:emul.zip:" + Constants.WEB_AOT_VERSION, + "org.apidesign.bck2brwsr:emul.zip:" + Constants.WEB_AOT_VERSION + ":" + Constants.WEB_AOT_CLASSIFIER); + + private static final List webFiles = List.of("uongl.js", WEB_INDEX_HTML); + + private final String sourceOS; + private final Path rootPath; + + public WebTargetConfiguration(ProcessPaths paths, InternalProjectConfiguration configuration) { + super(paths, configuration); + this.sourceOS = projectConfiguration.getTargetTriplet().getOs(); + rootPath = paths.getSourcePath().resolve(sourceOS); + } + + @Override + public boolean compile() throws IOException, InterruptedException { + final List jars = new ClassPath(projectConfiguration.getClasspath()).getJars(true); + + Path webPath = paths.getGvmPath().resolve("web"); + if (!Files.exists(webPath)) { + Files.createDirectory(webPath); + } + Path libPath = paths.getGvmPath().resolve("web").resolve("lib"); + if (!Files.exists(libPath)) { + Files.createDirectory(libPath); + } + File mainJar = webPath.resolve(projectConfiguration.getAppName().concat(".jar")).toFile(); + File classes = jars.stream() + .filter(f -> f.toString().endsWith("classes.jar")) + .findFirst() + .orElseThrow(() -> new IOException("Classes not found")); + FileOps.copyFile(classes.toPath(), mainJar.toPath()); + jars.remove(classes); + + File mainJavaScript = webPath.resolve(projectConfiguration.getAppName().concat(".js")).toFile(); + + // Extract web files to tmp folder + Path tmpPath = paths.getTmpPath().resolve("web"); + if (!Files.exists(tmpPath)) { + Files.createDirectory(tmpPath); + } + for (String s : webFiles) { + for (File jar : jars) { + try (ZipFile zip = new ZipFile(jar)) { + Logger.logDebug("Scanning " + jar); + for (Enumeration e = zip.entries(); e.hasMoreElements(); ) { + ZipEntry zipEntry = e.nextElement(); + String name = zipEntry.getName(); + if (!zipEntry.isDirectory() && name.equals(META_INF_SUBSTRATE_WEB + s)) { + Logger.logDebug("Adding file from " + zip.getName() + " :: " + name + " into " + tmpPath.resolve(s)); + FileOps.copyStream(zip.getInputStream(zipEntry), tmpPath.resolve(s)); + } + } + } catch (IOException e) { + throw new IOException("Error processing web files from jar: " + jar + ": " + e.getMessage() + ", " + Arrays.toString(e.getSuppressed())); + } + } + } + + // Copy and update files to web folder + Path userHtml = rootPath.resolve(Constants.WEB_INDEX_HTML); + if (!Files.exists(userHtml)) { + // copy index to gensrc/web + Path genHtml = paths.getGenPath().resolve(sourceOS).resolve(Constants.WEB_INDEX_HTML); + Logger.logDebug("Copy " + Constants.WEB_INDEX_HTML + " to " + genHtml.toString()); + FileOps.copyFile(tmpPath.resolve(Constants.WEB_INDEX_HTML), genHtml); + FileOps.replaceInFile(genHtml, "WEB_TITLE", projectConfiguration.getAppName()); + FileOps.replaceInFile(genHtml, "WEB_APP_NAME", projectConfiguration.getAppName()); + FileOps.replaceInFile(genHtml, "WEB_MAIN_CLASS", projectConfiguration.getMainClassName()); + Logger.logInfo("Default " + Constants.WEB_INDEX_HTML + " generated in " + genHtml.toString() + ".\n" + + "Consider copying it to " + rootPath.toString() + " before performing any modification"); + FileOps.copyFile(genHtml, webPath.resolve(Constants.WEB_INDEX_HTML)); + } else { + FileOps.copyFile(userHtml, webPath.resolve(Constants.WEB_INDEX_HTML)); + } + + for (String s : webFiles) { + if (WEB_INDEX_HTML.equals(s)) { + continue; + } + FileOps.copyFile(tmpPath.resolve(s), webPath.resolve(s)); + } + + // AOT + class Work extends AheadOfTimeBase { + + private final Map artifacts = new HashMap<>(); + + @Override + protected File mainJavaScript() { + return mainJavaScript; + } + + @Override + protected File libraryPath(String fileNameJs) { + return libPath.resolve(fileNameJs).toFile(); + } + + @Override + protected ObfuscationLevel obfuscation() { + return ObfuscationLevel.NONE; + } + + @Override + protected String[] exports() { + return new String[0]; + } + + @Override + protected boolean ignoreBootClassPath() { + return true; + } + + @Override + protected boolean generateAotLibraries() { + return true; + } + + @Override + protected File mainJar() { + return mainJar; + } + + @Override + protected File vm() { + return webPath.resolve("bck2brwsr.js").toFile(); + } + + @Override + protected Collection artifacts() { + return jars; + } + + @Override + protected void logInfo(String msg) { + if (projectConfiguration.isVerbose()) { + Logger.logInfo(msg); + } else { + Logger.logDebug(msg); + } + } + + @Override + protected Exception failure(String msg, Throwable cause) { + if (cause != null) { + return new Exception(msg, cause); + } else { + return new Exception(msg); + } + } + + @Override + protected File file(File a) { + setArtifact(a); + return a; + } + + @Override + protected AheadOfTimeBase.Scope scope(File a) { + return Scope.RUNTIME; + } + + // m2 file: + // ~/.m2/repository/$groupId/$artifactId/$version/$artifactId-$version-$classifier.jar + + private void setArtifact(File a) { + String artifact = null; + if (a.toString().contains(".m2")) { + Path path = a.toPath(); + int m2Index = 0; + while (!".m2".equals(path.getName(m2Index++).toString())) { } + String groupId = path.subpath(m2Index + 1, path.getNameCount() - 3).toString().replaceAll(File.separator, "."); + String artifactId = path.getName(path.getNameCount() - 3).toString(); + String version = path.getName(path.getNameCount() - 2).toString(); + artifact = groupId + ":" + artifactId + ":" + version; + + String last = path.getName(path.getNameCount() - 1).toString(); + String lastPrefix = artifactId(a) + "-" + version(a) + "-"; + if (last.startsWith(lastPrefix)) { + String classifier = last.substring(lastPrefix.length(), last.length() - 4); + artifact += ":" + classifier; + } + artifacts.put(a, artifact); + } + } + + @Override + protected String groupId(File a) { + String artifact = artifacts.get(a); + if (artifact != null) { + return artifact.split(":")[0]; + } + return null; + } + + @Override + protected String artifactId(File a) { + String artifact = artifacts.get(a); + if (artifact != null) { + return artifact.split(":")[1]; + } + return null; + } + + @Override + protected String version(File a) { + String artifact = artifacts.get(a); + if (artifact != null) { + return artifact.split(":")[2]; + } + return null; + } + + @Override + protected String classifier(File a) { + String artifact = artifacts.get(a); + if (artifact != null) { + String[] split = artifact.split(":"); + return split.length < 4 ? null : split[3]; + } + return null; + } + + } + new Work().work(); + + return true; + } + + @Override + public boolean link() throws IOException, InterruptedException { + return true; + } + + // TODO: Requires Chrome as default browser for now + @Override + public boolean runUntilEnd() throws IOException, InterruptedException { + String url = paths.getGvmPath().resolve("web").resolve(WEB_INDEX_HTML).toUri().toURL().toExternalForm(); + Logger.logDebug("Launching url " + url); + String os = System.getProperty("os.name").toLowerCase(Locale.ROOT); + try { + List command = os.contains("mac") ? + List.of("open", url) : + os.contains("win") ? + List.of("rundll32", "url.dll,FileProtocolHandler", url) : + List.of("xdg-open", url); + ProcessRunner.runProcessForSingleOutput("browse", command.toArray(String[]::new)); + } catch (Exception e) { + throw new IOException("Error launching url " + url); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/gluonhq/substrate/util/web/AheadOfTimeBase.java b/src/main/java/com/gluonhq/substrate/util/web/AheadOfTimeBase.java new file mode 100644 index 000000000..9d91608c8 --- /dev/null +++ b/src/main/java/com/gluonhq/substrate/util/web/AheadOfTimeBase.java @@ -0,0 +1,215 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2018 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package com.gluonhq.substrate.util.web; + +import org.apidesign.bck2brwsr.aot.Bck2BrwsrJars; +import org.apidesign.vm4brwsr.Bck2Brwsr; +import org.apidesign.vm4brwsr.ObfuscationLevel; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +public abstract class AheadOfTimeBase { + protected abstract File mainJavaScript(); + protected abstract File libraryPath(String fileNameJs); + protected abstract ObfuscationLevel obfuscation(); + protected abstract String[] exports(); + protected abstract boolean ignoreBootClassPath(); + protected abstract boolean generateAotLibraries(); + protected abstract File mainJar(); + protected abstract File vm(); + protected abstract Iterable artifacts(); + protected abstract void logInfo(String msg); + protected abstract Exception failure(String msg, Throwable cause); + + protected abstract File file(Art a); + protected abstract Scope scope(Art a); + protected abstract String classifier(Art a); + protected abstract String artifactId(Art a); + protected abstract String groupId(Art a); + protected abstract String version(Art a); + + public final void work() { + URLClassLoader loader; + final Iterable artifacts = artifacts(); + artifacts.forEach(a -> logInfo(a.toString())); + try { + loader = buildClassLoader(mainJar(), artifacts); + } catch (MalformedURLException ex) { + throw raise("Can't initialize classloader", ex); + } + List libsCp = new ArrayList<>(); + for (Art a : artifacts) { + final File aFile = file(a); + if (aFile == null) { + continue; + } + String n = aFile.getName(); + if (!n.endsWith(".jar")) { + continue; + } + if (Scope.PROVIDED == scope(a)) { + continue; + } + if ("bck2brwsr".equals(classifier(a))) { + continue; + } + final String libNameJs = n.substring(0, n.length() - 4) + ".js"; + File js = libraryPath(libNameJs); + try { + js.getParentFile().mkdirs(); + aotLibrary(a, artifacts, js, loader, libsCp); + } catch (IOException ex) { + throw raise("Can't compile " + aFile, ex); + } + } + + try { + if (mainJavaScript().lastModified() > mainJar().lastModified()) { + logInfo("Skipping " + mainJavaScript() + " as it already exists."); + } else { + logInfo("Generating " + mainJavaScript()); + Bck2Brwsr withLibsCp = Bck2Brwsr.newCompiler().library(libsCp.toArray(new String[0])); + Bck2Brwsr c = Bck2BrwsrJars.configureFrom(withLibsCp, mainJar(), loader, ignoreBootClassPath()); + if (exports() != null) { + for (String e : exports()) { + if (e != null) { + c = c.addExported(e.replace('.', '/')); + } + } + } + try (Writer w = new OutputStreamWriter(new FileOutputStream(mainJavaScript()), "UTF-8")) { + c. + obfuscation(obfuscation()). + generate(w); + } + } + } catch (IOException ex) { + throw raise("Cannot generate script for " + mainJar(), ex); + } + + try (Writer w = new OutputStreamWriter(new FileOutputStream(vm()), "UTF-8")) { + Bck2Brwsr.newCompiler(). + obfuscation(obfuscation()). + standalone(false). + resources(new Bck2Brwsr.Resources() { + + @Override + public InputStream get(String resource) throws IOException { + return null; + } + }). + generate(w); + w.close(); + } catch (IOException ex) { + throw raise("Can't compile", ex); + } + } + + private void aotLibrary(Art a, Iterable allArtifacts, File js, URLClassLoader loader, List libsCp) throws IOException { + File aFile = file(a); + if (js.lastModified() > aFile.lastModified()) { + logInfo("Skipping " + js + " as it already exists."); + libsCp.add(js.getParentFile().getName() + '/' + js.getName()); + return; + } + for (Art b : allArtifacts) { + final File file = file(b); + if ("bck2brwsr".equals(classifier(b))) { // NOI18N + JarFile jf = new JarFile(file); + Manifest man = jf.getManifest(); + for (Map.Entry entrySet : man.getEntries().entrySet()) { + String entryName = entrySet.getKey(); + Attributes attr = entrySet.getValue(); + if ( + attr.getValue("Bck2BrwsrArtifactId").equals(artifactId(a)) && + attr.getValue("Bck2BrwsrGroupId").equals(groupId(a)) && + attr.getValue("Bck2BrwsrVersion").equals(version(a)) && + "melta".equals(attr.getValue("Bck2BrwsrMagic")) && + ( + obfuscation() == ObfuscationLevel.FULL && "true".equals(attr.getValue("Bck2BrwsrMinified")) + || + obfuscation() != ObfuscationLevel.FULL && "true".equals(attr.getValue("Bck2BrwsrDebug")) + ) + ) { + logInfo("Extracting " + js + " from " + file); + libsCp.add(js.getParentFile().getName() + '/' + js.getName()); + try (InputStream is = jf.getInputStream(new ZipEntry(entryName))) { + Files.copy(is, js.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + return; + } + } + } + } + if (!generateAotLibraries()) { + throw raise("Not generating " + js + " and no precompiled version found!", null); + } + logInfo("Generating " + js); + libsCp.add(js.getParentFile().getName() + '/' + js.getName()); + try (Writer w = new OutputStreamWriter(new FileOutputStream(js), "UTF-8")) { + Bck2Brwsr c = Bck2BrwsrJars.configureFrom(null, file(a), loader, ignoreBootClassPath()); + if (exports() != null) { + c = c.addExported(exports()); + } + c. + obfuscation(obfuscation()). + generate(w); + } + } + private URLClassLoader buildClassLoader(File root, Iterable deps) throws MalformedURLException { + List arr = new ArrayList<>(); + if (root != null) { + arr.add(root.toURI().toURL()); + } + for (Art a : deps) { + if (file(a) != null) { + arr.add(file(a).toURI().toURL()); + } + } + return new URLClassLoader(arr.toArray(new URL[0]), AheadOfTimeBase.class.getClassLoader()); + } + + private RuntimeException raise(String msg, Throwable cause) throws RuntimeException { + return raise(RuntimeException.class, failure(msg, cause)); + } + + private static E raise(Class type, Throwable ex) throws E { + throw (E)ex; + } + + public enum Scope { + PROVIDED, RUNTIME; + } +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 3b534a6c5..68f1b9ee0 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -4,4 +4,7 @@ requires java.xml; requires bcpkix.jdk15on; requires org.graalvm.sdk; + requires svm; + requires aot; + requires vm4brwsr; }