From b6cb514d383dcef52ba6c609a863f19e1a4c1faf Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Thu, 19 Jan 2012 12:29:16 +0100 Subject: [PATCH] Generate Maven Central-compatible poms Understanding Gradle pom generation ------------------------------------------- All spring-* subprojects have had Gradle's 'maven' plugin applied to them. This means that one can run `gradle install`, and POMs will be generated according to the metadata in the build.gradle file. The 'customizePom' routine added by this commit hooks into this generation process in order to add elements to the pom required for entry into Maven Central via oss.sonatype.org[1]. This pom generation happens on-the-fly during `gradle install` and the generated poms exist only in your local .m2 cache. Therefore, you will not see the poms on the source tree after this command. Handling optional and provided dependencies ------------------------------------------- Note particularly the handling of 'optional' and 'provided' dependencies. Gradle does not have a first class notion for these concepts, nor are they significant to the actual Gradle build process, but they are important when publishing POMs for consumption via Maven Central and other Maven-compatible repositories. true indicates that a dependency need not be downloaded when resolving artifacts. e.g. spring-context has an compile-time dependency on cglib, but when a Spring user resolves spring-context from Maven Central, cglib should *not* automatically be downloaded at the same time. This is because the core functionality within spring-context can operate just fine without cglib on the classpath; it is only if the user chooses explicitly to use certain functionality, e.g. @Configuration classes, which do require cglib, that the user must declare an explicit dependency in their own build script on cglib. Marking these kinds of dependencies as 'optional' provides a kind of built in 'documentation' about which version of cglib the user should declare if in fact he wishes to. Spring has a great many compile-time dependencies, but in fact very few mandatory runtime dependencies. Therefore, *most* of Spring's dependencies are optional. provided is similar to 'optional', in that dependencies so marked should not be automatically downloaded during dependency resolution, but indicates rather that they are expected to have been provided by the user application runtime environment. For example, the Servlet API is in fact a required runtime dependency for spring-webmvc, but it is expected that it will be available via the user's servlet container classpath. Again, it serves here as a kind of 'documentation' that spring-webmvc does in fact expect the servlet api to be available, and furthermore which (minimum) version. This commit adds two closures named 'optional' and 'provided' as well as two arrays (optionalDeps, providedDeps) for tracking which dependencies are optional or provided. An optional dependency is declared as follows: compile("group:artifact:version", optional) Here, the optional closure accepts the dependency argument implicitly, and appends it to the 'optionalDeps' array. Then, during pom generation (again, the customizePom routine), these arrays are interrogated, and pom elements are updated with true or provided as appropriate. Thanks to the Spock framework for inspiration on this approach[2]. [1] http://bit.ly/wauOqP (Sonatype's central sync requirements) [2] https://github.com/spockframework/spock/blob/groovy-1.7/gradle/publishMaven.gradle#L63 --- build.gradle | 193 ++++++++++++++++++++++--------------------- publish-maven.gradle | 60 ++++++++++++++ 2 files changed, 160 insertions(+), 93 deletions(-) create mode 100644 publish-maven.gradle diff --git a/build.gradle b/build.gradle index b1bf75f6a6..3c3a2eb858 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,8 @@ configure(allprojects) { apply plugin: 'eclipse' apply plugin: 'idea' + group = 'org.springframework' + sourceCompatibility=1.5 targetCompatibility=1.5 @@ -44,9 +46,8 @@ configure(allprojects) { } } -configure(subprojects) { - apply plugin: 'maven' - group = 'org.springframework' +configure(subprojects) { subproject -> + apply from: "${rootProject.projectDir}/publish-maven.gradle" jar { from("${rootProject.projectDir}/src/dist") { @@ -141,11 +142,13 @@ project('spring-core') { builtBy project(":spring-asm").jar } compile "commons-logging:commons-logging:1.1.1" - compile("org.aspectj:aspectjweaver:1.6.8") { optional = true } - compile("net.sf.jopt-simple:jopt-simple:3.0") { optional = true + compile("org.aspectj:aspectjweaver:1.6.8", optional) + compile("net.sf.jopt-simple:jopt-simple:3.0") { dep -> + optional dep exclude group: 'org.apache.ant', module: 'ant' } - compile("log4j:log4j:1.2.15") { optional = true + compile("log4j:log4j:1.2.15") { dep -> + optional dep exclude group: 'javax.mail', module: 'mail' exclude group: 'javax.jms', module: 'jms' exclude group: 'com.sun.jdmk', module: 'jmxtools' @@ -160,9 +163,9 @@ project('spring-beans') { description = 'Spring Beans' dependencies { compile project(":spring-core") - compile("javax.el:el-api:1.0") { provided = true } - compile("javax.inject:javax.inject:1") { provided = true } - compile("cglib:cglib-nodep:2.2") { optional = true } + compile("javax.el:el-api:1.0", provided) + compile("javax.inject:javax.inject:1", provided) + compile("cglib:cglib-nodep:2.2", optional) } } @@ -170,9 +173,9 @@ project('spring-aop') { description = 'Spring AOP' dependencies { compile project(":spring-beans") - compile("com.jamonapi:jamon:2.4") { optional = true } - compile("aopalliance:aopalliance:1.0") { optional = true } - compile("commons-pool:commons-pool:1.5.3") { optional = true } + compile("com.jamonapi:jamon:2.4", optional) + compile("aopalliance:aopalliance:1.0", optional) + compile("commons-pool:commons-pool:1.5.3", optional) } } @@ -193,7 +196,7 @@ project('spring-instrument') { project('spring-instrument-tomcat') { description = 'Spring Instrument Tomcat' dependencies { - compile("org.apache.tomcat:catalina:6.0.16") { provided = true } + compile("org.apache.tomcat:catalina:6.0.16", provided) } } @@ -203,24 +206,25 @@ project('spring-context') { compile project(":spring-aop") compile project(":spring-expression") compile project(":spring-instrument") - compile("backport-util-concurrent:backport-util-concurrent:3.0") { optional = true } - compile("javax.annotation:jsr250-api:1.0") { optional = true } - compile("javax.ejb:ejb-api:3.0") { optional = true } - compile("javax.inject:javax.inject:1") { optional = true } - compile("org.apache.geronimo.specs:geronimo-jms_1.1_spec:1.1") { optional = true } - compile("org.apache.geronimo.specs:geronimo-jta_1.1_spec:1.1") { optional = true } - compile("javax.persistence:persistence-api:1.0") { optional = true } - compile("javax.validation:validation-api:1.0.0.GA") { optional = true } - compile("javax.xml.ws:jaxws-api:2.1-1") { optional = true + compile("backport-util-concurrent:backport-util-concurrent:3.0", optional) + compile("javax.annotation:jsr250-api:1.0", optional) + compile("javax.ejb:ejb-api:3.0", optional) + compile("javax.inject:javax.inject:1", optional) + compile("org.apache.geronimo.specs:geronimo-jms_1.1_spec:1.1", optional) + compile("org.apache.geronimo.specs:geronimo-jta_1.1_spec:1.1", optional) + compile("javax.persistence:persistence-api:1.0", optional) + compile("javax.validation:validation-api:1.0.0.GA", optional) + compile("javax.xml.ws:jaxws-api:2.1-1") { dep -> + optional dep exclude group: 'javax.jws', module: 'jsr181' } - compile("org.beanshell:bsh:2.0b4") { optional = true } - compile("org.codehaus.groovy:groovy-all:1.6.3") { optional = true } - compile("org.jruby:jruby:1.4.0") { optional = true } - compile("org.hibernate:hibernate-validator:4.2.0.Final") { optional = true } - compile("joda-time:joda-time:1.6") { optional = true } - compile("net.sf.ehcache:ehcache-core:2.0.0") { optional = true } - compile("org.codehaus.jsr166-mirror:jsr166:1.7.0") { provided = true } + compile("org.beanshell:bsh:2.0b4", optional) + compile("org.codehaus.groovy:groovy-all:1.6.3", optional) + compile("org.jruby:jruby:1.4.0", optional) + compile("org.hibernate:hibernate-validator:4.2.0.Final", optional) + compile("joda-time:joda-time:1.6", optional) + compile("net.sf.ehcache:ehcache-core:2.0.0", optional) + compile("org.codehaus.jsr166-mirror:jsr166:1.7.0", provided) testCompile "commons-dbcp:commons-dbcp:1.2.2" testCompile("javax.xml:jaxrpc-api:1.1") testCompile("javax.inject:com.springsource.org.atinject.tck:1.0.0") @@ -231,8 +235,8 @@ project('spring-tx') { description = 'Spring Transaction' dependencies { compile project(":spring-context") - compile("com.ibm.websphere:uow:6.0.2.17") { provided = true } - compile("javax.resource:connector-api:1.5") { optional = true } + compile("com.ibm.websphere:uow:6.0.2.17", provided) + compile("javax.resource:connector-api:1.5", optional) compile "aopalliance:aopalliance:1.0" // NOT optional, as opposed to in :spring-aop testCompile "org.easymock:easymockclassextension:2.3" } @@ -244,11 +248,11 @@ project('spring-oxm') { dependencies { compile project(":spring-context") compile "commons-lang:commons-lang:2.5" - compile("com.thoughtworks.xstream:xstream:1.3.1") { optional = true } - compile("com.sun.xml.bind:jaxb-impl:2.1.7") { optional = true } - compile("org.jibx:jibx-run:1.1.5") { optional = true } - compile("org.apache.xmlbeans:xmlbeans:2.4.0") { optional = true } - compile("org.codehaus.castor:castor-xml:1.3.2") { optional = true } + compile("com.thoughtworks.xstream:xstream:1.3.1", optional) + compile("com.sun.xml.bind:jaxb-impl:2.1.7", optional) + compile("org.jibx:jibx-run:1.1.5", optional) + compile("org.apache.xmlbeans:xmlbeans:2.4.0", optional) + compile("org.codehaus.castor:castor-xml:1.3.2", optional) testCompile "org.codehaus.jettison:jettison:1.0.1" testCompile "xmlunit:xmlunit:1.2" testCompile "xmlpull:xmlpull:1.1.3.4a" @@ -263,7 +267,7 @@ project('spring-jms') { dependencies { compile project(":spring-oxm") compile project(":spring-tx") - compile("org.codehaus.jackson:jackson-mapper-asl:1.4.2") { optional = true } + compile("org.codehaus.jackson:jackson-mapper-asl:1.4.2", optional) } } @@ -271,11 +275,11 @@ project('spring-jdbc') { description = 'Spring JDBC' dependencies { compile project(":spring-tx") - compile("c3p0:c3p0:0.9.1.2") { optional = true } - compile("hsqldb:hsqldb:1.8.0.7") { optional = true } - compile("com.h2database:h2:1.0.71") { optional = true } - compile("org.apache.derby:derby:10.5.3.0_1") { optional = true } - compile("org.apache.derby:derbyclient:10.5.3.0_1") { optional = true } + compile("c3p0:c3p0:0.9.1.2", optional) + compile("hsqldb:hsqldb:1.8.0.7", optional) + compile("com.h2database:h2:1.0.71", optional) + compile("org.apache.derby:derby:10.5.3.0_1", optional) + compile("org.apache.derby:derbyclient:10.5.3.0_1", optional) } } @@ -283,16 +287,16 @@ project('spring-context-support') { description = 'Spring Context Support' dependencies { compile project(":spring-jdbc") - compile("org.codehaus.fabric3.api:commonj:1.1.0") { optional = true } - compile("opensymphony:quartz:1.6.2") { optional = true } - compile("javax.mail:mail:1.4") { optional = true } - compile("velocity:velocity:1.5") { optional = true } - compile("commons-collections:commons-collections:3.2") { optional = true } - compile("org.freemarker:freemarker:2.3.15") { optional = true } + compile("org.codehaus.fabric3.api:commonj:1.1.0", optional) + compile("opensymphony:quartz:1.6.2", optional) + compile("javax.mail:mail:1.4", optional) + compile("velocity:velocity:1.5", optional) + compile("commons-collections:commons-collections:3.2", optional) + compile("org.freemarker:freemarker:2.3.15", optional) compile("jasperreports:jasperreports:2.0.5") { transitive = false; optional = true } - compile("commons-digester:commons-digester:1.8.1") { optional = true } - compile("commons-beanutils:commons-beanutils:1.8.0") { optional = true } - compile("com.lowagie:itext:2.0.8") { optional = true } + compile("commons-digester:commons-digester:1.8.1", optional) + compile("commons-beanutils:commons-beanutils:1.8.0", optional) + compile("com.lowagie:itext:2.0.8", optional) testCompile "hsqldb:hsqldb:1.8.0.10" testCompile("org.apache.poi:poi:3.0.2-FINAL") { exclude group: 'log4j', module: 'log4j' @@ -307,22 +311,23 @@ project('spring-web') { description = 'Spring Web' dependencies { compile project(":spring-oxm") - compile("com.caucho:hessian:3.2.1") { optional = true } // NOTE: unavailable in maven central - compile("rome:rome:1.0") { optional = true } - compile("javax.el:el-api:1.0") { optional = true } // as opposed to 'provided' in spring-core - compile("javax.faces:jsf-api:1.2_08") { optional = true } - compile("javax.portlet:portlet-api:2.0") { provided = true } - compile("org.apache.tomcat:tomcat-servlet-api:7.0.8") { provided = true } // servlet-api 3.0 - compile("javax.servlet.jsp:jsp-api:2.1") { provided = true } - compile("javax.xml.soap:saaj-api:1.3") { provided = true } - compile("axis:axis:1.4") { optional = true } - compile("commons-fileupload:commons-fileupload:1.2") { optional = true } - runtime("commons-io:commons-io:1.3") { optional = true } - compile("commons-httpclient:commons-httpclient:3.1") { optional = true } - compile("org.apache.httpcomponents:httpclient:4.1.1") { optional = true } - compile("org.codehaus.jackson:jackson-mapper-asl:1.4.2") { optional = true } - compile("taglibs:standard:1.1.2") { optional = true } - compile("org.mortbay.jetty:jetty:6.1.9") { optional = true + compile("com.caucho:hessian:3.2.1", optional) + compile("rome:rome:1.0", optional) + compile("javax.el:el-api:1.0", optional) + compile("javax.faces:jsf-api:1.2_08", optional) + compile("javax.portlet:portlet-api:2.0", provided) + compile("org.apache.tomcat:tomcat-servlet-api:7.0.8", provided) // servlet-api 3.0 + compile("javax.servlet.jsp:jsp-api:2.1", provided) + compile("javax.xml.soap:saaj-api:1.3", provided) + compile("axis:axis:1.4", optional) + compile("commons-fileupload:commons-fileupload:1.2", optional) + runtime("commons-io:commons-io:1.3", optional) + compile("commons-httpclient:commons-httpclient:3.1", optional) + compile("org.apache.httpcomponents:httpclient:4.1.1", optional) + compile("org.codehaus.jackson:jackson-mapper-asl:1.4.2", optional) + compile("taglibs:standard:1.1.2", optional) + compile("org.mortbay.jetty:jetty:6.1.9") { dep -> + optional dep exclude group: 'org.mortbay.jetty', module: 'servlet-api-2.5' } testCompile "xmlunit:xmlunit:1.2" @@ -337,17 +342,17 @@ project('spring-orm') { dependencies { // compiling against both hibernate 3 and 4 here in order to support // our respective orm.hibernate3 and orm.hibernate4 packages - compile("org.hibernate:com.springsource.org.hibernate:3.3.1.GA") { optional = true } - compile("org.hibernate:hibernate-core:4.0.0.CR7") { optional = true } - compile("org.hibernate:hibernate-cglib-repack:2.1_3") { optional = true } - compile("org.hibernate:hibernate-annotations:3.4.0.GA") { optional = true } - compile("org.hibernate:hibernate-entitymanager:4.0.0.CR4") { optional = true } - compile("org.apache.openjpa:openjpa:1.1.0") { optional = true } - compile("org.eclipse.persistence:org.eclipse.persistence.core:1.0.1") { optional = true } - compile("org.eclipse.persistence:org.eclipse.persistence.jpa:1.0.1") { optional = true } - compile("toplink.essentials:toplink-essentials:2.0-41b") { optional = true } - compile("javax.jdo:jdo-api:3.0") { optional = true } - compile("org.apache.ibatis:ibatis-sqlmap:2.3.4.726") { optional = true } + compile("org.hibernate:com.springsource.org.hibernate:3.3.1.GA", optional) + compile("org.hibernate:hibernate-core:4.0.0.CR7", optional) + compile("org.hibernate:hibernate-cglib-repack:2.1_3", optional) + compile("org.hibernate:hibernate-annotations:3.4.0.GA", optional) + compile("org.hibernate:hibernate-entitymanager:4.0.0.CR4", optional) + compile("org.apache.openjpa:openjpa:1.1.0", optional) + compile("org.eclipse.persistence:org.eclipse.persistence.core:1.0.1", optional) + compile("org.eclipse.persistence:org.eclipse.persistence.jpa:1.0.1", optional) + compile("toplink.essentials:toplink-essentials:2.0-41b", optional) + compile("javax.jdo:jdo-api:3.0", optional) + compile("org.apache.ibatis:ibatis-sqlmap:2.3.4.726", optional) testCompile "javax.servlet:servlet-api:2.5" testCompile "org.slf4j:slf4j-jcl:1.5.3" testCompile "commons-dbcp:commons-dbcp:1.2.2" @@ -366,19 +371,21 @@ project('spring-webmvc') { compile project(":spring-web") compile project(":spring-orm") compile project(":spring-context-support") - compile("org.apache.tiles:tiles-api:2.1.2") { optional = true } - compile("org.apache.tiles:tiles-core:2.1.2") { optional = true } - compile("org.apache.tiles:tiles-jsp:2.1.2") { optional = true } - compile("org.apache.tiles:tiles-servlet:2.1.2") { optional = true } - compile("velocity-tools:velocity-tools-view:1.4") { optional = true } - compile("net.sourceforge.jexcelapi:jxl:2.6.3") { optional = true + compile("org.apache.tiles:tiles-api:2.1.2", optional) + compile("org.apache.tiles:tiles-core:2.1.2", optional) + compile("org.apache.tiles:tiles-jsp:2.1.2", optional) + compile("org.apache.tiles:tiles-servlet:2.1.2", optional) + compile("velocity-tools:velocity-tools-view:1.4", optional) + compile("net.sourceforge.jexcelapi:jxl:2.6.3") { dep -> + optional dep exclude group: 'log4j', module: 'log4j' } - compile("org.apache.poi:poi:3.0.2-FINAL") { optional = true + compile("org.apache.poi:poi:3.0.2-FINAL") { dep -> + optional dep exclude group: 'log4j', module: 'log4j' } - compile("javax.servlet:jstl:1.1.2") { provided = true } - compile("org.apache.tomcat:tomcat-servlet-api:7.0.8") { provided = true } // servlet-api 3.0 + compile("javax.servlet:jstl:1.1.2", provided) + compile("org.apache.tomcat:tomcat-servlet-api:7.0.8", provided) // servlet-api 3.0 testCompile("org.slf4j:slf4j-log4j12:${slf4jLog4jVersion}") { exclude group: 'log4j', module: 'log4j' } @@ -401,7 +408,7 @@ project('spring-webmvc') { project('spring-webmvc-portlet') { description = 'Spring Web Portlet' dependencies { - compile("javax.servlet:servlet-api:2.5") { provided = true } + compile("javax.servlet:servlet-api:2.5", provided) compile project(":spring-webmvc") } @@ -413,10 +420,10 @@ project('spring-test') { description = 'Spring TestContext Framework' dependencies { compile project(":spring-webmvc-portlet") - compile("javax.activation:activation:1.0") { provided = true } - compile("org.testng:testng:5.10:jdk15") { optional = true } - compile("junit:junit:4.9") { optional = true } - compile("javax.servlet:servlet-api:2.5") { provided = true } + compile("javax.activation:activation:1.0", provided) + compile("org.testng:testng:5.10:jdk15", optional) + compile("junit:junit:4.9", optional) + compile("javax.servlet:servlet-api:2.5", provided) testCompile "org.slf4j:slf4j-jcl:1.5.3" } } @@ -427,7 +434,7 @@ project('spring-struts') { compile project(":spring-webmvc") compile "struts:struts:1.2.9" compile "commons-beanutils:commons-beanutils:1.7.0" - compile("javax.servlet:servlet-api:2.5") { provided = true } + compile("javax.servlet:servlet-api:2.5", provided) testCompile project(":spring-test") } } diff --git a/publish-maven.gradle b/publish-maven.gradle new file mode 100644 index 0000000000..17a84f86ec --- /dev/null +++ b/publish-maven.gradle @@ -0,0 +1,60 @@ +apply plugin: 'maven' + +optionalDeps = [] +providedDeps = [] + +optional = { optionalDeps << it } +provided = { providedDeps << it } + +install { + repositories.mavenInstaller { + customizePom(pom, project) + } +} + +def customizePom(pom, gradleProject) { + pom.whenConfigured { generatedPom -> + // respect 'optional' and 'provided' dependencies + gradleProject.optionalDeps.each { dep -> + generatedPom.dependencies.find { it.artifactId == dep.name }?.optional = true + } + gradleProject.providedDeps.each { dep -> + generatedPom.dependencies.find { it.artifactId == dep.name }?.scope = 'provided' + } + + // eliminate test-scoped dependencies (no need in maven central poms) + generatedPom.dependencies.removeAll { dep -> + dep.scope == 'test' + } + + // add all items necessary for maven central publication + generatedPom.project { + name = gradleProject.description + description = gradleProject.description + url = 'https://github.com/SpringSource/spring-framework' + organization { + name = 'SpringSource' + url = 'http://springsource.org/spring-framework' + } + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + scm { + url = 'https://github.com/SpringSource/spring-framework' + connection = 'scm:git:git://github.com/SpringSource/spring-framework' + developerConnection = 'scm:git:git://github.com/SpringSource/spring-framework' + } + developers { + developer { + id = 'jhoeller' + name = 'Juergen Hoeller' + email = 'jhoeller@vmware.com' + } + } + } + } +}