web-dev-qa-db-fra.com

Gradle plusieurs pots à partir d'un dossier source unique

Pour l'instant, nous avons une structure de projet avec un dossier source unique nommé src, qui contient le code source de trois modules. Ce que je veux faire, c'est:

1) Compilez le code source. Cela se fait facilement avec la définition de sourceSets:

sourceSets {
    main {
        Java {
            srcDir 'src'
        }
    }
}

2) Mettez les résultats de la compilation dans trois bocaux. Je le fais via trois tâches de type "pot":

Je le fais maintenant via trois tâches distinctes:

  • util.jar

    task utilJar(type: Jar) {
        from(sourceSets.main.output) {
            include "my/util/package/**"
        }
    }
    
  • client.jar

    task clientJar(type: Jar) {
        from(sourceSets.main.output) {
            include "my/client/package/**"
        }
    }
    
  • server.jar

    task serverJar(type: Jar) {
        from(sourceSets.main.output) {
            include "**"
        }
        excludes.addAll(utilJar.includes)
        excludes.addAll(clientJar.includes)
    }
    

Le truc c'est que server.jar doit contenir toutes les classes qui ne sont pas contenues dans client.jar et util.jar. Dans ant script de construction, nous résolvons ce problème en utilisant difference ant task. Comment cela peut-il être fait graduellement (mon approche actuelle ne fonctionne pas)?

Peut-être que mon approche est complètement fausse. Veuillez conseiller.

P.S. comme pour l'instant nous NE POUVONS PAS changer la structure du dossier du code source du projet.

34
vitalidze

Je posterai ma solution de travail ici comme réponse (j'ai un indice sur le forum de gradle).

Les étendues dans gradle sont une chose très étrange :) Je pensais que chaque définition de tâche crée un objet d'une classe 'Task', qui est quelque chose comme 'JarTask' dans ce cas particulier. Ensuite, je peux accéder à n'importe quelle propriété de la classe depuis n'importe où dans mon script build.gradle. Cependant, j'ai trouvé le seul endroit où je peux voir les modèles, qui sont inclus dans le fichier jar - à l'intérieur d'un bloc from d'une tâche. Donc, ma solution de travail pour l'instant est de:

1) Définissez une collection au niveau du projet pour contenir les modèles à exclure de server.jar

2) Exclure tous les modèles du bloc from de la tâche serverJar.

Veuillez consulter la version finale ci-dessous

sourceSets {  
    main {  
        Java {  
            srcDir 'src'  
        }  
    }  
} 

// holds classes included into client.jar and util.jar, so they are to be excluded from server.jar
ext.serverExcludes = []

// util.jar
task utilJar(type: Jar) {  
    from(sourceSets.main.output) {  
        include "my/util/package/**" 
        project.ext.serverExcludes.addAll(includes)
    }  
}

// client.jar
task clientJar(type: Jar) {  
    from(sourceSets.main.output) {  
        include "my/client/package/**"
        project.ext.serverExcludes.addAll(includes)
    }  
}

// server.jar
task serverJar(type: Jar) {  
    from(sourceSets.main.output) {  
        exclude project.ext.serverExcludes
    }  
}
30
vitalidze

Je pense que l'approche est fausse. Je recommande de faire un projet avec 3 sous-projets.

project
- util
- server (depends on util)
- client (depends on util)

Si, pour une raison quelconque, vous ne pouvez pas modifier la structure de la classe, utilisez ce type de fichiers de construction:

settings.gradle

include 'util', 'client', 'server'

build.gradle

subprojects {
    apply plugin: 'Java'
}

project(':util') {
    sourceSets {
        main {
            Java {
                srcDir '../src'
                include 'util/**'
            }
        }
    }
}

project(':server') {
    sourceSets {
        main {
            Java {
                srcDir '../src'
                include 'server/**'
            }
        }
    }
    dependencies {
        compile project(':util')
    }
}

project(':client') {
    sourceSets {
        main {
            Java {
                srcDir '../src'
                include 'client/**'
            }
        }
    }
    dependencies {
        compile project(':util')
    }
}

Vous avez toujours besoin de répertoires pour les sous-projets mais les sources sont au même endroit que vous le souhaitez.

