web-dev-qa-db-ja.com

複数のプロジェクトを構築するための複数の設定Gradleファイル

次のプロジェクト構造があります

-->Starnderd Location
        -->Project1 
           -->settings.gradle 
           -->build.gradle
           -->Subproject11
              -->build.gradle
           -->Subproject12
              -->build.gradle
        -->Project2 
           -->settings.gradle 
           -->build.gradle
           -->Subproject21
              -->build.gradle
           -->Subproject22
              -->build.gradle
        -->build.gradle
        -->settings.gradle

上記のプロジェクト構造のアイデアは、サブプロジェクトを含む複数のプロジェクトがあり、各プロジェクトが他のプロジェクトへの依存関係を持つことができるということです。また、プロジェクト内のサブプロジェクトは、同じプロジェクト内の他のサブプロジェクトへの依存関係を持つことができます。プロジェクトは、ルートのsettings.gradleで指定されます。また、各プロジェクト内のsettings.gradleは、その特定のプロジェクトのサブプロジェクトが何であるかを示します。

ルートのsettings.gradleは次のようになります

include 'Project1',
         'Project2'

そしてProject1のsettings.gradleは次のようになります

include 'SubProject11'
         'SubProject12'

他の依存関係の順序は、それぞれのbuild.gradleファイルで定義されています。ルートの場所(Standarの場所)内でクリーンビルドインストールをRandする場合、プロジェクトレベルのsettings.gradleファイルの構成を使用していないようです。

私がここで間違っていることは何ですか?

45
Isuru

私はこの問題を比較的きれいな方法で解決することができました。改善は確かに歓迎です!

Gradleはそのままでは複数のsettings.gradleスクリプトをサポートしていませんが、それぞれ独自のsettings.gradleファイルを使用して個別のサブプロジェクトを作成することが可能です。それぞれが独自のサブプロジェクトを持つマルチプロジェクトBに依存するマルチプロジェクトAがあるとします。ディレクトリ構造は次のようになります。

A
- settings.gradle
- foo
- faz
\ B
  - settings.gradle
  - bar
  - bap

そのままでは、GradleはA/settings.gradleが次のようになることを期待しています。

include ':foo', ':faz', 'B:bar', 'B:bap'

これに関する問題は、Bが新しいプロジェクトを追加するたびに、新しいプロジェクトがBでのみ使用されている場合でもA/settings.gradleも変更する必要があることです。この状況を回避するには、追加する代わりにB/settings.gradleapplyA/settings.gradle冗長な宣言:

apply from: 'B/settings.gradle'
include ':foo', ':faz'

これを試すと、:bar:bapに対して誤ったprojectDirを生成するため、Gradleが失敗することがわかります。 BのインクルードがsettingsDirに関連していると誤って想定しているため、Gradleがそのプロジェクトルートから呼び出された場合、これはA/になります。これを修正するには、B/settings-parent.gradleなどの別のスクリプトを追加できます(正確な名前は重要ではありません)。

apply from: 'settings.gradle'

def updateProjectPaths(Set<ProjectDescriptor> projects) {
    projects.each { ProjectDescriptor project ->
        String relativeProjectPath = project.projectDir.path.replace(settingsDir.path, "")
        project.projectDir = new File("B/$relativeProjectPath")
        // Recursively update paths for all children
        updateProjectPaths(project.children)
    }
}

updateProjectPaths(rootProject.children)

これはsettingsDir.pathを取り除き、パスの前にB/を付けます。これは、各層をパスに追加することにより、settings[-parent].gradleファイルの複数の層に拡張できます。次に、このスクリプトをA/settings.gradleに適用します。

apply from: 'B/settings-parent.gradle'
include ':foo', ':faz'

このスキームを使用すると、新しいBプロジェクトがA/settings.gradleを不必要に破壊することがなくなり、Bサブプロジェクトを明示的に参照しなくてもすべてのプロジェクトを使用できます。たとえば、':foo''B:bap'を使用したい場合、次のように宣言します。

compile project(':bap')
20
James Wald

Gradle 3.xを使用している場合は、includeBuild()を試してください: https://docs.gradle.org/current/userguide/composite_builds.html

// settings.gradle
includeBuild './Project1'
includeBuild './Project2'

Gradle 2.xを使用している場合は、この関数のデモを作成しました。それがあなたを助けることを願っています: https://github.com/xujiaao/gradle-composite-build-demo

// settings.gradle
def includeProject(String path, Closure closure) {
    ...

    apply {
        from ...
        to new SettingsProxy(...)
    }
}

class SettingsProxy {
    ...

    public getRootProject() { ... }

    public void include(String... paths) {
        for (String path : paths) {
            if (!mProjectSpec.accept(path)) {
                continue
            }

            def descendantPath = generateDescendantPath(path)
            mSettings.include(descendantPath)

            def descendantProjectDir = new File(mProject.projectDir, path.replace(':', '/'))
            mSettings.project(descendantPath).projectDir = descendantProjectDir
        }
    }
}
9
Real.Xu

