2013-11-15 11 views
25

Per ora abbiamo una struttura di progetto con una singola cartella di origine denominata src, che contiene il codice sorgente per tre moduli. Quello che voglio fare è:Gradle più giare da una cartella di origine singola

1) Compilare il codice sorgente. Questo è fatto facilmente con la definizione sourceSets:

sourceSets { 
    main { 
     java { 
      srcDir 'src' 
     } 
    } 
} 

2) Inserire i risultati della compilazione in tre barattoli. Sto facendo questo attraverso tre attività di tipo 'jar':

sto facendo questo ora via tre attività distinte:

  • util.jar

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

  • server.jar

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

Il fatto è che server.jar dovrebbe contenere tutte le classi che non sono contenuti all'interno client.jar e util.jar. Nello script di form di formiche risolviamo questo problema usando l'attività ant difference ant. Come si può fare a gradle (il mio approccio attuale non funziona)?

Forse il mio approccio è completamente sbagliato. Per favore consiglio

P.S. per ora NON POSSIAMO cambiare la struttura della cartella del codice sorgente del progetto.

risposta

26

Inserirò qui la mia soluzione di lavoro come risposta (ho un suggerimento sul forum di gradle).

Gli ambiti in gradle sono una cosa molto strana :) Ho pensato che ogni definizione di attività crea un oggetto di una classe 'Task', che è qualcosa come 'JarTask' in questo caso particolare. Quindi posso accedere a qualsiasi proprietà della classe da qualsiasi posizione nel mio script build.gradle. Tuttavia, ho trovato l'unico posto dove posso vedere i modelli, che sono inclusi nel file jar - all'interno di un blocco from di un'attività. Così la mia soluzione di lavoro per ora è:

1) Definire una raccolta a livello di progetto per contenere i modelli da escludere dal server.jar

2) Escludere tutti i modelli in from blocco di serverJar compito.

Si prega di vedere la versione definitiva di seguito

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 
    } 
} 
+0

Questo è ottimo, ma come si fa riferimento ai file separati '.jar' da altri progetti - ad es. come potresti inserire 'client.jar' in un altro sottoprogetto? – z0r

+0

@ z0r stiamo semplicemente pubblicando gli artefatti nel repository e quindi li utilizziamo come dipendenze nei sottoprogetti. P.S. scusa per il ritardo nella risposta. – vitalidze

16

Penso che l'approccio sia sbagliato. Consiglio di realizzare un progetto con 3 sottoprogetti.

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

Se per qualche motivo non è possibile modificare la struttura di classe utilizzo di questo tipo di file di build:

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

Hai ancora bisogno di directory per i sottoprogetti ma le fonti sono in un posto come volevi.

Quando si esegue gradle assemble, si avranno 3 contenitori con un set separato di classi. Il vantaggio di questa soluzione è che realizziamo un progetto multi-modulo Gradle adatto con dipendenze corrette, non solo attività per la costruzione di vasi.

Leggere Multi-Project Builds.

+0

Grazie per il tuo commento. Ho pensato a un simile approccio, ma è molto desiderabile non modificare affatto la struttura delle directory. La ragione principale è che abbiamo molti rami SVN (più di cinquanta) e dovrebbe essere un vero problema fonderli dopo le modifiche alla struttura delle directory (sapete, i conflitti tra alberi). Un'altra ragione è che 'util' e' client' dipendono l'uno dall'altro, quindi suppongo che dovrebbero essere in un modulo separato, che porta lo stesso problema. So che questo è un disastro nella progettazione della nostra struttura del codice sorgente, ma credo che Gradle possa fare tutto ciò che può fare :) – vitalidze

+1

@vitalidze La soluzione che ho proposto non modifica la struttura delle directory. Le directory vuote aggiuntive sono un problema? Hai cicli di dipendenza? –

+0

Il fatto è che le classi non sono separate da cartelle (cioè client, server, util).Ad esempio, includo nel client jar 'a/b' e 'z/x/y', util include 'q/w/e', e il server dovrebbe includere tutte le rest restule, che possono essere sotto 'a', 'z ',' z/x ',' q ',' q/w '. – vitalidze

4

Abbiamo lo stesso problema alla mia azienda, vale a dire. codice legacy difficile da migrare in una struttura di progetto "buona" e la necessità di creare diversi jar dalla stessa base di codice. Abbiamo deciso di definire diversi set sorgente e creare ciascuno dei set sorgente utilizzando Gradle standard.

Abbiamo quindi utilizzare iteratori aggiungere JAR e javadoc-compiti per ciascun 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 
} 
1

Sono d'accordo in linea di principio con la risposta accettata troppo. Ho trovato un progetto in cui il client richiede essenzialmente due JAR dello stesso file, tranne che il Manifest è diverso solo dalla chiave Percorso di classe.

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/**' ]) 
} 

Lo stesso manifesto e il codice sorgente è quindi:

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/**'  } 

Quindi Grzegorz notazione è corretto, perché il Gradle dovrebbe sapere che ci sono due JAR diverso con GAVS. Il multi-modulo è l'opzione preferita.

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

L'unico modo per configurare per questo è di utilizzare il progetto più moduli Gradle e quindi aggiungere una compilazione e/o distribuire dipendenza progetto core/principale.

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

All'interno del progetto 'acme-micro-service-webapp', che garantisce che il dipendente 'comuni: acme-core' viene compilato prima.

PS: Sto ancora cercando di capire una soluzione migliore.

PS PS: Se si utilizza anche Maven, potrebbe essere possibile eseguire l'hook dell'attività "install".

Problemi correlati