Maven:在使用 retrolambda-maven-plugin 和 DEX-ed 的应用程序中使用 Java 8 个库 android-maven-plugin
Maven: Using Java 8 libraries in applications instrumented with retrolambda-maven-plugin and DEX-ed with android-maven-plugin
我写了一小部分迷你库供我内部使用。这是使用 Maven 构建的集合。这些库针对 "regular" Java、GWT 和 Android。其中一些是用 Java 8 编写的,因为我无意在 GWT 或 Android 上 运行 它们,因此其他库是用旧的 Java 6 编写的支持这两个我计划将我的库完全迁移到 Java 8(就语言功能而言),并且我成功地 运行 Java 8 在尚未发布的 GWT 2.8.0 上重写了库.但是,我无法使 Java 8 个重写的库为 Android 应用程序编译。问题是 Retrolambda(retrolambda-maven-plugin
插件)似乎只能处理当前的 Maven 模块 类,并完全忽略依赖性 类。因此,android-maven-plugin
破坏了目标应用程序构建:
[INFO] UNEXPECTED TOP-LEVEL EXCEPTION:
[INFO] com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)
[INFO] at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472)
[INFO] at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406)
[INFO] at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388)
[INFO] at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251)
[INFO] at com.android.dx.command.dexer.Main.processClass(Main.java:665)
[INFO] at com.android.dx.command.dexer.Main.processFileBytes(Main.java:634)
[INFO] at com.android.dx.command.dexer.Main.access0(Main.java:78)
[INFO] at com.android.dx.command.dexer.Main.processFileBytes(Main.java:572)
[INFO] at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)
[INFO] at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
[INFO] at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
[INFO] at com.android.dx.command.dexer.Main.processOne(Main.java:596)
[INFO] at com.android.dx.command.dexer.Main.processAllFiles(Main.java:498)
[INFO] at com.android.dx.command.dexer.Main.runMonoDex(Main.java:264)
[INFO] at com.android.dx.command.dexer.Main.run(Main.java:230)
[INFO] at com.android.dx.command.dexer.Main.main(Main.java:199)
[INFO] at com.android.dx.command.Main.main(Main.java:103)
[INFO] ...while parsing foo/bar/FooBar.class
retrolambda-maven-plugin
配置如下:
<plugin>
<groupId>net.orfjackal.retrolambda</groupId>
<artifactId>retrolambda-maven-plugin</artifactId>
<version>2.0.2</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>process-main</goal>
<goal>process-test</goal>
</goals>
</execution>
</executions>
<configuration>
<target>1.6</target>
<defaultMethods>true</defaultMethods>
</configuration>
</plugin>
是否可以配置 Retrolambda 插件来处理库 类 以及所有依赖项?或者也许我可以使用另一个字节码处理工具?
更新 #1
我认为我对 Retrolambda 失败的看法是错误的。再深入一点,我发现 android-maven-plugin
应该为此负责,因为它直接从 Maven 存储库中选择未检测的 JAR 文件,而不是从 target
目录中选择。启用详细日志记录发现了 android-maven-plugin
:
调用的伪代码命令
$JAVA_HOME/jre/bin/java
-Xmx1024M
-jar "$ANDROID_HOME/sdk/build-tools/android-4.4/lib/dx.jar"
--dex
--output=$BUILD_DIRECTORY/classes.dex
$BUILD_DIRECTORY/classes
$M2_REPO/foo1/bar1/0.1-SNAPSHOT/bar1-0.1-SNAPSHOT.jar
$M2_REPO/foo2/bar2/0.1-SNAPSHOT/bar2-0.1-SNAPSHOT.jar
$M2_REPO/foo3/bar3/0.1-JAVA-8-SNAPSHOT/bar3-0.1-JAVA-8-SNAPSHOT.jar
我的想法是执行 maven-dependency-plugin
插件将这三个工件获取到 $BUILD_DIRECTORY/classes
目录中,让 retrolambda-maven-plugin
检测依赖项。比方说:
第一步:复制依赖到目标目录
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
第 2 步:使用 Retrolambda
检测复制的依赖项
调用retrolambda-maven-plugin
第 3 步:编译 DEX 文件
调用 android-maven-plugin
排除复制到目标目录的依赖项,因为它们都应该位于目标目录中
但这也失败了,因为我找不到一种方法来排除工件,避免使用 android-maven-plugin
。
如何禁止从以非检测状态存储它们的存储库中提取工件?
我的插件配置:
- org.apache.maven.plugins:maven-dependency-plugin:2.10
- net.orfjackal.retrolambda:retrolambda-maven-plugin:2.0.2
- com.jayway.maven.plugins.android.generation2:android-maven-plugin:3.9.0-rc.3
更新#2
Simpligility 团队发布了 Android Maven 插件 4.4.1,具有下述场景。查看更多插件 changelog.
<plugin>
<groupId>com.simpligility.maven.plugins</groupId>
<artifactId>android-maven-plugin</artifactId>
<version>4.4.1</version>
</plugin>
可以在 http://simpligility.github.io/android-maven-plugin/instrumentation.html
找到示例场景
四个月后,经过一段时间的调查,我终于成功了。首先,解决方案的关键是修补 android-maven-plugin
。我在 GitHub 上分叉了原始插件并添加了一些配置过滤器选项,允许按组 ID、工件 ID 及其各自的版本包含或排除工件。这对配置retrolambda-maven-plugin
非常重要。整体工作流程与我在问题中提到的基本相同。这是:
maven-compiler-plugin
启用Java 8 种语言功能支持。可选,因为主要 objective 正在处理 Java 8 个依赖项。
maven-dependency-plugin
将所有 Java 8 个依赖项解压到当前项目构建目标目录以供进一步处理。
retrolambda-maven-plugin
使用 Retrolambda 插件处理所有获得的 class 文件。
android-maven-plugin
使用原始 android-maven-plugin
的分支编译 DEX 和 APK 文件。
正在安装前叉:
#!/bin/bash
# The last upstream merge revision
PLUGIN_COMMIT=a79e45bc0721bfea97ec139311fe31d959851476
# Clone the fork
git clone https://github.com/lyubomyr-shaydariv/android-maven-plugin.git
# Ensure proper revision
cd android-maven-plugin
git checkout $PLUGIN_COMMIT
# Build the forked plugin, no tests
mvn clean package -Dmaven.test.skip=true
# Clone plugin JAR
cd target
cp android-maven-plugin-4.3.1-SNAPSHOT.jar android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar
# Clone and modify pom.xml
cp ../pom.xml pom-$PLUGIN_COMMIT.xml
sed -i "s/<version>4.3.1-SNAPSHOT<\/version>/<version>4.3.1-SNAPSHOT-$PLUGIN_COMMIT<\/version>/g" pom-$PLUGIN_COMMIT.xml
# Update the plugin descriptor
unzip android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar META-INF/maven/plugin.xml
sed -i "s/<version>4.3.1-SNAPSHOT<\/version>/<version>4.3.1-SNAPSHOT-$PLUGIN_COMMIT<\/version>/g" META-INF/maven/plugin.xml
zip android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar META-INF/maven/plugin.xml
# Install the plugin
mvn org.apache.maven.plugins:maven-install-plugin:2.5.2:install-file -DpomFile=pom-$PLUGIN_COMMIT.xml -Dfile=android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar
完成。接下来,在您的 pom.xml
中注册 fork 并配置构建插件:
<!-- enable Java 8 for the current module -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- unpack Java 8 dependency classes to the current module build directory -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<includeGroupIds>foo-group,bar-group,baz-group</includeGroupIds>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- Convert Java 8 to Java 6 -->
<plugin>
<groupId>net.orfjackal.retrolambda</groupId>
<artifactId>retrolambda-maven-plugin</artifactId>
<version>2.0.6</version>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>process-main</goal>
<goal>process-test</goal>
</goals>
</execution>
</executions>
<configuration>
<defaultMethods>true</defaultMethods>
<target>1.6</target>
</configuration>
</plugin>
<!-- DEXify and build the APK excluding the Java 8 dependencies as they are already processed -->
<plugin>
<groupId>com.simpligility.maven.plugins</groupId>
<artifactId>android-maven-plugin</artifactId>
<version>4.3.1-SNAPSHOT-a79e45bc0721bfea97ec139311fe31d959851476</version>
<executions>
<execution>
<phase>package</phase>
</execution>
</executions>
<configuration>
<androidManifestFile>${project.basedir}/src/main/android/AndroidManifest.xml</androidManifestFile>
<assetsDirectory>${project.basedir}/src/main/android/assets</assetsDirectory>
<resourceDirectory>${project.basedir}/src/main/android/res</resourceDirectory>
<sdk>
<platform>19</platform>
</sdk>
<undeployBeforeDeploy>true</undeployBeforeDeploy>
<proguard>
<skip>true</skip>
<config>${project.basedir}/proguard.conf</config>
</proguard>
<excludes>
<exclude>foo-group</exclude>
<exclude>bar-group</exclude>
<exclude>baz-group</exclude>
</excludes>
</configuration>
<extensions>true</extensions>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard-base</artifactId>
<version>5.2.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
应该可以。
更新
最近我修补了 Proguard Maven mojo 的分支。
更新 2
android-maven-plugin
存储库所有者 merged 我的提交,因此工件过滤安排在下一个版本。
我写了一小部分迷你库供我内部使用。这是使用 Maven 构建的集合。这些库针对 "regular" Java、GWT 和 Android。其中一些是用 Java 8 编写的,因为我无意在 GWT 或 Android 上 运行 它们,因此其他库是用旧的 Java 6 编写的支持这两个我计划将我的库完全迁移到 Java 8(就语言功能而言),并且我成功地 运行 Java 8 在尚未发布的 GWT 2.8.0 上重写了库.但是,我无法使 Java 8 个重写的库为 Android 应用程序编译。问题是 Retrolambda(retrolambda-maven-plugin
插件)似乎只能处理当前的 Maven 模块 类,并完全忽略依赖性 类。因此,android-maven-plugin
破坏了目标应用程序构建:
[INFO] UNEXPECTED TOP-LEVEL EXCEPTION:
[INFO] com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)
[INFO] at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472)
[INFO] at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406)
[INFO] at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388)
[INFO] at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251)
[INFO] at com.android.dx.command.dexer.Main.processClass(Main.java:665)
[INFO] at com.android.dx.command.dexer.Main.processFileBytes(Main.java:634)
[INFO] at com.android.dx.command.dexer.Main.access0(Main.java:78)
[INFO] at com.android.dx.command.dexer.Main.processFileBytes(Main.java:572)
[INFO] at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)
[INFO] at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
[INFO] at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
[INFO] at com.android.dx.command.dexer.Main.processOne(Main.java:596)
[INFO] at com.android.dx.command.dexer.Main.processAllFiles(Main.java:498)
[INFO] at com.android.dx.command.dexer.Main.runMonoDex(Main.java:264)
[INFO] at com.android.dx.command.dexer.Main.run(Main.java:230)
[INFO] at com.android.dx.command.dexer.Main.main(Main.java:199)
[INFO] at com.android.dx.command.Main.main(Main.java:103)
[INFO] ...while parsing foo/bar/FooBar.class
retrolambda-maven-plugin
配置如下:
<plugin>
<groupId>net.orfjackal.retrolambda</groupId>
<artifactId>retrolambda-maven-plugin</artifactId>
<version>2.0.2</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>process-main</goal>
<goal>process-test</goal>
</goals>
</execution>
</executions>
<configuration>
<target>1.6</target>
<defaultMethods>true</defaultMethods>
</configuration>
</plugin>
是否可以配置 Retrolambda 插件来处理库 类 以及所有依赖项?或者也许我可以使用另一个字节码处理工具?
更新 #1
我认为我对 Retrolambda 失败的看法是错误的。再深入一点,我发现 android-maven-plugin
应该为此负责,因为它直接从 Maven 存储库中选择未检测的 JAR 文件,而不是从 target
目录中选择。启用详细日志记录发现了 android-maven-plugin
:
$JAVA_HOME/jre/bin/java
-Xmx1024M
-jar "$ANDROID_HOME/sdk/build-tools/android-4.4/lib/dx.jar"
--dex
--output=$BUILD_DIRECTORY/classes.dex
$BUILD_DIRECTORY/classes
$M2_REPO/foo1/bar1/0.1-SNAPSHOT/bar1-0.1-SNAPSHOT.jar
$M2_REPO/foo2/bar2/0.1-SNAPSHOT/bar2-0.1-SNAPSHOT.jar
$M2_REPO/foo3/bar3/0.1-JAVA-8-SNAPSHOT/bar3-0.1-JAVA-8-SNAPSHOT.jar
我的想法是执行 maven-dependency-plugin
插件将这三个工件获取到 $BUILD_DIRECTORY/classes
目录中,让 retrolambda-maven-plugin
检测依赖项。比方说:
第一步:复制依赖到目标目录
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
第 2 步:使用 Retrolambda
检测复制的依赖项调用retrolambda-maven-plugin
第 3 步:编译 DEX 文件
调用 android-maven-plugin
排除复制到目标目录的依赖项,因为它们都应该位于目标目录中
但这也失败了,因为我找不到一种方法来排除工件,避免使用 android-maven-plugin
。
如何禁止从以非检测状态存储它们的存储库中提取工件?
我的插件配置:
- org.apache.maven.plugins:maven-dependency-plugin:2.10
- net.orfjackal.retrolambda:retrolambda-maven-plugin:2.0.2
- com.jayway.maven.plugins.android.generation2:android-maven-plugin:3.9.0-rc.3
更新#2
Simpligility 团队发布了 Android Maven 插件 4.4.1,具有下述场景。查看更多插件 changelog.
<plugin>
<groupId>com.simpligility.maven.plugins</groupId>
<artifactId>android-maven-plugin</artifactId>
<version>4.4.1</version>
</plugin>
可以在 http://simpligility.github.io/android-maven-plugin/instrumentation.html
找到示例场景四个月后,经过一段时间的调查,我终于成功了。首先,解决方案的关键是修补 android-maven-plugin
。我在 GitHub 上分叉了原始插件并添加了一些配置过滤器选项,允许按组 ID、工件 ID 及其各自的版本包含或排除工件。这对配置retrolambda-maven-plugin
非常重要。整体工作流程与我在问题中提到的基本相同。这是:
maven-compiler-plugin
启用Java 8 种语言功能支持。可选,因为主要 objective 正在处理 Java 8 个依赖项。maven-dependency-plugin
将所有 Java 8 个依赖项解压到当前项目构建目标目录以供进一步处理。retrolambda-maven-plugin
使用 Retrolambda 插件处理所有获得的 class 文件。android-maven-plugin
使用原始android-maven-plugin
的分支编译 DEX 和 APK 文件。
正在安装前叉:
#!/bin/bash
# The last upstream merge revision
PLUGIN_COMMIT=a79e45bc0721bfea97ec139311fe31d959851476
# Clone the fork
git clone https://github.com/lyubomyr-shaydariv/android-maven-plugin.git
# Ensure proper revision
cd android-maven-plugin
git checkout $PLUGIN_COMMIT
# Build the forked plugin, no tests
mvn clean package -Dmaven.test.skip=true
# Clone plugin JAR
cd target
cp android-maven-plugin-4.3.1-SNAPSHOT.jar android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar
# Clone and modify pom.xml
cp ../pom.xml pom-$PLUGIN_COMMIT.xml
sed -i "s/<version>4.3.1-SNAPSHOT<\/version>/<version>4.3.1-SNAPSHOT-$PLUGIN_COMMIT<\/version>/g" pom-$PLUGIN_COMMIT.xml
# Update the plugin descriptor
unzip android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar META-INF/maven/plugin.xml
sed -i "s/<version>4.3.1-SNAPSHOT<\/version>/<version>4.3.1-SNAPSHOT-$PLUGIN_COMMIT<\/version>/g" META-INF/maven/plugin.xml
zip android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar META-INF/maven/plugin.xml
# Install the plugin
mvn org.apache.maven.plugins:maven-install-plugin:2.5.2:install-file -DpomFile=pom-$PLUGIN_COMMIT.xml -Dfile=android-maven-plugin-4.3.1-SNAPSHOT-$PLUGIN_COMMIT.jar
完成。接下来,在您的 pom.xml
中注册 fork 并配置构建插件:
<!-- enable Java 8 for the current module -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- unpack Java 8 dependency classes to the current module build directory -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<includeGroupIds>foo-group,bar-group,baz-group</includeGroupIds>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- Convert Java 8 to Java 6 -->
<plugin>
<groupId>net.orfjackal.retrolambda</groupId>
<artifactId>retrolambda-maven-plugin</artifactId>
<version>2.0.6</version>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>process-main</goal>
<goal>process-test</goal>
</goals>
</execution>
</executions>
<configuration>
<defaultMethods>true</defaultMethods>
<target>1.6</target>
</configuration>
</plugin>
<!-- DEXify and build the APK excluding the Java 8 dependencies as they are already processed -->
<plugin>
<groupId>com.simpligility.maven.plugins</groupId>
<artifactId>android-maven-plugin</artifactId>
<version>4.3.1-SNAPSHOT-a79e45bc0721bfea97ec139311fe31d959851476</version>
<executions>
<execution>
<phase>package</phase>
</execution>
</executions>
<configuration>
<androidManifestFile>${project.basedir}/src/main/android/AndroidManifest.xml</androidManifestFile>
<assetsDirectory>${project.basedir}/src/main/android/assets</assetsDirectory>
<resourceDirectory>${project.basedir}/src/main/android/res</resourceDirectory>
<sdk>
<platform>19</platform>
</sdk>
<undeployBeforeDeploy>true</undeployBeforeDeploy>
<proguard>
<skip>true</skip>
<config>${project.basedir}/proguard.conf</config>
</proguard>
<excludes>
<exclude>foo-group</exclude>
<exclude>bar-group</exclude>
<exclude>baz-group</exclude>
</excludes>
</configuration>
<extensions>true</extensions>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard-base</artifactId>
<version>5.2.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
应该可以。
更新
最近我修补了 Proguard Maven mojo 的分支。
更新 2
android-maven-plugin
存储库所有者 merged 我的提交,因此工件过滤安排在下一个版本。