現在、Gradleはビルドごとに単一のsettings.gradleファイルのみをサポートしています。これは将来変更される可能性があります。

8

このトピックが私の日常の仕事をかなり頻繁に通過し、それに対する改善( GRADLE-8 )がまだ開いているので、私のアプローチも共有したいと思います。

一見すると、James Waldの answer に似ていますが、1つだけ違いがあります。サブプロジェクトで設定ファイルを何らかの方法で分割する必要はありません。スーパープロジェクトですべてを行うためのクリーンな方法があります。通常、小さなサブプロジェクトは、周囲のスーパープロジェクトを気にする必要はありません。それらにはサブ依存関係が含まれ、それだけです。また、Waldのアプローチでは、モジュール自体がそのディレクトリ名( 'B')を知る必要があるため、モジュールディレクトリの名前の付け方は無関係である必要があります。

project.projectDir = new File("B/$relativeProjectPath")

対照的に、通常、スーパープロジェクトはそのサブプロジェクトとディレクトリをよく知っています。これらは、たとえばgitサブモジュールであり、スーパープロジェクトの観点から、安全に参照できる明確に定義された修正名を持つことができるためです。

それが私のセットアップです(Gradle 2.4を使用):

Super Project
├── build.gradle
├── settings.gradle (include 'A' include 'B')
├── <Subproject A>
    ├── build.gradle
    ├── settings.gradle (include 'subA')
    ├── <Subsubproject subA>
        ├── build.gradle
├── <Subproject B>
    ├── build.gradle
    ├── settings.gradle (include 'subB')
    ├── <Subsubproject subB>
        ├── build.gradle

スーパープロジェクトのsettings.gradleで、次のコードを記述できます。

include 'A'
include 'B'
apply from: 'A/settings.gradle'
apply from: 'B/settings.gradle'
project(':subA').projectDir = new File(rootDir, 'A/subA')
project(':subB').projectDir = new File(rootDir, 'B/subB')

それはまだかなり冗長に見えます(実際の階層的な動作は追加しません)が、通常は含まれているモジュールに関するすべてを常に知る必要があるスーパープロジェクトで余分な労力を維持します。

残りは再び非常に簡単です。

私のアプローチを実際に確認したい場合は、この ブログ投稿 のセクション5.)をお読みください。モジュール間の独立性を明示的に要求するか、git-hubプロジェクトを cross-言語ベンチマーク 。ただし、それを実行するには、gccやClangなどの実行中のネイティブコンパイラツールチェーンが必要です。

お役に立てれば!乾杯のベン

5
Ben Steinert

私もこれを調べましたが、あなたはそれを行うことができますが、それは非常に醜いです!これが私たちにとってまったくうまくいく理由は、ほとんどの場合、最上位レベルから構築したいだけだからです。

それがまったく役立つ場合は、最上位のsettings.gradleファイルがすべてのプロジェクトサブプロジェクトを直接正しく参照するようにする必要があります。最初にこれを機能させます。

次に、Project1とProject2(など)が互いに独立してビルドできる場合は、そのプロジェクトのローカルのsettings.gradleファイルを作成できます。上で述べたように、これは通常行うことではないため、このファイルをsettings.project1と呼びます。このファイルを使用する場合は、settings.gradleにコピーします。私は醜いことを知っています。

しかし、実際にはさらに悪化します:)このsettings.gradleファイルを配置すると、Project1からビルドすると、おそらく定義が必要なトップレベルのbuild.gradleファイルが表示されなくなります。これを呼び出すには、すべてのプロジェクトレベルのbuild.gradleファイルに次のようなものを追加する必要があります。

if (project.hasProperty('local')) {
    apply from: '../build.gradle'
}

その後、次のようにビルドを実行できます:gradle -Plocal build

醜いですが、それが必要な場合は、少なくとも機能します。そして、完全な開示のために、これを数週間前に導入したので、どの開発者もそれを必要とせず、使用もしていません。使用しない場合は、あと数週間で削除される可能性があります。

サブプロジェクト自体からビルドする場合は、そのサブプロジェクト(および依存するプロジェクト)のみがビルドされることに注意してください(すべてのGradleスクリプトがコンパイル/評価されます)。

4
JoeG

以前の答えを基にして、これが私が思いついたものです。

interface Includer {
  def include(String... args)
}

def applyFrom(String folder) {
  apply from: folder + '/settings.gradle'
  def includer = [
    include: { args ->
          args.each {
            project(it.startsWith(':') ? it : ':' + it).projectDir =
                new File(rootDir, folder + "/" + (it.startsWith(':') ? it.substring(1) : it).replace(':', '/'))
          }
    }] as Includer

  apply from: folder + '/settings.gradle', to: includer
}

applyFrom('A')
applyFrom('B')

利点は重複がないことです。