Gradle Copyタスクで同じファイルをコピーしない方法

この記事では、Gradle でディレクトリ内のファイルを別のディレクトリにコピーする際に、中身が同じファイルはコピーしないようにする方法を紹介します。

ディレクトリをコピーする

コピー元と、コピー先のディレクトリ間のコピーをする場合

Gradleでは、Copyタスクか、自分で実装したタスク内で、project.copy を使って、ディレクトリ内のファイルを再帰的にコピーする事ができます。

更に、eachFile で、各ファイルの中身が同じかどうかチェックして、もし同じ場合にはコピーしないようにする事ができます。

ファイルの中身が同じか判定

多くの場合は、ファイルサイズと、タイムスタンプ(更新日時)を参考にして、コピーするかどうか判定するので充分な場合が多いです。

しかし、今回はファイルの中身(バイト配列)が一致するかどうかで判定したいと思います。※1
(タイムスタンプは無視する)

ファイルの中身、つまりバイト配列が同じかどうかを判定するには、Apache Commons のFileUtils.contentEqualsメソッドを利用するのがてっとり早くで簡単です。

コピータスクの実装

build.gradle 今回はKotlin DSLの例を記載します。

特定のディレクトリと、
ビルド成果物のコピーを実行するタスク実装をしてみます。

build.gradle.kts 書き方

buildscript {

    // Gradleのタスクで使う依存ライブラリの設定
    dependencies {
        // FileUtils.contentEquals を使う為に必要
        classpath("commons-io:commons-io:2.10.0")
    }

}

ApacheCommonsのFileUtilsを利用するために、
buildscriptディレクティブでライブラリ追加の宣言をします。

/**
 * build/image ディレクトリ内のファイルを、/path/to/somewhere にコピーする。
 * バイト比較でファイル内容が同じ場合は、コピーしない。
 */
tasks.register("myCopy") {
    doLast {
        val distDir = "/path/to/somewhere"

        copy {
            from("$buildDir/image")
            into(distDir)

            // コピー対象のファイルをひとつひとつチェック
            eachFile {
                // ファイルが同じ内容なら、exclueしてコピーされないようにする。
                val distFile = File(distDir, path)
                if (org.apache.commons.io.FileUtils.contentEquals(file, distFile)) {
                    exclude()
                }
            }
        }
    }
}

copyディレクティブに、from と into を記載のうえ
eachFileで、ファイルごとにコピー先とファイルの中身が同じかどうかチェックしています。

ファイルの中身が同じ場合は、excludeすることで、
そのファイルはコピーされなくなります。

まとめ

  • Copyタスクや、project.copy メソッドを使えば、簡単にディレクトリのコピーができる。
  • eachFile メソッドを使う事で、ファイルごとに追加のチェック処理を記述可能。
  • コピー対象から除きたい場合は、excludeメソッドを使う。

※1 ファイルの中身で判定したかった理由としては、
ビルド成果物をどこかのディレクトリに配置する際に、同じファイルなのにタイムスタンプが変わるのが嫌なケースがあったからです。

例えば、共有の社内ドライブに、成果物のファイル郡を配置しておいて、XCOPYや、ROBOCOPYでダウンロードしているような場合、ファイルが同じものであってもタイムスタンプが変わっていると、残念ながら再ダウンロードされてしまいます。

なので、共有のドライブにファイルを配置する際には、
中身が同じファイルであれば、再アップロードしないように(タイムスタンプが変わらないように)運用する必要がありました。

タイトルとURLをコピーしました