Java tutorial
/* * Copyright (C) 2013 BugVM AB. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.bugvm.maven.plugin; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.Collection; import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; import org.apache.maven.artifact.resolver.ArtifactResolver; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.archiver.UnArchiver; import org.codehaus.plexus.archiver.manager.ArchiverManager; import org.codehaus.plexus.archiver.manager.NoSuchArchiverException; import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; import org.codehaus.plexus.util.xml.XMLWriter; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.codehaus.plexus.util.xml.Xpp3DomWriter; import com.bugvm.compiler.AppCompiler; import com.bugvm.compiler.Version; import com.bugvm.compiler.config.Arch; import com.bugvm.compiler.config.Config; import com.bugvm.compiler.config.Config.Home; import com.bugvm.compiler.config.OS; import com.bugvm.compiler.log.Logger; import com.bugvm.compiler.target.ios.ProvisioningProfile; import com.bugvm.compiler.target.ios.SigningIdentity; /** */ public abstract class AbstractBugVMMojo extends AbstractMojo { @Component protected MavenProject project; @Component private ArchiverManager archiverManager; @Component private ArtifactResolver artifactResolver; @Parameter(defaultValue = "${localRepository}") private ArtifactRepository localRepository; /** * Base directory to extract BugVM native distribution files into. The * bugvm-dist bundle will be downloaded from Maven and extracted into this * directory. Note that each release of BugVM is placed in a separate * sub-directory with the version number as suffix. * * If not set, then the tar file is extracted into the local Maven * repository where the tar file is downloaded to. */ @Parameter protected File home; /** * The path to a {@code bugvm.properties} file which contains info for your app. */ @Parameter(property = "bugvm.propertiesFile") protected File propertiesFile; /** * The path to a {@code bugvm.xml} file which configures the BugVM compiler. */ @Parameter(property = "bugvm.configFile") protected File configFile; /** * The identity to sign the app as when building an iOS or tvOS bundle for * the app. Default is to look for an identity starting with 'iPhone * Developer' or 'iOS Development'. Enclose in '/' to search by regexp, e.g. * '/foo|bar/'. */ @Parameter(property = "bugvm.signIdentity", alias = "bugvm.iosSignIdentity") protected String signIdentity; /** * The provisioning profile to use when building for device. */ @Parameter(property = "bugvm.provisioningProfile", alias = "bugvm.iosProvisioningProfile") protected String provisioningProfile; /** * Whether the app should be signed or not. Unsigned apps can only be run on jailbroken * devices. */ @Parameter(property = "bugvm.skipSigning", alias = "bugvm.iosSkipSigning") protected boolean skipSigning = false; // /** // * Keychain password to use when unlocking the codesign keychain. May be // * needed during headless build. The compiler will also look for a // * KEYCHAIN_PASSWORD env variable if this property hasn't been specified. // */ // @Parameter(property="bugvm.keychainPassword") // protected String keychainPassword; // /** // * Read the keychain password to use when unlocking the codesign keychain // * from the specified file. May be needed during headless build. The // * compiler will also look for a KEYCHAIN_PASSWORD env variable if this // * property hasn't been specified. // */ // @Parameter(property="bugvm.keychainPasswordFile") // protected File keychainPasswordFile; /** * The directory into which the BugVM distributable for the project will be built. */ @Parameter(property = "bugvm.installDir", defaultValue = "${project.build.directory}/bugvm") protected File installDir; /** * The directory where cached compiled class files will be placed. Default * is ~/.bugvm/cache. */ @Parameter(property = "bugvm.cacheDir") protected File cacheDir; /** * Overrides the arch used when running the app. One of x86, x86_64, thumbv7, arm64. * Will be ignored if the specified value isn't supported by the executed goal. */ @Parameter(property = "bugvm.arch") protected String arch; /** * Overrides the os used when running the app. One of macosx, linux, ios, tvos. * Will be ignored if the specified value isn't supported by the executed goal. */ @Parameter(property = "bugvm.os") protected String os; /** * If set to {@code true} the app will be launched in debug mode. The app * will suspend before the main method is called and will wait for a * debugger to connect. If set to {@code "clientmode"} then the application * will connect back to the local host to attach to already started * debugging server which is waiting for connection on <code>bugvm.debugPort</code>. */ @Parameter(property = "bugvm.debug") protected String debug; /** * The port to listen for debugger connections on when launching in debug * mode using {@code debug=true}. If not set a default port will be used. * The port actually used will be written to the console before the app is * launched. */ @Parameter(property = "bugvm.debugPort") protected int debugPort = -1; private Logger roboVMLogger; protected Config.Builder configure(Config.Builder builder) throws MojoExecutionException { builder.logger(getBugVMLogger()); // load config base file if it exists (and properties) if (os != null) { builder.os(OS.valueOf(os)); } if (propertiesFile != null) { if (!propertiesFile.exists()) { throw new MojoExecutionException( "Invalid 'propertiesFile' specified for BugVM compile: " + propertiesFile); } try { getLog().debug( "Including properties file in BugVM compiler config: " + propertiesFile.getAbsolutePath()); builder.addProperties(propertiesFile); } catch (IOException e) { throw new MojoExecutionException( "Failed to add properties file to BugVM config: " + propertiesFile); } } else { try { builder.readProjectProperties(project.getBasedir(), false); } catch (IOException e) { throw new MojoExecutionException("Failed to read BugVM project properties file(s) in " + project.getBasedir().getAbsolutePath(), e); } } if (configFile != null) { if (!configFile.exists()) { throw new MojoExecutionException("Invalid 'configFile' specified for BugVM compile: " + configFile); } try { getLog().debug("Loading config file for BugVM compiler: " + configFile.getAbsolutePath()); builder.read(configFile); } catch (Exception e) { throw new MojoExecutionException("Failed to read BugVM config file: " + configFile); } } else { try { builder.readProjectConfig(project.getBasedir(), false); } catch (Exception e) { throw new MojoExecutionException( "Failed to read project BugVM config file in " + project.getBasedir().getAbsolutePath(), e); } } // Read embedded BugVM <config> if there is one Plugin plugin = project.getPlugin("com.bugvm:bugvm-maven-plugin"); MavenProject p = project; while (p != null && plugin == null) { plugin = p.getPluginManagement().getPluginsAsMap().get("com.bugvm:bugvm-maven-plugin"); if (plugin == null) p = p.getParent(); } if (plugin != null) { getLog().debug("Reading BugVM plugin configuration from " + p.getFile().getAbsolutePath()); Xpp3Dom configDom = (Xpp3Dom) plugin.getConfiguration(); if (configDom != null && configDom.getChild("config") != null) { StringWriter sw = new StringWriter(); XMLWriter xmlWriter = new PrettyPrintXMLWriter(sw, "UTF-8", null); Xpp3DomWriter.write(xmlWriter, configDom.getChild("config")); try { builder.read(new StringReader(sw.toString()), project.getBasedir()); } catch (Exception e) { throw new MojoExecutionException("Failed to read BugVM config embedded in POM", e); } } } File tmpDir = new File(project.getBuild().getDirectory(), "bugvm.tmp"); try { FileUtils.deleteDirectory(tmpDir); } catch (IOException e) { throw new MojoExecutionException("Failed to clean output dir " + tmpDir, e); } tmpDir.mkdirs(); Home home = null; try { home = Home.find(); } catch (Throwable t) { } if (home == null || !home.isDev()) { home = new Config.Home(unpackBugVMDist()); } builder.home(home).tmpDir(tmpDir).skipInstall(true).installDir(installDir); if (home.isDev()) { builder.useDebugLibs(Boolean.getBoolean("bugvm.useDebugLibs")); builder.dumpIntermediates(true); } if (debug != null && !debug.equals("false")) { builder.debug(true); if (debugPort != -1) { builder.addPluginArgument("debug:jdwpport=" + debugPort); } if ("clientmode".equals(debug)) { builder.addPluginArgument("debug:clientmode=true"); } } if (skipSigning) { builder.iosSkipSigning(true); } else { if (signIdentity != null) { getLog().debug("Using explicit signing identity: " + signIdentity); builder.iosSignIdentity(SigningIdentity.find(SigningIdentity.list(), signIdentity)); } if (provisioningProfile != null) { getLog().debug("Using explicit provisioning profile: " + provisioningProfile); builder.iosProvisioningProfile( ProvisioningProfile.find(ProvisioningProfile.list(), provisioningProfile)); } // if (keychainPassword != null) { // builder.keychainPassword(keychainPassword); // } else if (keychainPasswordFile != null) { // builder.keychainPasswordFile(keychainPasswordFile); // } } if (cacheDir != null) { builder.cacheDir(cacheDir); } builder.clearClasspathEntries(); // configure the runtime classpath try { for (Object object : project.getRuntimeClasspathElements()) { String path = (String) object; if (getLog().isDebugEnabled()) { getLog().debug("Including classpath element for BugVM app: " + path); } builder.addClasspathEntry(new File(path)); } } catch (DependencyResolutionRequiredException e) { throw new MojoExecutionException("Error resolving application classpath for BugVM build", e); } return builder; } protected AppCompiler build(OS os, Arch arch, String targetType) throws MojoExecutionException, MojoFailureException { getLog().info("Building BugVM app for: " + os + " (" + arch + ")"); Config.Builder builder; try { builder = new Config.Builder(); } catch (IOException e) { throw new MojoExecutionException(e.getMessage(), e); } configure(builder).os(os).arch(arch).targetType(targetType); // execute the BugVM build try { getLog().info("Compiling BugVM app, this could take a while, especially the first time round"); AppCompiler compiler = new AppCompiler(builder.build()); compiler.build(); return compiler; } catch (IOException e) { throw new MojoExecutionException("Error building BugVM executable for app", e); } } protected String getBugVMVersion() { return Version.getVersion(); } protected File unpackBugVMDist() throws MojoExecutionException { Artifact distTarArtifact = resolveBugVMDistArtifact(); File distTarFile = distTarArtifact.getFile(); File unpackBaseDir; if (home != null) { unpackBaseDir = home; } else { // by default unpack into the local repo directory unpackBaseDir = new File(distTarFile.getParent(), "unpacked"); } if (unpackBaseDir.exists() && distTarArtifact.isSnapshot()) { getLog().debug("Deleting directory for unpacked snapshots: " + unpackBaseDir); try { FileUtils.deleteDirectory(unpackBaseDir); } catch (IOException e) { throw new MojoExecutionException("Failed to delete " + unpackBaseDir, e); } } unpack(distTarFile, unpackBaseDir); File unpackedDir = new File(unpackBaseDir, "bugvm-" + getBugVMVersion()); return unpackedDir; } protected Artifact resolveBugVMDistArtifact() throws MojoExecutionException { MavenArtifactHandler handler = new MavenArtifactHandler("tar.gz"); Artifact artifact = new DefaultArtifact("com.bugvm", "bugvm-dist", getBugVMVersion(), "", "tar.gz", null, handler); return resolveArtifact(artifact); } protected Artifact resolveArtifact(Artifact artifact) throws MojoExecutionException { ArtifactResolutionRequest request = new ArtifactResolutionRequest(); request.setArtifact(artifact); if (artifact.isSnapshot()) { request.setForceUpdate(true); } request.setLocalRepository(localRepository); final List<ArtifactRepository> remoteRepositories = project.getRemoteArtifactRepositories(); request.setRemoteRepositories(remoteRepositories); getLog().debug("Resolving artifact " + artifact); ArtifactResolutionResult result = artifactResolver.resolve(request); if (!result.isSuccess()) { throw new MojoExecutionException("Unable to resolve artifact: " + artifact); } Collection<Artifact> resolvedArtifacts = result.getArtifacts(); artifact = (Artifact) resolvedArtifacts.iterator().next(); return artifact; } protected void unpack(File archive, File targetDirectory) throws MojoExecutionException { if (!targetDirectory.exists()) { getLog().info("Extracting '" + archive + "' to: " + targetDirectory); if (!targetDirectory.mkdirs()) { throw new MojoExecutionException( "Unable to create base directory to unpack into: " + targetDirectory); } try { UnArchiver unArchiver = archiverManager.getUnArchiver(archive); unArchiver.setSourceFile(archive); unArchiver.setDestDirectory(targetDirectory); unArchiver.extract(); } catch (NoSuchArchiverException e) { throw new MojoExecutionException("Unable to unpack archive " + archive + " to " + targetDirectory, e); } getLog().debug("Archive '" + archive + "' unpacked to: " + targetDirectory); } else { getLog().debug("Archive '" + archive + "' was already unpacked in: " + targetDirectory); } } protected Logger getBugVMLogger() { if (roboVMLogger == null) { roboVMLogger = new Logger() { public void debug(String s, Object... objects) { getLog().debug(String.format(s, objects)); } public void info(String s, Object... objects) { getLog().info(String.format(s, objects)); } public void warn(String s, Object... objects) { getLog().warn(String.format(s, objects)); } public void error(String s, Object... objects) { getLog().error(String.format(s, objects)); } }; } return roboVMLogger; } }