org.robovm.maven.plugin.AbstractRoboVMMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.robovm.maven.plugin.AbstractRoboVMMojo.java

Source

/*
 * Copyright (C) 2013 Trillian Mobile 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 org.robovm.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.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 org.robovm.compiler.AppCompiler;
import org.robovm.compiler.Version;
import org.robovm.compiler.config.Arch;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.config.Config.Lib;
import org.robovm.compiler.config.Config.TargetType;
import org.robovm.compiler.config.OS;
import org.robovm.compiler.log.Logger;
import org.robovm.compiler.target.ios.ProvisioningProfile;
import org.robovm.compiler.target.ios.SigningIdentity;

/**
 */
public abstract class AbstractRoboVMMojo extends AbstractMojo {

    /**
     * The maven project.
     * 
     * @parameter expression="${project}"
     * @required
     */
    protected MavenProject project;

    /**
     * To look up Archiver/UnArchiver implementations
     * 
     * @component
     * @readonly
     */
    private ArchiverManager archiverManager;

    /**
     * To resolve artifacts
     * 
     * @component
     * @readonly
     */
    private ArtifactResolver artifactResolver;

    /**
     * 
     * @parameter default-value="${localRepository}"
     * 
     */
    private ArtifactRepository localRepository;

    /**
     * Base directory to extract RoboVM native distribution files into. The
     * robovm-dist bundle will be downloaded from Maven and extracted into this
     * directory. Note that each release of RoboVM 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;

    /**
     * @parameter expression="${robovm.propertiesFile}"
     */
    protected File propertiesFile;

    /**
     * @parameter expression="${robovm.configFile}"
     */
    protected File configFile;

    /**
     * The identity to sign the app as when building an iOS bundle for the app.
     * 
     * @parameter expression="${robovm.iosSignIdentity}"
     */
    protected String iosSignIdentity;

    /**
     * The provisioning profile to use when building for device..
     * 
     * @parameter expression="${robovm.iosProvisioningProfile}"
     */
    protected String iosProvisioningProfile;

    /**
     * Whether the app should be signed or not. Unsigned apps can only be run
     * on jailbroken devices.
     *
     * @parameter expression="${robovm.iosSkipSigning}"
     */
    protected boolean iosSkipSigning = false;

    /**
     * The directory that the RoboVM distributable for the project will be built
     * to.
     * 
     * @parameter expression="${project.build.directory}/robovm"
     */
    protected File installDir;

    /**
     * @parameter
     */
    protected boolean includeJFX;

    private Logger roboVMLogger;

    protected Config configure(Config.Builder configBuilder) throws IOException {
        return configBuilder.build();
    }