Lorsque vous exécutez gradle assemble vous aurez 3 pots avec un ensemble de classes séparé. L'avantage de cette solution est que nous faisons un projet multi-modules Gradle approprié avec des dépendances correctes, pas seulement des tâches pour la construction de pots.

Veuillez lire Constructions multi-projets .

23
Grzegorz Żur

Nous avons le même problème dans mon entreprise, à savoir. code hérité difficile à migrer vers une "bonne" structure de projet et nécessité de créer plusieurs fichiers jar à partir de la même base de code. Nous avons décidé de définir différents ensembles de sources et de construire chacun des ensembles de sources à l'aide de Gradle standard.

Nous utilisons ensuite des itérateurs pour ajouter des tâches jar et javadoc pour chaque sourceSet:

sourceSets.all { SourceSet sourceSet ->
    Task jarTask = tasks.create("jar" + sourceSet.name, Jar.class)
    jarTask.from(sourceSet.output)
    // Configure other jar task properties: group, description, manifest etc

    Task javadocTask = tasks.create("javadoc" + sourceSet.name, Javadoc.class)
    javadocTask.setClasspath(sourceSet.output + sourceSet.compileClasspath)
    javadocTask.setSource(sourceSet.allJava)
    // Extra config for the javadoc task: group, description etc

    Task javadocJarTask = tasks.create("javadocJar" + sourceSet.name, Jar.class)
    javadocJarTask.setClassifier("javadoc") // adds "-javadoc" to the name of the jar
    javadocJarTask.from(javadocTask.outputs)
    // Add extra config: group, description, manifest etc
}
4
JonasOhrnell

Je suis également d'accord en principe avec la réponse acceptée. J'ai trouvé un projet où le client a besoin de deux JAR essentiellement du même fichier sauf que le manifeste n'est différent que par la clé Class-Path.

jar {
    manifest {
        attributes(
                "Main-Class": platformMainClass,
                "Implementation-Title": platformDisplayName,
                "Implementation-Description": platformDescription,
                "Platform-Version": platformVersion,
                "Implementation-Version": version,
                "Build-Assembly-User": System.getProperty("user.name"),
                "Build-Assembly-Date": new Java.util.Date().toString(),
                "Class-Path": configurations.compile.collect { "lib/"+it.getName() }.join(' ')
        )
    }

    duplicatesStrategy = DuplicatesStrategy.EXCLUDE

    exclude( [ 'log4j*.properties', 'uk/gov/acme/secret/product/server/**' ])
}

Le même manifeste et le code source sont alors:

task applicationClientJar(type: Jar, description: "Creates the Application  Client JAR file.") {
    dependsOn compileJava
    manifest {
        attributes(
                "Main-Class": platformMainClass,
                "Implementation-Title": platformDisplayName,
                "Implementation-Description": platformDescription,
                "Platform-Version": platformVersion,
                "Implementation-Version": version,
                "Assembly-Date": new Java.util.Date().toString()
        )
    }
    archiveName = "acme-client-${platformVersion}.jar"
    destinationDir = file("${buildDir}/libs")
    from sourceSets.main.output

    duplicatesStrategy = DuplicatesStrategy.EXCLUDE

    exclude( [ 'log4j*.properties', 'uk/gov/acme/secret/product/server/**'     }

La notation Grzegorz est donc correcte, car le Gradle devrait savoir qu'il existe deux JAR différents avec des GAV. Le multi-module est l'option préférée.

compile "uk.gov.acme.secret:acme:1.0"  // CORE
compile "uk.gov.acme.secret:acme-client:1.0"

La seule façon de configurer cela est d'utiliser le projet Gradle multi-module, puis d'ajouter une dépendance de compilation et/ou de déploiement au projet principal/principal.

project(':common:acme-micro-service-webapp') {
    dependencies {
        compile project(':common:acme-core')
    }
}

Dans le projet "acme-micro-service-webapp", cela garantit que le "common: acme-core" dépendant est d'abord compilé.

PS: J'essaie toujours de trouver une meilleure solution.

PS PS: Si vous utilisez également Maven, il peut être possible d'accrocher la tâche `install '.

2
peter_pilgrim