1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/AheadOfTime.java Thu May 22 10:48:09 2014 +0200
1.3 @@ -0,0 +1,199 @@
1.4 +/**
1.5 + * Back 2 Browser Bytecode Translator
1.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
1.7 + *
1.8 + * This program is free software: you can redistribute it and/or modify
1.9 + * it under the terms of the GNU General Public License as published by
1.10 + * the Free Software Foundation, version 2 of the License.
1.11 + *
1.12 + * This program is distributed in the hope that it will be useful,
1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.15 + * GNU General Public License for more details.
1.16 + *
1.17 + * You should have received a copy of the GNU General Public License
1.18 + * along with this program. Look for COPYING file in the top folder.
1.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
1.20 + */
1.21 +
1.22 +package org.apidesign.bck2brwsr.mojo;
1.23 +
1.24 +import java.io.BufferedReader;
1.25 +import java.io.File;
1.26 +import java.io.FileWriter;
1.27 +import java.io.IOException;
1.28 +import java.io.InputStream;
1.29 +import java.io.InputStreamReader;
1.30 +import java.net.MalformedURLException;
1.31 +import java.net.URL;
1.32 +import java.net.URLClassLoader;
1.33 +import java.util.ArrayList;
1.34 +import java.util.Collection;
1.35 +import java.util.Enumeration;
1.36 +import java.util.HashSet;
1.37 +import java.util.List;
1.38 +import java.util.Set;
1.39 +import java.util.jar.JarEntry;
1.40 +import java.util.jar.JarFile;
1.41 +import org.apache.maven.artifact.Artifact;
1.42 +import org.apache.maven.plugin.AbstractMojo;
1.43 +import org.apache.maven.plugin.MojoExecutionException;
1.44 +import org.apache.maven.plugin.MojoFailureException;
1.45 +import org.apache.maven.plugins.annotations.LifecyclePhase;
1.46 +import org.apache.maven.plugins.annotations.Mojo;
1.47 +import org.apache.maven.plugins.annotations.Parameter;
1.48 +import org.apache.maven.plugins.annotations.ResolutionScope;
1.49 +import org.apache.maven.project.MavenProject;
1.50 +import org.apidesign.vm4brwsr.Bck2Brwsr;
1.51 +import org.apidesign.vm4brwsr.ObfuscationLevel;
1.52 +
1.53 +/**
1.54 + *
1.55 + * @author Jaroslav Tulach
1.56 + * @since 0.9
1.57 + */
1.58 +@Mojo(name = "aot",
1.59 + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
1.60 + defaultPhase = LifecyclePhase.PACKAGE
1.61 +)
1.62 +public class AheadOfTime extends AbstractMojo {
1.63 + @Parameter(defaultValue = "${project}")
1.64 + private MavenProject prj;
1.65 +
1.66 + /**
1.67 + * Directory where to generate ahead-of-time JavaScript files for
1.68 + * required libraries.
1.69 + */
1.70 + @Parameter(defaultValue = "${project.build.directory}/lib")
1.71 + private File aot;
1.72 +
1.73 + /** Root JavaScript file to generate */
1.74 + @Parameter(defaultValue="${project.build.directory}/bck2brwsr.js")
1.75 + private File vm;
1.76 +
1.77 + /**
1.78 + * The obfuscation level for the generated JavaScript file.
1.79 + *
1.80 + * @since 0.5
1.81 + */
1.82 + @Parameter(defaultValue = "NONE")
1.83 + private ObfuscationLevel obfuscation;
1.84 +
1.85 + @Override
1.86 + public void execute() throws MojoExecutionException, MojoFailureException {
1.87 + try {
1.88 + URLClassLoader loader = buildClassLoader(null, prj.getArtifacts());
1.89 + for (Artifact a : prj.getArtifacts()) {
1.90 + if (a.getFile() == null) {
1.91 + continue;
1.92 + }
1.93 + String n = a.getFile().getName();
1.94 + if (!n.endsWith(".jar")) {
1.95 + continue;
1.96 + }
1.97 + aot.mkdirs();
1.98 + File js = new File(aot, n.substring(0, n.length() - 4) + ".js");
1.99 + aotLibrary(a, js , loader);
1.100 + }
1.101 +
1.102 + FileWriter w = new FileWriter(vm);
1.103 + Bck2Brwsr.newCompiler().
1.104 + obfuscation(obfuscation).
1.105 + standalone(false).
1.106 + resources(new Bck2Brwsr.Resources() {
1.107 +
1.108 + @Override
1.109 + public InputStream get(String resource) throws IOException {
1.110 + return null;
1.111 + }
1.112 + }).
1.113 + generate(w);
1.114 + w.close();
1.115 +
1.116 + } catch (IOException ex) {
1.117 + throw new MojoExecutionException("Can't compile", ex);
1.118 + }
1.119 + }
1.120 +
1.121 + private void aotLibrary(Artifact a, File js, URLClassLoader loader) throws IOException {
1.122 + List<String> classes = new ArrayList<String>();
1.123 + List<String> resources = new ArrayList<String>();
1.124 + Set<String> exported = new HashSet<String>();
1.125 +
1.126 + JarFile jf = new JarFile(a.getFile());
1.127 + listJAR(jf, classes , resources, exported);
1.128 +
1.129 + FileWriter w = new FileWriter(js);
1.130 + Bck2Brwsr.newCompiler().
1.131 + obfuscation(obfuscation).
1.132 + library(true).
1.133 + resources(loader).
1.134 + addResources(resources.toArray(new String[0])).
1.135 + addClasses(classes.toArray(new String[0])).
1.136 + addExported(exported.toArray(new String[0])).
1.137 + generate(w);
1.138 + w.close();
1.139 + }
1.140 + private static URLClassLoader buildClassLoader(File root, Collection<Artifact> deps) throws MalformedURLException {
1.141 + List<URL> arr = new ArrayList<URL>();
1.142 + if (root != null) {
1.143 + arr.add(root.toURI().toURL());
1.144 + }
1.145 + for (Artifact a : deps) {
1.146 + if (a.getFile() != null) {
1.147 + arr.add(a.getFile().toURI().toURL());
1.148 + }
1.149 + }
1.150 + return new URLClassLoader(arr.toArray(new URL[0]), Java2JavaScript.class.getClassLoader());
1.151 + }
1.152 +
1.153 + private static void listJAR(
1.154 + JarFile j, List<String> classes,
1.155 + List<String> resources, Set<String> exported
1.156 + ) throws IOException {
1.157 + Enumeration<JarEntry> en = j.entries();
1.158 + while (en.hasMoreElements()) {
1.159 + JarEntry e = en.nextElement();
1.160 + final String n = e.getName();
1.161 + if (n.endsWith("/")) {
1.162 + continue;
1.163 + }
1.164 + int last = n.lastIndexOf('/');
1.165 + String pkg = n.substring(0, last + 1);
1.166 + if (n.endsWith(".class")) {
1.167 + classes.add(n.substring(0, n.length() - 6));
1.168 + } else {
1.169 + resources.add(n);
1.170 + if (n.startsWith("META-INF/services/") && exported != null) {
1.171 + final InputStream is = j.getInputStream(e);
1.172 + exportedServices(is, exported);
1.173 + is.close();
1.174 + }
1.175 + }
1.176 + }
1.177 + String exp = j.getManifest().getMainAttributes().getValue("Export-Package");
1.178 + if (exp != null && exported != null) {
1.179 + for (String def : exp.split(",")) {
1.180 + for (String sep : def.split(";")) {
1.181 + exported.add(sep.replace('.', '/') + "/");
1.182 + break;
1.183 + }
1.184 + }
1.185 + }
1.186 + }
1.187 +
1.188 + static void exportedServices(final InputStream is, Set<String> exported) throws IOException {
1.189 + BufferedReader r = new BufferedReader(new InputStreamReader(is));
1.190 + for (;;) {
1.191 + String l = r.readLine();
1.192 + if (l == null) {
1.193 + break;
1.194 + }
1.195 + if (l.startsWith("#")) {
1.196 + continue;
1.197 + }
1.198 + exported.add(l.replace('.', '/'));
1.199 + }
1.200 + }
1.201 +
1.202 +}