如何在 Android 工具中解决 javac 增量构建问题,无法检测到应导致重新编译的更改



我和我的同事最近将我们的Android工具升级到r19,并看到Google引入的新增量构建系统存在问题。增量编译似乎是一个难题,看起来他们没有解决它,所以如果我们不小心,我们现在就会得到损坏的 APK。

我向谷歌报告了这个问题,但我想知道是否有其他人看过它,如果是这样,也许他们可以游说谷歌修复它,这样工程工时就不会查看由于糟糕的构建脚本而损坏的构建。

看看这里: https://code.google.com/p/android/issues/detail?id=31242

作为一种解决方法,我修改了 Ant 脚本,以删除 Google Ant 脚本中 -build-setup 步骤之前的所有类文件。

该错误中描述的问题实际上是大多数Java编译器和工具的长期限制,与.class文件没有维护足够的信息来允许工具正确计算依赖关系有关。 Javac 甚至没有走那么远,而是只在 java 文件比相应的.class文件更新时才重新编译。 这甚至是设计使然。

我能找到的唯一确定的答案是每次构建时删除所有类文件,使您的构建不是增量的。 您可以通过将其添加到您的构建中来执行此操作.xml:

<target name="-pre-compile" depends="-pre-compile-delete"/>
<target name="-pre-compile-delete">
    <!-- Workaround for https://code.google.com/p/android/issues/detail?id=31242 -->
    <do-only-if-manifest-hasCode elseText="hasCode = false. Skipping...">
        <delete failonerror="false" dir="${out.classes.absolute.dir}"/>
    </do-only-if-manifest-hasCode>
</target>

JDK 用于解决此问题的内置工具是依赖任务。 它尝试遍历类文件,并删除其传递依赖项已更改的文件。但是,如文档中所述,depend有一些限制,所有这些限制都源于类文件不包含足够的信息来提供完整的依赖树。

相反,我建议使用依赖集任务进行更简单但相当强大的解决方法。 如果任何源代码/jar 较新,我们删除所有类文件,而不是尝试使用 dependtask 获得最小的增量编译。 我们可能会在实际不需要时重新编译,但是没有更改的构建仍然更快,并且我们不会在需要的情况下无意中跳过编译。

<target name="-pre-compile" depends="-pre-compile-dependset" />
<target name="-pre-compile-dependset">
    <!-- Alternative workaround for https://code.google.com/p/android/issues/detail?id=31242 -->
    <echo>Deleting all class files if newer sources (java or jars) are found...</echo>
    <do-only-if-manifest-hasCode elseText="hasCode = false. Skipping...">
        <!-- The names of these properties suggest they are single directories, but -->
        <!-- some projects with Android.mk files treat them like paths (with multiple dirs). -->
        <!-- Massage these paths get all the files inside multiple dirs. -->
        <path id="project.all.sources.path">
            <pathelement path="${source.absolute.dir}"/>
            <pathelement path="${gen.absolute.dir}"/>
        </path>
        <!-- convert project.all.sources.path to project.all.sources.list  -->
        <pathconvert
            refid="project.all.sources.path"
            property="project.all.sources.list"
            pathsep=","
            dirsep="/">
            <!-- Make the path elements relative to basedir and glob to match all files -->
            <!-- The result will look like: "src/**,gen/**" -->
            <chainedmapper>
                <filtermapper>
                    <replacestring from="${basedir}/" to=""/>
                </filtermapper>
                <regexpmapper from="^(.*)" to="1/**"/>
            </chainedmapper>
        </pathconvert>
        <dependset verbose="true">
            <sources>
                    <!-- merge the project's own classpath and the tested project's classpath -->
                    <path refid="project.all.jars.path" />
                    <path refid="tested.project.classpath" />
                    <!-- All source files -->
                    <fileset dir="${basedir}" includes="${project.all.sources.list}" />
            </sources>
            <targets>
                <fileset dir="${out.classes.absolute.dir}"/>
            </targets>
        </dependset>
    </do-only-if-manifest-hasCode>
</target>

这个解决方案足够可靠,我相信它最终可以合并到 sdk 版本中。

相关内容

  • 没有找到相关文章

最新更新