jaroslav@1584: /** jaroslav@1584: * Back 2 Browser Bytecode Translator jaroslav@1584: * Copyright (C) 2012 Jaroslav Tulach jaroslav@1584: * jaroslav@1584: * This program is free software: you can redistribute it and/or modify jaroslav@1584: * it under the terms of the GNU General Public License as published by jaroslav@1584: * the Free Software Foundation, version 2 of the License. jaroslav@1584: * jaroslav@1584: * This program is distributed in the hope that it will be useful, jaroslav@1584: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@1584: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@1584: * GNU General Public License for more details. jaroslav@1584: * jaroslav@1584: * You should have received a copy of the GNU General Public License jaroslav@1584: * along with this program. Look for COPYING file in the top folder. jaroslav@1584: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@1584: */ jaroslav@1584: jaroslav@1584: package org.apidesign.bck2brwsr.mojo; jaroslav@1584: jaroslav@1584: import java.io.BufferedReader; jaroslav@1584: import java.io.File; jaroslav@1584: import java.io.FileWriter; jaroslav@1584: import java.io.IOException; jaroslav@1584: import java.io.InputStream; jaroslav@1584: import java.io.InputStreamReader; jaroslav@1584: import java.net.MalformedURLException; jaroslav@1584: import java.net.URL; jaroslav@1584: import java.net.URLClassLoader; jaroslav@1584: import java.util.ArrayList; jaroslav@1584: import java.util.Collection; jaroslav@1584: import java.util.Enumeration; jaroslav@1584: import java.util.HashSet; jaroslav@1584: import java.util.List; jaroslav@1584: import java.util.Set; jaroslav@1584: import java.util.jar.JarEntry; jaroslav@1584: import java.util.jar.JarFile; jaroslav@1594: import java.util.logging.Level; jaroslav@1594: import java.util.logging.Logger; jaroslav@1584: import org.apache.maven.artifact.Artifact; jaroslav@1584: import org.apache.maven.plugin.AbstractMojo; jaroslav@1584: import org.apache.maven.plugin.MojoExecutionException; jaroslav@1584: import org.apache.maven.plugin.MojoFailureException; jaroslav@1584: import org.apache.maven.plugins.annotations.LifecyclePhase; jaroslav@1584: import org.apache.maven.plugins.annotations.Mojo; jaroslav@1584: import org.apache.maven.plugins.annotations.Parameter; jaroslav@1584: import org.apache.maven.plugins.annotations.ResolutionScope; jaroslav@1584: import org.apache.maven.project.MavenProject; jaroslav@1584: import org.apidesign.vm4brwsr.Bck2Brwsr; jaroslav@1584: import org.apidesign.vm4brwsr.ObfuscationLevel; jaroslav@1584: jaroslav@1584: /** jaroslav@1584: * jaroslav@1584: * @author Jaroslav Tulach jaroslav@1584: * @since 0.9 jaroslav@1584: */ jaroslav@1584: @Mojo(name = "aot", jaroslav@1584: requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, jaroslav@1584: defaultPhase = LifecyclePhase.PACKAGE jaroslav@1584: ) jaroslav@1584: public class AheadOfTime extends AbstractMojo { jaroslav@1584: @Parameter(defaultValue = "${project}") jaroslav@1584: private MavenProject prj; jaroslav@1584: jaroslav@1584: /** jaroslav@1584: * Directory where to generate ahead-of-time JavaScript files for jaroslav@1584: * required libraries. jaroslav@1584: */ jaroslav@1584: @Parameter(defaultValue = "${project.build.directory}/lib") jaroslav@1584: private File aot; jaroslav@1584: jaroslav@1584: /** Root JavaScript file to generate */ jaroslav@1584: @Parameter(defaultValue="${project.build.directory}/bck2brwsr.js") jaroslav@1584: private File vm; jaroslav@1584: jaroslav@1584: /** jaroslav@1584: * The obfuscation level for the generated JavaScript file. jaroslav@1584: * jaroslav@1584: * @since 0.5 jaroslav@1584: */ jaroslav@1584: @Parameter(defaultValue = "NONE") jaroslav@1584: private ObfuscationLevel obfuscation; jaroslav@1584: jaroslav@1584: @Override jaroslav@1584: public void execute() throws MojoExecutionException, MojoFailureException { jaroslav@1594: URLClassLoader loader; jaroslav@1584: try { jaroslav@1594: loader = buildClassLoader(null, prj.getArtifacts()); jaroslav@1594: } catch (MalformedURLException ex) { jaroslav@1594: throw new MojoFailureException("Can't initialize classloader"); jaroslav@1594: } jaroslav@1594: for (Artifact a : prj.getArtifacts()) { jaroslav@1594: if (a.getFile() == null) { jaroslav@1594: continue; jaroslav@1594: } jaroslav@1594: String n = a.getFile().getName(); jaroslav@1594: if (!n.endsWith(".jar")) { jaroslav@1594: continue; jaroslav@1594: } jaroslav@1594: if ("provided".equals(a.getScope())) { jaroslav@1594: continue; jaroslav@1594: } jaroslav@1594: aot.mkdirs(); jaroslav@1594: File js = new File(aot, n.substring(0, n.length() - 4) + ".js"); jaroslav@1594: try { jaroslav@1584: aotLibrary(a, js , loader); jaroslav@1594: } catch (IOException ex) { jaroslav@1594: throw new MojoFailureException("Can't compile" + a.getFile(), ex); jaroslav@1584: } jaroslav@1594: } jaroslav@1584: jaroslav@1594: try { jaroslav@1584: FileWriter w = new FileWriter(vm); jaroslav@1584: Bck2Brwsr.newCompiler(). jaroslav@1584: obfuscation(obfuscation). jaroslav@1584: standalone(false). jaroslav@1584: resources(new Bck2Brwsr.Resources() { jaroslav@1584: jaroslav@1584: @Override jaroslav@1584: public InputStream get(String resource) throws IOException { jaroslav@1584: return null; jaroslav@1584: } jaroslav@1584: }). jaroslav@1584: generate(w); jaroslav@1584: w.close(); jaroslav@1584: jaroslav@1584: } catch (IOException ex) { jaroslav@1584: throw new MojoExecutionException("Can't compile", ex); jaroslav@1584: } jaroslav@1584: } jaroslav@1584: jaroslav@1584: private void aotLibrary(Artifact a, File js, URLClassLoader loader) throws IOException { jaroslav@1584: List classes = new ArrayList(); jaroslav@1584: List resources = new ArrayList(); jaroslav@1584: Set exported = new HashSet(); jaroslav@1584: jaroslav@1584: JarFile jf = new JarFile(a.getFile()); jaroslav@1584: listJAR(jf, classes , resources, exported); jaroslav@1584: jaroslav@1584: FileWriter w = new FileWriter(js); jaroslav@1584: Bck2Brwsr.newCompiler(). jaroslav@1584: obfuscation(obfuscation). jaroslav@1584: library(true). jaroslav@1584: resources(loader). jaroslav@1584: addResources(resources.toArray(new String[0])). jaroslav@1584: addClasses(classes.toArray(new String[0])). jaroslav@1584: addExported(exported.toArray(new String[0])). jaroslav@1584: generate(w); jaroslav@1584: w.close(); jaroslav@1584: } jaroslav@1584: private static URLClassLoader buildClassLoader(File root, Collection deps) throws MalformedURLException { jaroslav@1584: List arr = new ArrayList(); jaroslav@1584: if (root != null) { jaroslav@1584: arr.add(root.toURI().toURL()); jaroslav@1584: } jaroslav@1584: for (Artifact a : deps) { jaroslav@1584: if (a.getFile() != null) { jaroslav@1584: arr.add(a.getFile().toURI().toURL()); jaroslav@1584: } jaroslav@1584: } jaroslav@1584: return new URLClassLoader(arr.toArray(new URL[0]), Java2JavaScript.class.getClassLoader()); jaroslav@1584: } jaroslav@1584: jaroslav@1584: private static void listJAR( jaroslav@1584: JarFile j, List classes, jaroslav@1584: List resources, Set exported jaroslav@1584: ) throws IOException { jaroslav@1584: Enumeration en = j.entries(); jaroslav@1584: while (en.hasMoreElements()) { jaroslav@1584: JarEntry e = en.nextElement(); jaroslav@1584: final String n = e.getName(); jaroslav@1584: if (n.endsWith("/")) { jaroslav@1584: continue; jaroslav@1584: } jaroslav@1584: int last = n.lastIndexOf('/'); jaroslav@1584: String pkg = n.substring(0, last + 1); jaroslav@1584: if (n.endsWith(".class")) { jaroslav@1584: classes.add(n.substring(0, n.length() - 6)); jaroslav@1584: } else { jaroslav@1584: resources.add(n); jaroslav@1584: if (n.startsWith("META-INF/services/") && exported != null) { jaroslav@1584: final InputStream is = j.getInputStream(e); jaroslav@1584: exportedServices(is, exported); jaroslav@1584: is.close(); jaroslav@1584: } jaroslav@1584: } jaroslav@1584: } jaroslav@1584: String exp = j.getManifest().getMainAttributes().getValue("Export-Package"); jaroslav@1584: if (exp != null && exported != null) { jaroslav@1584: for (String def : exp.split(",")) { jaroslav@1584: for (String sep : def.split(";")) { jaroslav@1584: exported.add(sep.replace('.', '/') + "/"); jaroslav@1584: break; jaroslav@1584: } jaroslav@1584: } jaroslav@1584: } jaroslav@1584: } jaroslav@1584: jaroslav@1584: static void exportedServices(final InputStream is, Set exported) throws IOException { jaroslav@1584: BufferedReader r = new BufferedReader(new InputStreamReader(is)); jaroslav@1584: for (;;) { jaroslav@1584: String l = r.readLine(); jaroslav@1584: if (l == null) { jaroslav@1584: break; jaroslav@1584: } jaroslav@1584: if (l.startsWith("#")) { jaroslav@1584: continue; jaroslav@1584: } jaroslav@1584: exported.add(l.replace('.', '/')); jaroslav@1584: } jaroslav@1584: } jaroslav@1584: jaroslav@1584: }