web-dev-qa-db-fra.com

Fichiers gradle de paramètres multiples pour la construction de plusieurs projets

J'ai la structure de projet suivante

-->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

L'idée de la structure de projet ci-dessus est que nous avons plusieurs projets qui contiennent des sous-projets, chaque projet peut avoir des dépendances avec d'autres projets. Les sous-projets à l'intérieur du projet peuvent également avoir des dépendances avec d'autres sous-projets au sein du même projet. Les projets seront spécifiés dans le settings.gradle à la racine. Aussi settings.gradle à l'intérieur de chaque projet dira quels sont les sous-projets de ce projet particulier.

Mon settings.gradle à la racine ressemblerait à

include 'Project1',
         'Project2'

et Project1 settings.gradle ressemblera à

include 'SubProject11'
         'SubProject12'

d'autres ordres de dépendance sont définis dans les fichiers build.gradle respectifs. Si I Rand gradle clean build installe à l'intérieur de l'emplacement racine (emplacement Standar), il ne semble pas utiliser les configurations dans le fichier settings.gradle du niveau projet.

Qu'est-ce que je fais ici mal?

45
Isuru

J'ai pu résoudre ce problème d'une manière relativement propre. Les améliorations sont certainement les bienvenues!

Bien que Gradle ne prenne pas en charge plusieurs settings.gradle scripts prêts à l'emploi, il est possible de créer des sous-projets individuels ayant chacun leur propre settings.gradle fichier. Supposons que vous ayez plusieurs projets A qui dépendent de plusieurs projets B, chacun avec ses propres sous-projets. La structure de votre répertoire pourrait ressembler à:

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

Hors de la boîte, Gradle attend A/settings.gradle pour ressembler à ceci:

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

Le problème est que chaque fois que B ajoute un nouveau projet, A/settings.gradle doit également changer même si le nouveau projet n'est utilisé que par B. Pour éviter cette situation, vous pouvez essayer de applyB/settings.gradle dans A/settings.gradle au lieu d'ajouter des déclarations redondantes:

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

Si vous essayez ceci, vous constaterez que Gradle échoue car il génère le mauvais projectDir pour :bar et :bap. Il suppose à tort que les inclusions de B sont relatives à settingsDir qui se trouve être A/ lorsque Gradle est appelé à partir de cette racine de projet. Pour résoudre ce problème, vous pouvez ajouter un autre script tel que B/settings-parent.gradle (le nom exact n'est pas significatif):

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)

Cela dépouille settingsDir.path et préfixe le chemin avec B/. Cela peut être étendu à plusieurs couches de settings[-parent].gradle fichiers en faisant en sorte que chaque couche s'ajoute au chemin. Vous allez maintenant appliquer ce script à A/settings.gradle:

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

Avec ce schéma, les nouveaux projets B ne cassent pas inutilement A/settings.gradle et tous les projets peuvent être utilisés sans référencer explicitement le sous-projet B. Par exemple, si ':foo' voulait utiliser 'B:bap', il peut simplement déclarer:

compile project(':bap')
20
James Wald

Si vous utilisez Gradle 3.x, essayez includeBuild (): https://docs.gradle.org/current/userguide/composite_builds.html

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

Si vous utilisez Gradle 2.x, j'ai écrit une démo pour cette fonction. J'espère que cela vous aidera: 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

Actuellement, Gradle ne prend en charge qu'un seul settings.gradle fichiers par build. Cela pourrait changer à l'avenir.

8
Peter Niederwieser

comme ce sujet a traversé mon travail quotidien assez souvent maintenant et que l'amélioration ( GRADLE-8 ) est toujours ouverte, je voudrais également partager mon approche:

À première vue, il ressemble à celui de James Wald réponse mais a une différence. Vous n'avez pas besoin de diviser vos fichiers de paramètres d'une manière ou d'une autre dans le sous-projet. Il existe un moyen propre de tout faire dans le super projet si cela vous convient. Normalement, vos petits sous-projets ne devraient pas avoir à se soucier des super projets environnants. Ils incluent leurs sous-dépendances et c'est tout. Il ne devrait pas non plus être question de la façon dont le répertoire du module est nommé, dans l'approche de Wald, le module lui-même doit connaître son nom de répertoire ('B') ici:

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

En revanche, généralement, un super projet connaît bien ses sous-projets et répertoires, car ils peuvent être des sous-modules git par exemple, qui ont des noms de correctifs bien définis qui peuvent, du point de vue du super projet, être référencés en toute sécurité.

C'est ma configuration (en utilisant 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

Dans le super projet settings.gradle, vous pouvez maintenant coder ce qui suit:

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')

Il semble toujours assez verbeux (et n'ajoute toujours pas de véritable comportement hiérarchique), mais conserve l'effort supplémentaire dans le super projet où vous devez normalement tout savoir sur vos modules contenus.

Le reste est encore assez simple.

Si vous voulez voir mon approche dans la pratique, lisez la section 5.) de ce article de blog , où j'exige explicitement cette indépendance entre les modules ou consultez simplement mon projet github sur cross- repères linguistiques . Mais sachez que vous avez besoin d'une chaîne d'outils de compilation native en cours d'exécution comme gcc ou Clang pour l'exécuter;)

J'espère que cela t'aides! Bravo Ben

5
Ben Steinert

J'ai aussi examiné cela et vous pouvez en quelque sorte le faire, mais c'est très moche! La raison pour laquelle cela fonctionne pour nous, c'est que la grande majorité du temps, nous voulons juste construire à partir du plus haut niveau.

Si cela vous aide du tout, ce que vous devez faire est que le fichier settings.gradle le plus haut référence correctement chaque projet-sous-projet directement. Obtenez ce travail en premier.

Ensuite, si Project1 et Project2 (et ainsi de suite) peuvent être construits indépendamment l'un de l'autre, vous pouvez créer un fichier settings.gradle local pour ce projet. Comme, comme je l'ai dit ci-dessus, ce n'est pas ce que nous faisons habituellement, nous appelons ce fichier settings.project1. Si nous voulons utiliser ce fichier, nous le copions dans settings.gradle. Je sais moche.

Mais cela empire réellement :) Une fois que vous avez mis ce fichier settings.gradle en place, vous générez à partir de Project1 ne verra plus le fichier build.gradle de niveau supérieur où vous avez probablement besoin de définir des choses. Pour invoquer cela, vous auriez besoin de quelque chose comme ceci ajouté à chaque fichier build.gradle au niveau du projet:

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

Ensuite, vous pouvez exécuter la construction en tant que: gradle-build local

Moche, mais si vous en avez besoin, cela fonctionne au moins. Et dans l'intérêt d'une divulgation complète, après avoir mis cela en place il y a quelques semaines, aucun des développeurs n'en a eu besoin et/ou ne l'a utilisé. Va probablement le retirer dans quelques semaines s'il continue de ne pas être utilisé.

N'oubliez pas que si vous construisez à partir du sous-projet lui-même, seul ce sous-projet (et tous les projets dépendants) sera construit (bien que tous les scripts de gradle soient compilés/évalués).

4
JoeG

Sur la base des réponses précédentes, c'est ce que je suis sorti.

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')

L'avantage est qu'il n'y a pas de duplication.

2