    public Config buildArchive(OS os, Arch arch, TargetType targetType)
            throws MojoExecutionException, MojoFailureException {

        getLog().info("Building RoboVM app for: " + os + " (" + arch + ")");

        Config.Builder builder;
        try {
            builder = new Config.Builder();
        } catch (IOException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }

        // load config base file if it exists (and properties)

        if (propertiesFile != null) {
            if (!propertiesFile.exists()) {
                throw new MojoExecutionException(
                        "Invalid 'propertiesFile' specified for RoboVM compile: " + propertiesFile);
            }
            try {
                getLog().debug(
                        "Including properties file in RoboVM compiler config: " + propertiesFile.getAbsolutePath());
                builder.addProperties(propertiesFile);
            } catch (IOException e) {
                throw new MojoExecutionException(
                        "Failed to add properties file to RoboVM config: " + propertiesFile);
            }
        } else {
            File file = new File(project.getBasedir(), "robovm.properties");
            if (file.exists()) {
                getLog().debug("Using default properties file: " + file.getAbsolutePath());
                try {
                    builder.addProperties(file);
                } catch (IOException e) {
                    throw new MojoExecutionException("Failed to add properties file to RoboVM config: " + file, e);
                }
            }
        }

        if (configFile != null) {
            if (!configFile.exists()) {
                throw new MojoExecutionException(
                        "Invalid 'configFile' specified for RoboVM compile: " + configFile);
            }
            try {
                getLog().debug("Loading config file for RoboVM compiler: " + configFile.getAbsolutePath());
                builder.read(configFile);
            } catch (Exception e) {
                throw new MojoExecutionException("Failed to read RoboVM config file: " + configFile);
            }
        } else {
            File file = new File(project.getBasedir(), "robovm.xml");
            if (file.exists()) {
                getLog().debug("Using default config file: " + file.getAbsolutePath());
                try {
                    builder.read(file);
                } catch (Exception e) {
                    throw new MojoExecutionException("Failed to read RoboVM config file: " + file, e);
                }
            }
        }

        // Read embedded RoboVM <config> if there is one
        Plugin plugin = project.getPlugin("org.robovm:robovm-maven-plugin");
        MavenProject p = project;
        while (p != null && plugin == null) {
            plugin = p.getPluginManagement().getPluginsAsMap().get("org.robovm:robovm-maven-plugin");
            p = p.getParent();
        }
        if (plugin != null) {
            getLog().debug("Reading RoboVM 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 RoboVM config embedded in POM", e);
                }
            }
        }

        File tmpDir = new File(new File(installDir, os.name()), arch.name());
        try {
            FileUtils.deleteDirectory(tmpDir);
        } catch (IOException e) {
            throw new MojoExecutionException("Failed to clean output dir " + tmpDir, e);
        }
        tmpDir.mkdirs();

        builder.home(new Config.Home(unpackRoboVMDist())).logger(getRoboVMLogger()).tmpDir(tmpDir)
                .targetType(targetType).skipInstall(true).installDir(installDir).os(os).arch(arch);

        if (iosSkipSigning) {
            builder.iosSkipSigning(true);
        } else {
            if (iosSignIdentity != null) {
                getLog().debug("Using explicit iOS Signing identity: " + iosSignIdentity);
                builder.iosSignIdentity(SigningIdentity.find(SigningIdentity.list(), iosSignIdentity));
            }

            if (iosProvisioningProfile != null) {
                getLog().debug("Using explicit iOS provisioning profile: " + iosProvisioningProfile);
                builder.iosProvisioningProfile(
                        ProvisioningProfile.find(ProvisioningProfile.list(), iosProvisioningProfile));
            }
        }

        builder.clearClasspathEntries();

        // add JavaFX if needed

        if (includeJFX) {

            getLog().info("Including JavaFX Runtime in build");

            // add jfxrt.jar from 78 backport to classpath
            File jfxJar = resolveJavaFXBackportRuntimeArtifact();
            getLog().debug("JavaFX backport runtime JAR found at: " + jfxJar);
            builder.addClasspathEntry(jfxJar);

            // add backport compatibility additions to classpath
            File jfxCompatJar = resolveJavaFXBackportCompatibilityArtifact();
            getLog().debug("JavaFX backport compatibilty JAR found at: " + jfxCompatJar);
            builder.addClasspathEntry(jfxCompatJar);

            // include native files as resources

            File iosNativesBaseDir = unpackJavaFXNativeIOSArtifact();
            // builder.addLib(new File(iosNativesBaseDir, "libdecora-sse-" +
            // arch.getClangName() + ".a").getAbsolutePath());
            builder.addLib(new Lib(
                    new File(iosNativesBaseDir, "libglass-" + arch.getClangName() + ".a").getAbsolutePath(), true));
            builder.addLib(new Lib(
                    new File(iosNativesBaseDir, "libjavafx-font-" + arch.getClangName() + ".a").getAbsolutePath(),
                    true));
            builder.addLib(new Lib(
                    new File(iosNativesBaseDir, "libjavafx-iio-" + arch.getClangName() + ".a").getAbsolutePath(),
                    true));
            builder.addLib(new Lib(
                    new File(iosNativesBaseDir, "libprism-common-" + arch.getClangName() + ".a").getAbsolutePath(),
                    true));
            builder.addLib(new Lib(
                    new File(iosNativesBaseDir, "libprism-es2-" + arch.getClangName() + ".a").getAbsolutePath(),
                    true));
            // builder.addLib(new File(iosNativesBaseDir, "libprism-sw-" +
            // arch.getClangName() + ".a").getAbsolutePath());

            // add default 'roots' needed for JFX to work
            builder.addForceLinkClass("com.sun.javafx.tk.quantum.QuantumToolkit");
            builder.addForceLinkClass("com.sun.prism.es2.ES2Pipeline");
            builder.addForceLinkClass("com.sun.prism.es2.IOSGLFactory");
            builder.addForceLinkClass("com.sun.glass.ui.ios.**.*");
            builder.addForceLinkClass("javafx.scene.CssStyleHelper");
            builder.addForceLinkClass("com.sun.prism.shader.**.*");
            builder.addForceLinkClass("com.sun.scenario.effect.impl.es2.ES2ShaderSource");
            builder.addForceLinkClass("com.sun.javafx.font.coretext.CTFactory");
            builder.addForceLinkClass("sun.util.logging.PlatformLogger");

            // add default 'frameworks' needed for JFX to work
            builder.addFramework("UIKit");
            builder.addFramework("OpenGLES");
            builder.addFramework("QuartzCore");
            builder.addFramework("CoreGraphics");
            builder.addFramework("CoreText");
            builder.addFramework("ImageIO");
            builder.addFramework("MobileCoreServices");

            // todo do we need to exclude built-in JFX from JDK classpath?
        }

        // configure the runtime classpath

        try {
            for (Object object : project.getRuntimeClasspathElements()) {
                String path = (String) object;
                if (getLog().isDebugEnabled()) {
                    getLog().debug("Including classpath element for RoboVM app: " + path);
                }
                builder.addClasspathEntry(new File(path));
            }
        } catch (DependencyResolutionRequiredException e) {
            throw new MojoExecutionException("Error resolving application classpath for RoboVM build", e);
        }

        // execute the RoboVM build

        try {

            getLog().info("Compiling RoboVM app, this could take a while, especially the first time round");
            Config config = configure(builder);
            AppCompiler compiler = new AppCompiler(config);
            compiler.compile();

            return config;

        } catch (IOException e) {
            throw new MojoExecutionException("Error building RoboVM executable for app", e);
        }
    }

    protected String getRoboVMVersion() {
        return Version.getVersion();
    }

    protected File unpackRoboVMDist() throws MojoExecutionException {

        File distTarFile = resolveRoboVMDistArtifact();
        File unpackBaseDir;
        if (home != null) {
            unpackBaseDir = home;
        } else {
            // by default unpack into the local repo directory
            unpackBaseDir = new File(distTarFile.getParent(), "unpacked");
        }
        File unpackedDir = new File(unpackBaseDir, "robovm-" + getRoboVMVersion());
        unpack(distTarFile, unpackBaseDir);
        return unpackedDir;
    }

    protected File resolveRoboVMDistArtifact() throws MojoExecutionException {

        MavenArtifactHandler handler = new MavenArtifactHandler("tar.gz");
        Artifact artifact = new DefaultArtifact("org.robovm", "robovm-dist", getRoboVMVersion(), "", "tar.gz",
                "nocompiler", handler);
        return resolveArtifact(artifact);
    }

    protected File resolveJavaFXBackportRuntimeArtifact() throws MojoExecutionException {

        MavenArtifactHandler handler = new MavenArtifactHandler("jar");
        Artifact artifact = new DefaultArtifact("net.java.openjfx.backport", "openjfx-78-backport",
                "1.8.0-ea-b96.1", "", "jar", "ios", handler);
        return resolveArtifact(artifact);
    }

    protected File resolveJavaFXBackportCompatibilityArtifact() throws MojoExecutionException {

        MavenArtifactHandler handler = new MavenArtifactHandler("jar");
        Artifact artifact = new DefaultArtifact("net.java.openjfx.backport", "openjfx-78-backport-compat",
                "1.8.0.1", "", "jar", "", handler);
        return resolveArtifact(artifact);
    }

    protected File resolveJavaFXNativeArtifact() throws MojoExecutionException {

        MavenArtifactHandler handler = new MavenArtifactHandler("jar");
        Artifact artifact = new DefaultArtifact("net.java.openjfx.backport", "openjfx-78-backport-native",
                "1.8.0-ea-b96.1", "", "jar", "ios", handler);
        return resolveArtifact(artifact);
    }

    protected File unpackJavaFXNativeIOSArtifact() throws MojoExecutionException {

        File jarFile = resolveJavaFXNativeArtifact();
        // by default unpack into the local repo directory
        File unpackBaseDir = new File(jarFile.getParent(), "unpacked");
        unpack(jarFile, unpackBaseDir);
        return unpackBaseDir;
    }

    protected File resolveArtifact(Artifact artifact) throws MojoExecutionException {

        ArtifactResolutionRequest request = new ArtifactResolutionRequest();
        request.setArtifact(artifact);
        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 resolvedArtifacts = result.getArtifacts();
        artifact = (Artifact) resolvedArtifacts.iterator().next();
        return artifact.getFile();
    }

    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 getRoboVMLogger() {

        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;
    }
}