4

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

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

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

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

4

1 回答 1

3

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

我能找到的唯一确定的答案是每次构建时删除所有类文件,使构建不是增量的。您可以通过将其添加到您的 build.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 有一些限制,这些限制都源于类文件没有包含足够的信息来提供完整的依赖树。

相反,我建议使用dependset task一个更简单但相当健壮的解决方法。如果任何源/jar 较新,我们将删除所有类文件,而不是尝试使用依赖任务进行最小增量编译。我们可能会在实际上不需要时重新编译,但是没有更改的构建仍然更快,并且我们不会在需要时无意中跳过编译。

<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 构建中。

于 2012-09-19T00:19:59.123 回答