diff --git a/src/main/java/com/simpligility/maven/plugins/android/AndroidSdk.java b/src/main/java/com/simpligility/maven/plugins/android/AndroidSdk.java index dd7ef5852..a8c2f3378 100644 --- a/src/main/java/com/simpligility/maven/plugins/android/AndroidSdk.java +++ b/src/main/java/com/simpligility/maven/plugins/android/AndroidSdk.java @@ -189,8 +189,18 @@ public String getDxJarPath() { return getPathForBuildTool( BuildToolInfo.PathId.DX_JAR ); } - - /** + + /** + * @return the path to the dx.jar + */ + public String getD8JarPath() + { + final File pathToDexJar = new File( getPathForBuildTool( BuildToolInfo.PathId.DX_JAR ) ); + final File pathToD8Jar = new File( pathToDexJar.getParent(), "d8.jar" ); + return pathToD8Jar.getAbsolutePath(); + } + + /** * Get the path for proguard.jar * @return */ diff --git a/src/main/java/com/simpligility/maven/plugins/android/configuration/D8.java b/src/main/java/com/simpligility/maven/plugins/android/configuration/D8.java new file mode 100644 index 000000000..edb588164 --- /dev/null +++ b/src/main/java/com/simpligility/maven/plugins/android/configuration/D8.java @@ -0,0 +1,75 @@ +package com.simpligility.maven.plugins.android.configuration; + +import com.simpligility.maven.plugins.android.phase08preparepackage.DexMechanism; + +/** + * Configuration for the D8 execution. This class is only the definition of the parameters that are + * shadowed in + * {@link com.simpligility.maven.plugins.android.phase08preparepackage.D8Mojo} and used there. + * + * @author William Ferguson - william.ferguson@xandar.com.aui + */ +public class D8 +{ + /** + * Mirror of {@link com.simpligility.maven.plugins.android.phase08preparepackage.D8Mojo#dexJvmArguments} + */ + private String[] jvmArguments; + /** + * Mirror of {@link com.simpligility.maven.plugins.android.phase08preparepackage.D8Mojo#dexIntermediate} + */ + private Boolean intermediate; + /** + * Mirror of {@link com.simpligility.maven.plugins.android.phase08preparepackage.D8Mojo#dexMainDexList} + */ + private String mainDexList; + + private String dexArguments; + + /** + * Mirror of {@link com.simpligility.maven.plugins.android.phase08preparepackage.D8Mojo#dexRelease} + */ + private Boolean release; + + /** + * Mirror of {@link com.simpligility.maven.plugins.android.phase08preparepackage.D8Mojo#dexMinApi} + */ + private Integer minApi; + + private DexMechanism dexMechanism = DexMechanism.Dex; + + public String[] getJvmArguments() + { + return jvmArguments; + } + + public Boolean isIntermediate() + { + return intermediate; + } + + public String getMainDexList() + { + return mainDexList; + } + + public String getDexArguments() + { + return dexArguments; + } + + public DexMechanism getDexMechanism() + { + return dexMechanism; + } + + public Boolean isRelease() + { + return release; + } + + public Integer getMinApi() + { + return minApi; + } +} diff --git a/src/main/java/com/simpligility/maven/plugins/android/configuration/Dex.java b/src/main/java/com/simpligility/maven/plugins/android/configuration/Dex.java index 5cc24be4d..9824a1bf0 100644 --- a/src/main/java/com/simpligility/maven/plugins/android/configuration/Dex.java +++ b/src/main/java/com/simpligility/maven/plugins/android/configuration/Dex.java @@ -1,5 +1,7 @@ package com.simpligility.maven.plugins.android.configuration; +import com.simpligility.maven.plugins.android.phase08preparepackage.DexMechanism; + /** * Configuration for the dex test execution. This class is only the definition of the parameters that are * shadowed in @@ -60,6 +62,7 @@ public class Dex private String dexArguments; + private DexMechanism dexMechanism = DexMechanism.Dex; public String[] getJvmArguments() { @@ -125,4 +128,9 @@ public String getDexArguments() { return dexArguments; } + + public DexMechanism getDexMechanism() + { + return dexMechanism; + } } diff --git a/src/main/java/com/simpligility/maven/plugins/android/phase08preparepackage/D8Mojo.java b/src/main/java/com/simpligility/maven/plugins/android/phase08preparepackage/D8Mojo.java new file mode 100644 index 000000000..d7ed4d6d3 --- /dev/null +++ b/src/main/java/com/simpligility/maven/plugins/android/phase08preparepackage/D8Mojo.java @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2009 Jayway AB + * Copyright (C) 2007-2008 JVending Masa + * + * 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.simpligility.maven.plugins.android.phase08preparepackage; + +import com.simpligility.maven.plugins.android.AbstractAndroidMojo; +import com.simpligility.maven.plugins.android.CommandExecutor; +import com.simpligility.maven.plugins.android.ExecutionException; +import com.simpligility.maven.plugins.android.IncludeExcludeSet; +import com.simpligility.maven.plugins.android.configuration.D8; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Resource; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.jar.JarArchiver; +import org.codehaus.plexus.archiver.util.DefaultFileSet; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.simpligility.maven.plugins.android.InclusionExclusionResolver.filterArtifacts; + +/** + * Converts compiled Java classes (including those containing Java 8 syntax) to the Android dex format. + * It is a replacement for the {@link DexMojo}. + * + * You should only run one or the other. + * By default D8 will run and Dex will not. But this is determined by the + * + * @author william.ferguson@xandar.com.au + */ +@Mojo( + name = "d8", + defaultPhase = LifecyclePhase.PREPARE_PACKAGE, + requiresDependencyResolution = ResolutionScope.COMPILE +) +public class D8Mojo extends AbstractAndroidMojo +{ + private static final String JAR = "jar"; + + /** + * Configuration for the D8 command execution. It can be configured in the plugin configuration like so + * + *
+ * <dex> + * <dexMechanism>d8|dex</dexMechanism> + * <jvmArguments> + * <jvmArgument>-Xms256m</jvmArgument> + * <jvmArgument>-Xmx512m</jvmArgument> + * </jvmArguments> + * <intermediate>true|false</intermediate> + * <mainDexList>path to class list file</mainDexList> + * <release>path to class list file</release> + * <minApi>path to class list file</minApi> + * <arguments> + * <argument>--someOtherArgA</argument> + * <argument>--someOtherArgB</argument> + * </arguments> + * </dex> + *+ * + * or via properties dex* or command line parameters android.dex.* + */ + @Parameter + private D8 dex; + + /** + * Extra JVM Arguments. Using these you can e.g. increase memory for the jvm running the build. + */ + @Parameter( property = "android.dex.jvmArguments", defaultValue = "-Xmx1024M" ) + private String[] dexJvmArguments; + + /** + * Decides whether to pass the --intermediate flag to d8. + */ + @Parameter( property = "android.dex.intermediate", defaultValue = "false" ) + private boolean dexIntermediate; + + /** + * Full path to class list to multi dex + */ + @Parameter( property = "android.dex.maindexlist" ) + private String dexMainDexList; + + /** + * Whether to pass the --release flag to d8. + */ + @Parameter( property = "android.dex.release", defaultValue = "false" ) + private boolean dexRelease; + + /** + * The minApi (if any) to pass to d8. + */ + @Parameter( property = "android.dex.release" ) + private Integer dexMinApi; + + /** + * Additional command line parameters passed to d8. + */ + @Parameter( property = "android.dex.dexarguments" ) + private String dexArguments; + + /** + * The name of the obfuscated JAR. + */ + @Parameter( property = "android.proguard.obfuscatedJar" ) + private File obfuscatedJar; + + /** + * Skips transitive dependencies. May be useful if the target classes directory is populated with the + * {@code maven-dependency-plugin} and already contains all dependency classes. + */ + @Parameter( property = "skipDependencies", defaultValue = "false" ) + private boolean skipDependencies; + + /** + * Allows to include or exclude artifacts by type. The {@code include} parameter has higher priority than the + * {@code exclude} parameter. These two parameters can be overridden by the {@code artifactSet} parameter. Empty + * strings are ignored. Example: + *
+ * <artifactTypeSet> + * <includes> + * <include>aar</include> + * <includes> + * <excludes> + * <exclude>jar</exclude> + * <excludes> + * </artifactTypeSet> + *+ */ + @Parameter( property = "artifactTypeSet" ) + private IncludeExcludeSet artifactTypeSet; + + /** + * Allows to include or exclude artifacts by {@code groupId}, {@code artifactId}, and {@code versionId}. The + * {@code include} parameter has higher priority than the {@code exclude} parameter. These two parameters can + * override the {@code artifactTypeSet} and {@code skipDependencies} parameters. Artifact {@code groupId}, + * {@code artifactId}, and {@code versionId} are specified by a string with the respective values separated using + * a colon character {@code :}. {@code artifactId} and {@code versionId} can be optional covering an artifact + * range. Empty strings are ignored. Example: + *
+ * <artifactTypeSet> + * <includes> + * <include>foo-group:foo-artifact:1.0-SNAPSHOT</include> + * <include>bar-group:bar-artifact:1.0-SNAPSHOT</include> + * <include>baz-group:*</include> + * <includes> + * <excludes> + * <exclude>qux-group:qux-artifact:*</exclude> + * <excludes> + * </artifactTypeSet> + *+ */ + @Parameter( property = "artifactSet" ) + private IncludeExcludeSet artifactSet; + + private String[] parsedJvmArguments; + private boolean parsedIntermediate; + private String parsedMainDexList; + private String parsedDexArguments; + private DexMechanism parsedDexMechanism; + private boolean parsedRelease; + private Integer parsedMinApi; + + /** + * @throws MojoExecutionException + * @throws MojoFailureException + */ + @Override + public void execute() throws MojoExecutionException, MojoFailureException + { + parseConfiguration(); + + getLog().debug( "DexMechanism set to " + parsedDexMechanism ); + if ( parsedDexMechanism != DexMechanism.D8 ) + { + getLog().info( "Not executing D8Mojo because DexMechanism set to " + parsedDexMechanism ); + return; + } + + CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor(); + executor.setLogger( getLog() ); + + if ( generateApk ) + { + runD8( executor ); + } + + if ( attachJar ) + { + File jarFile = new File( targetDirectory + File.separator + + finalName + ".jar" ); + projectHelper.attachArtifact( project, "jar", project.getArtifact().getClassifier(), jarFile ); + } + + if ( attachSources ) + { + // Also attach an .apksources, containing sources from this project. + final File apksources = createApkSourcesFile(); + projectHelper.attachArtifact( project, "apksources", apksources ); + } + } + + private List
* <dex> + * <dexMechanism>d8|dex</dexMechanism> * <jvmArguments> * <jvmArgument>-Xms256m</jvmArgument> * <jvmArgument>-Xmx512m</jvmArgument> @@ -243,6 +244,7 @@ public class DexMojo extends AbstractAndroidMojo private boolean parsedMinimalMainDex; private boolean parsedGenerateMainDexList; private String parsedDexArguments; + private DexMechanism parsedDexMechanism; /** * @throws MojoExecutionException @@ -251,15 +253,24 @@ public class DexMojo extends AbstractAndroidMojo @Override public void execute() throws MojoExecutionException, MojoFailureException { + parseConfiguration(); + + getLog().debug( "DexMechanism set to " + parsedDexMechanism ); + if ( parsedDexMechanism != DexMechanism.Dex ) + { + getLog().info( "Not executing DexMojo because DexMechanism set to " + parsedDexMechanism ); + return; + } + if ( getJack().isEnabled() ) { //Dexxing is handled by Jack return; } + CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor(); executor.setLogger( getLog() ); - parseConfiguration(); File outputFile; if ( parsedMultiDex ) { @@ -473,6 +484,7 @@ private void parseConfiguration() { parsedDexArguments = dex.getDexArguments(); } + parsedDexMechanism = dex.getDexMechanism(); } else @@ -490,6 +502,7 @@ private void parseConfiguration() parsedMinimalMainDex = dexMinimalMainDex; parsedGenerateMainDexList = dexGenerateMainDexList; parsedDexArguments = dexArguments; + parsedDexMechanism = DexMechanism.Dex; } } diff --git a/src/main/java/com/simpligility/maven/plugins/android/phase09package/ApkMojo.java b/src/main/java/com/simpligility/maven/plugins/android/phase09package/ApkMojo.java index b09224a88..b396aee07 100644 --- a/src/main/java/com/simpligility/maven/plugins/android/phase09package/ApkMojo.java +++ b/src/main/java/com/simpligility/maven/plugins/android/phase09package/ApkMojo.java @@ -20,13 +20,13 @@ import com.android.sdklib.build.ApkCreationException; import com.android.sdklib.build.DuplicateFileException; import com.android.sdklib.build.SealedApkException; -import com.simpligility.maven.plugins.android.AbstractAndroidMojo; import com.google.common.io.Files; +import com.simpligility.maven.plugins.android.AbstractAndroidMojo; import com.simpligility.maven.plugins.android.AndroidNdk; import com.simpligility.maven.plugins.android.AndroidSigner; -import com.simpligility.maven.plugins.android.IncludeExcludeSet; import com.simpligility.maven.plugins.android.CommandExecutor; import com.simpligility.maven.plugins.android.ExecutionException; +import com.simpligility.maven.plugins.android.IncludeExcludeSet; import com.simpligility.maven.plugins.android.common.AaptCommandBuilder; import com.simpligility.maven.plugins.android.common.AndroidExtension; import com.simpligility.maven.plugins.android.common.NativeHelper; @@ -36,7 +36,6 @@ import com.simpligility.maven.plugins.android.configuration.Apk; import com.simpligility.maven.plugins.android.configuration.MetaInf; import com.simpligility.maven.plugins.android.configuration.Sign; - import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.DirectoryFileFilter; import org.apache.commons.io.filefilter.FileFileFilter; @@ -733,7 +732,8 @@ private void doAPKWithAPKBuilder( File outputFile, File dexFile, File zipArchive for ( File sourceFolder : sourceFolders ) { getLog().debug( "Adding source folder : " + sourceFolder ); - apkBuilder.addSourceFolder( sourceFolder ); + // Use ApkBuilder#addFile() to explicitly add resource files so that we can add META-INF/services. + addResourcesFromFolder( apkBuilder, sourceFolder ); } for ( File jarFile : jarFiles ) @@ -799,9 +799,9 @@ public boolean accept( File dir, String name ) } apkBuilder.sealApk(); } - catch ( ApkCreationException e ) + catch ( ApkCreationException | SealedApkException | IOException e ) { - throw new MojoExecutionException( e.getMessage() ); + throw new MojoExecutionException( e.getMessage(), e ); } catch ( DuplicateFileException e ) { @@ -809,9 +809,53 @@ public boolean accept( File dir, String name ) e.getArchivePath(), e.getFile1(), e.getFile2() ); throw new MojoExecutionException( msg, e ); } - catch ( SealedApkException e ) + } + + /** + * Collect all Files from Folder (recursively) that are not class files. + */ + private void collectFiles( File folder, final ListcollectedFiles ) + { + folder.listFiles( new FileFilter() + { + @Override + public boolean accept( File file ) + { + if ( file.isDirectory() ) + { + collectFiles( file, collectedFiles ); + } + else if ( file.isFile() ) + { + if ( !file.getName().endsWith( ".class" ) ) + { + collectedFiles.add( file ); + } + } + return false; + } + } ); + + } + /** + * Adds all non-class files from folder, so that we can add META-INF/services resources. + */ + private void addResourcesFromFolder( ApkBuilder builder, File folder ) + throws SealedApkException, DuplicateFileException, ApkCreationException, IOException + { + final int folderPathLength = folder.getCanonicalPath().length(); + + final List resourceFiles = new ArrayList<>( ); + collectFiles( folder, resourceFiles ); + + for ( final File resourceFile : resourceFiles ) { - throw new MojoExecutionException( e.getMessage() ); + final String resourceName = resourceFile + .getCanonicalPath() + .substring( folderPathLength + 1 ) + .replaceAll( "\\\\", "/" ); + getLog().info( "Adding resource " + resourceFile + " : " + resourceName ); + builder.addFile( resourceFile, resourceName ); } } diff --git a/src/main/resources/META-INF/plexus/components.xml b/src/main/resources/META-INF/plexus/components.xml index 8b2ddb001..a6ff57c3a 100644 --- a/src/main/resources/META-INF/plexus/components.xml +++ b/src/main/resources/META-INF/plexus/components.xml @@ -35,9 +35,12 @@ org.apache.maven.plugins:maven-resources-plugin:testResources org.apache.maven.plugins:maven-compiler-plugin:testCompile org.apache.maven.plugins:maven-surefire-plugin:test -com.simpligility.maven.plugins:android-maven-plugin:dex ++ com.simpligility.maven.plugins:android-maven-plugin:dex + org.apache.maven.plugins:maven-jar-plugin:jar, + com.simpligility.maven.plugins:android-maven-plugin:d8, com.simpligility.maven.plugins:android-maven-plugin:apk org.apache.maven.plugins:maven-install-plugin:install