4

同僚と私は最近、Android ツールを r19 にアップグレードしましたが、Google が導入した新しいインクリメンタル ビルド システムで問題が発生しています。インクリメンタル コンパイルは難しい問題のように見えますが、解決されていないように見えるため、注意を怠ると APK が壊れてしまいます。

私はこの問題を Google に報告しましたが、他の誰かがそれを見たかどうかを知りたいと思っていました。もしそうなら、エンジニアリング工数が悪いビルド スクリプトのために壊れたビルドを見ないように、Google に働きかけて修正することができるかもしれません。

こちらをご覧ください: https://code.google.com/p/android/issues/detail?id=31242

回避策として、Google の Ant スクリプトの -build-setup ステップの前にすべてのクラス ファイルを削除するように Ant スクリプトを変更しました。

4

1 に答える 1

3

そのバグで説明されている問題は、実際には、ツールが依存関係を適切に計算できるようにするのに十分な情報を保持していない .class ファイルに関連する、ほとんどの Java コンパイラとツールの長年にわたる制限です。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 の組み込みツールは、依存タスクです。クラス ファイルを走査し、推移的な依存関係が変更されたファイルを削除しようとします。ただし、ドキュメントに記載されているように、依存にはいくつかの制限があり、完全な依存関係ツリーを提供するのに十分な情報が含まれていないクラス ファイルに起因します。

代わりに、依存セット タスクを使用して、よりシンプルだがかなり堅牢な回避策をお勧めします。依存タスクを使用して最小限の増分コンパイルを取得しようとする代わりに、ソース/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 に答える