From e5418a241029ca5f4d8afb3dd5e2aba5ee665273 Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 13 Jun 2023 16:22:40 -0700 Subject: [PATCH] Update local Spring Build Conventions Gradle Plugins (in buildSrc/) with latest, required infrastructure changes. Required changes include, but are not limited to: * Switching from spring-doc-resources:0.2.5 to spring-asciidoctor-backends used to build documewntation. * Resolving build dependencies from Maven Central. * Importing the Spring PropDeps Gradle Plugins locally. * Refactoring the local Spring Build Conventions Gradle Plugins with the latest, state-of-art build and infrastructure practices. --- buildSrc/build.gradle | 35 +- .../AbstractSpringJavaPlugin.groovy | 47 +-- .../gradle/convention/DocsPlugin.groovy | 13 +- .../convention/SpringMavenPlugin.groovy | 83 +++-- .../convention/SpringModulePlugin.groovy | 7 +- .../convention/SpringSampleBootPlugin.groovy | 15 +- .../convention/SpringSamplePlugin.groovy | 6 +- .../convention/SpringSampleWarPlugin.groovy | 14 +- .../propdeps/PropDepsEclipsePlugin.groovy | 45 +++ .../gradle/propdeps/PropDepsIdeaPlugin.groovy | 48 +++ .../propdeps/PropDepsMavenPlugin.groovy | 67 ++++ .../gradle/propdeps/PropDepsPlugin.groovy | 82 +++++ .../AsciidoctorConventionPlugin.java | 314 ++++++++++-------- .../gradle-plugins/gemfire-server.properties | 1 + 14 files changed, 555 insertions(+), 222 deletions(-) create mode 100644 buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsEclipsePlugin.groovy create mode 100644 buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsIdeaPlugin.groovy create mode 100644 buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsMavenPlugin.groovy create mode 100644 buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsPlugin.groovy create mode 100644 buildSrc/src/main/resources/META-INF/gradle-plugins/gemfire-server.properties diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 0579d28b..5ca840c7 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -12,13 +12,37 @@ group 'io.spring.gradle' sourceCompatibility = 1.8 +sourceSets { + main { + java { + srcDirs = [] + } + groovy { + srcDirs += [ "src/main/java" ] + } + } +} + repositories { - jcenter() - gradlePluginPortal() mavenCentral() + gradlePluginPortal() + jcenter() maven { url 'https://repo.spring.io/plugins-release/' } } +gradlePlugin { + plugins { + managementConfiguration { + id = "io.spring.convention.management-configuration" + implementationClass = "io.spring.gradle.convention.ManagementConfigurationPlugin" + } + propdeps { + id = "org.springframework.propdeps" + implementationClass = "io.spring.gradle.convention.propdeps.PropDepsPlugin" + } + } +} + configurations { implementation { exclude module: 'groovy-all' @@ -29,13 +53,14 @@ dependencies { implementation localGroovy() + runtimeOnly 'org.springframework.boot:spring-boot-loader-tools:2.7.12' + implementation 'com.github.ben-manes:gradle-versions-plugin:0.25.0' implementation 'gradle.plugin.org.gretty:gretty:3.0.1' implementation 'io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.21.1' - implementation 'io.spring.gradle:dependency-management-plugin:1.0.9.RELEASE' - implementation 'io.spring.gradle:propdeps-plugin:0.0.10.RELEASE' + implementation 'io.spring.gradle:dependency-management-plugin:1.0.15.RELEASE' implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.15' - implementation 'io.spring.nohttp:nohttp-gradle:0.0.3.RELEASE' + implementation 'io.spring.nohttp:nohttp-gradle:0.0.11' implementation 'org.asciidoctor:asciidoctor-gradle-jvm:3.1.0' implementation 'org.asciidoctor:asciidoctor-gradle-jvm-pdf:3.1.0' implementation 'org.hidetake:gradle-ssh-plugin:2.10.1' diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy index 3c991dba..e4636ead 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy @@ -13,47 +13,52 @@ * License for the specific language governing permissions and limitations under * the License. */ +package io.spring.gradle.convention -package io.spring.gradle.convention; - -import io.spring.gradle.propdeps.PropDepsMavenPlugin; -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.plugins.GroovyPlugin; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.plugins.MavenPlugin; -import org.gradle.api.plugins.PluginManager; -import org.gradle.internal.impldep.org.apache.maven.Maven; -import org.gradle.plugins.ide.eclipse.EclipseWtpPlugin; -import org.gradle.plugins.ide.idea.IdeaPlugin; -import io.spring.gradle.propdeps.PropDepsEclipsePlugin; -import io.spring.gradle.propdeps.PropDepsIdeaPlugin; -import io.spring.gradle.propdeps.PropDepsPlugin; +import io.spring.gradle.propdeps.PropDepsEclipsePlugin +import io.spring.gradle.propdeps.PropDepsIdeaPlugin +import io.spring.gradle.propdeps.PropDepsMavenPlugin +import io.spring.gradle.propdeps.PropDepsPlugin +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.GroovyPlugin +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.MavenPlugin +import org.gradle.api.plugins.PluginManager +import org.gradle.plugins.ide.eclipse.EclipseWtpPlugin +import org.gradle.plugins.ide.idea.IdeaPlugin /** * @author Rob Winch */ -public abstract class AbstractSpringJavaPlugin implements Plugin { +abstract class AbstractSpringJavaPlugin implements Plugin { @Override - public final void apply(Project project) { + final void apply(Project project) { + PluginManager pluginManager = project.getPluginManager(); + pluginManager.apply(JavaPlugin.class); pluginManager.apply(ManagementConfigurationPlugin.class); + if (project.file("src/main/groovy").exists() || project.file("src/test/groovy").exists() || project.file("src/integration-test/groovy").exists()) { pluginManager.apply(GroovyPlugin.class); } + pluginManager.apply("io.spring.convention.repository"); + pluginManager.apply(EclipseWtpPlugin); pluginManager.apply(IdeaPlugin); pluginManager.apply(PropDepsPlugin); pluginManager.apply(PropDepsEclipsePlugin); pluginManager.apply(PropDepsIdeaPlugin); + project.getPlugins().withType(MavenPlugin) { pluginManager.apply(PropDepsMavenPlugin); } + pluginManager.apply("io.spring.convention.tests-configuration"); pluginManager.apply("io.spring.convention.integration-test"); pluginManager.apply("io.spring.convention.springdependencymangement"); @@ -68,21 +73,25 @@ public abstract class AbstractSpringJavaPlugin implements Plugin { project.jar { manifest.attributes["Created-By"] = - "${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})" + "${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})" manifest.attributes["Implementation-Title"] = project.name manifest.attributes["Implementation-Version"] = project.version manifest.attributes["Automatic-Module-Name"] = project.name.replace('-', '.') } + additionalPlugins(project); } private void copyPropertyFromRootProjectTo(String propertyName, Project project) { + Project rootProject = project.getRootProject(); Object property = rootProject.findProperty(propertyName); - if(property != null) { + + if (property != null) { project.setProperty(propertyName, property); } } protected abstract void additionalPlugins(Project project); + } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy index 0eb1ad42..23c06af2 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy @@ -1,25 +1,22 @@ package io.spring.gradle.convention import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask -import org.gradle.api.Action import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.DependencySet import org.gradle.api.plugins.PluginManager -import org.gradle.api.tasks.Sync import org.gradle.api.tasks.bundling.Zip /** * Aggregates asciidoc, javadoc, and deploying of the docs into a single plugin */ -public class DocsPlugin implements Plugin { +class DocsPlugin implements Plugin { @Override public void apply(Project project) { PluginManager pluginManager = project.getPluginManager(); + pluginManager.apply("org.asciidoctor.jvm.convert"); pluginManager.apply("org.asciidoctor.jvm.pdf"); pluginManager.apply(AsciidoctorConventionPlugin); @@ -38,7 +35,6 @@ public class DocsPlugin implements Plugin { } } - Task docsZip = project.tasks.create('docsZip', Zip) { dependsOn 'api', 'asciidoctor' group = 'Distribution' @@ -51,15 +47,19 @@ public class DocsPlugin implements Plugin { into 'reference/html5' include '**' } + from(project.tasks.asciidoctorPdf.outputs) { into 'reference/pdf' include '**' rename "index.pdf", pdfFilename } + from(project.tasks.api.outputs) { into 'api' } + into 'docs' + duplicatesStrategy 'exclude' } @@ -68,6 +68,7 @@ public class DocsPlugin implements Plugin { description 'An aggregator task to generate all the documentation' dependsOn docsZip } + project.tasks.assemble.dependsOn docs } } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringMavenPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringMavenPlugin.groovy index a8d9be27..f6e7fe50 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringMavenPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringMavenPlugin.groovy @@ -2,8 +2,6 @@ package io.spring.gradle.convention import io.spring.gradle.dependencymanagement.DependencyManagementPlugin import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension -import io.spring.gradle.dependencymanagement.dsl.GeneratedPomCustomizationHandler -import org.gradle.api.Action import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.XmlProvider @@ -22,24 +20,32 @@ import org.gradle.plugins.signing.SigningPlugin import org.slf4j.Logger import org.slf4j.LoggerFactory -public class SpringMavenPlugin implements Plugin { +class SpringMavenPlugin implements Plugin { + private static final String ARCHIVES = "archives"; + Logger logger = LoggerFactory.getLogger(getClass()); @Override - public void apply(Project project) { + void apply(Project project) { + project.getPluginManager().apply(JavaPlugin.class); project.getPluginManager().apply(MavenPlugin.class); project.getPluginManager().apply(SigningPlugin.class); Javadoc javadoc = (Javadoc) project.getTasks().findByPath("javadoc"); + Jar javadocJar = project.getTasks().create("javadocJar", Jar.class); + javadocJar.setClassifier("javadoc"); javadocJar.from(javadoc); JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class); + SourceSet mainSourceSet = java.getSourceSets().getByName("main"); + Jar sourcesJar = project.getTasks().create("sourcesJar", Jar.class); + sourcesJar.setClassifier("sources"); sourcesJar.from(mainSourceSet.getAllSource()); @@ -51,6 +57,7 @@ public class SpringMavenPlugin implements Plugin { configurePom(project, pom) } } + project.uploadArchives { repositories.mavenDeployer { configurePom(project, pom) @@ -62,6 +69,7 @@ public class SpringMavenPlugin implements Plugin { } def hasSigningKey = project.hasProperty("signing.keyId") || project.findProperty("signingKey") + if(hasSigningKey && Utils.isRelease(project)) { sign(project) } @@ -70,7 +78,9 @@ public class SpringMavenPlugin implements Plugin { } private void inlineDependencyManagement(Project project) { - final DependencyManagementExtension dependencyManagement = project.getExtensions().findByType(DependencyManagementExtension.class); + + DependencyManagementExtension dependencyManagement = project.getExtensions().findByType(DependencyManagementExtension.class); + dependencyManagement.generatedPomCustomization( { handler -> handler.setEnabled(false) }); project.install { @@ -78,6 +88,7 @@ public class SpringMavenPlugin implements Plugin { configurePomForInlineDependencies(project, pom) } } + project.uploadArchives { repositories.mavenDeployer { configurePomForInlineDependencies(project, pom) @@ -86,6 +97,7 @@ public class SpringMavenPlugin implements Plugin { } private void configurePomForInlineDependencies(Project project, MavenPom pom) { + pom.withXml { XmlProvider xml -> project.plugins.withType(JavaBasePlugin) { def dependencies = xml.asNode()?.dependencies?.dependency @@ -118,6 +130,7 @@ public class SpringMavenPlugin implements Plugin { } private void sign(Project project) { + project.install { repositories { mavenDeployer { @@ -136,19 +149,23 @@ public class SpringMavenPlugin implements Plugin { project.signing { required { project.gradle.taskGraph.hasTask("uploadArchives") } + def signingKeyId = project.findProperty("signingKeyId") def signingKey = project.findProperty("signingKey") def signingPassword = project.findProperty("signingPassword") + if (signingKeyId) { useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) } else if (signingKey) { useInMemoryPgpKeys(signingKey, signingPassword) } + sign project.configurations.archives } } private static void configurePom(Project project, MavenPom pom) { + pom.whenConfigured { p -> p.dependencies = p.dependencies.sort { dep -> "$dep.scope:$dep.optional:$dep.groupId:$dep.artifactId" @@ -156,50 +173,52 @@ public class SpringMavenPlugin implements Plugin { } pom.project { - boolean isWar = project.hasProperty("war"); - String projectVersion = String.valueOf(project.getVersion()); - String projectName = Utils.getProjectName(project); - if(isWar) { + boolean isWar = project.hasProperty("war"); + + String projectName = Utils.getProjectName(project); + String projectVersion = String.valueOf(project.getVersion()); + + if (isWar) { packaging = "war" } + name = project.name description = project.name - url = 'https://spring.io/spring-security' - organization { - name = 'spring.io' - url = 'https://spring.io/' - } + url = 'https://spring.io/projects/spring-boot' + licenses { license { name 'The Apache Software License, Version 2.0' url 'https://www.apache.org/licenses/LICENSE-2.0.txt' distribution 'repo' - } - } - scm { - url = 'https://github.com/spring-projects/spring-security' - connection = 'scm:git:git://github.com/spring-projects/spring-security' - developerConnection = 'scm:git:git://github.com/spring-projects/spring-security' - } - developers { - developer { - id = 'rwinch' - name = 'Rob Winch' - email = 'rwinch@pivotal.io' - } - developer { - id = 'jgrandja' - name = 'Joe Grandja' - email = 'jgrandja@pivotal.io' } } - if(isWar) { + organization { + name = 'spring.io' + url = 'https://spring.io/' + } + + developers { + developer { + id = 'jblum' + name = 'John Blum' + } + } + + scm { + url = 'https://github.com/spring-projects/spring-boot-data-geode' + connection = 'scm:git:git://github.com/spring-projects/spring-boot-data-geode' + developerConnection = 'scm:git:git://github.com/spring-projects/spring-boot-data-geode' + } + + if (isWar) { properties { 'm2eclipse.wtp.contextRoot' '/' } } + if (Utils.isSnapshot(project)) { repositories { repository { diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy index 5c509e91..a544dc03 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention; import org.gradle.api.Project @@ -28,17 +27,22 @@ class SpringModulePlugin extends AbstractSpringJavaPlugin { @Override void additionalPlugins(Project project) { + PluginManager pluginManager = project.getPluginManager(); + pluginManager.apply(JavaLibraryPlugin.class) pluginManager.apply(MavenPlugin.class); + pluginManager.apply("io.spring.convention.maven"); pluginManager.apply("io.spring.convention.artifactory"); pluginManager.apply("io.spring.convention.jacoco"); pluginManager.apply("io.spring.convention.merge"); def deployArtifacts = project.task("deployArtifacts") + deployArtifacts.group = 'Deploy tasks' deployArtifacts.description = "Deploys the artifacts to either Artifactory or Maven Central" + if (Utils.isRelease(project)) { deployArtifacts.dependsOn project.tasks.uploadArchives } @@ -46,5 +50,4 @@ class SpringModulePlugin extends AbstractSpringJavaPlugin { deployArtifacts.dependsOn project.tasks.artifactoryPublish } } - } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy index 2d7ee845..d80aef07 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy @@ -13,22 +13,19 @@ * License for the specific language governing permissions and limitations under * the License. */ +package io.spring.gradle.convention -package io.spring.gradle.convention; - -import org.gradle.api.Project; -import org.gradle.api.plugins.PluginManager; -import org.gradle.api.plugins.WarPlugin -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.tasks.testing.Test +import org.gradle.api.Project +import org.gradle.api.plugins.PluginManager /** * @author Rob Winch */ -public class SpringSampleBootPlugin extends SpringSamplePlugin { +class SpringSampleBootPlugin extends SpringSamplePlugin { @Override - public void additionalPlugins(Project project) { + void additionalPlugins(Project project) { + super.additionalPlugins(project); PluginManager pluginManager = project.getPluginManager(); diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy index 37ae6cfb..3a065c7a 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention; import org.gradle.api.Project @@ -22,10 +21,11 @@ import org.sonarqube.gradle.SonarQubePlugin; /** * @author Rob Winch */ -public class SpringSamplePlugin extends AbstractSpringJavaPlugin { +class SpringSamplePlugin extends AbstractSpringJavaPlugin { @Override - public void additionalPlugins(Project project) { + void additionalPlugins(Project project) { + project.plugins.withType(SonarQubePlugin) { project.sonarqube.skipProject = true } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy index 59bb1545..72185b99 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention import org.gradle.api.Project @@ -24,10 +23,11 @@ import org.gradle.api.tasks.testing.Test /** * @author Rob Winch */ -public class SpringSampleWarPlugin extends SpringSamplePlugin { +class SpringSampleWarPlugin extends SpringSamplePlugin { @Override - public void additionalPlugins(Project project) { + void additionalPlugins(Project project) { + super.additionalPlugins(project); PluginManager pluginManager = project.getPluginManager(); @@ -51,6 +51,7 @@ public class SpringSampleWarPlugin extends SpringSamplePlugin { } } } + project.tasks.withType(org.akhikhl.gretty.AppBeforeIntegrationTestTask).all { task -> task.dependsOn prepareAppServerForIntegrationTests } @@ -63,6 +64,7 @@ public class SpringSampleWarPlugin extends SpringSamplePlugin { } def applyForIntegrationTest(Project project, Task integrationTest) { + project.gretty.integrationTestTask = integrationTest.name integrationTest.doFirst { @@ -89,9 +91,9 @@ public class SpringSampleWarPlugin extends SpringSamplePlugin { } def getRandomPort() { - ServerSocket ss = new ServerSocket(0) - int port = ss.localPort - ss.close() + ServerSocket serverSocket = new ServerSocket(0) + int port = serverSocket.localPort + serverSocket.close() return port } } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsEclipsePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsEclipsePlugin.groovy new file mode 100644 index 00000000..1fb8a3fe --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsEclipsePlugin.groovy @@ -0,0 +1,45 @@ +/* + * Copyright 2017-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package io.spring.gradle.propdeps + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.plugins.ide.eclipse.EclipsePlugin + +/** + * Gradle {@link Plugin} to allow {@literal optional} and {@literal provided} dependency configurations + * to work with the standard Gradle {@link EclipsePlugin}. + * + * @author Phillip Webb + * @author John Blum + * @see org.gradle.api.Plugin + * @see org.gradle.api.Project + * @see org.gradle.plugins.ide.eclipse.EclipsePlugin + */ +class PropDepsEclipsePlugin implements Plugin { + + void apply(Project project) { + + project.plugins.apply(PropDepsPlugin) + project.plugins.apply(EclipsePlugin) + + project.eclipse { + classpath { + plusConfigurations += [ project.configurations.provided, project.configurations.optional ] + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsIdeaPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsIdeaPlugin.groovy new file mode 100644 index 00000000..2435b1f8 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsIdeaPlugin.groovy @@ -0,0 +1,48 @@ +/* + * Copyright 2017-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package io.spring.gradle.propdeps + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.plugins.ide.idea.IdeaPlugin + +/** + * Gradle {@link Plugin} to allow {@literal optional} and {@literal provided} dependency configurations + * to work with the standard Gradle {@link IdeaPlugin}. + * + * @author Phillip Webb + * @author Brian Clozel + * @author John Blum + * @see org.gradle.api.Plugin + * @see org.gradle.api.Project + * @see org.gradle.plugins.ide.idea.IdeaPlugin + * @link https://youtrack.jetbrains.com/issue/IDEA-107046 + * @link https://youtrack.jetbrains.com/issue/IDEA-117668 + */ +class PropDepsIdeaPlugin implements Plugin { + + void apply(Project project) { + + project.plugins.apply(PropDepsPlugin) + project.plugins.apply(IdeaPlugin) + project.idea.module { + // IntelliJ IDEA internally deals with 4 scopes : COMPILE, TEST, PROVIDED, RUNTIME + // but only PROVIDED seems to be picked up + scopes.PROVIDED.plus += [ project.configurations.provided ] + scopes.PROVIDED.plus += [ project.configurations.optional ] + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsMavenPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsMavenPlugin.groovy new file mode 100644 index 00000000..067e5212 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsMavenPlugin.groovy @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.spring.gradle.propdeps + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer +import org.gradle.api.artifacts.maven.MavenPom +import org.gradle.api.artifacts.maven.PomFilterContainer +import org.gradle.api.plugins.MavenPlugin +import org.gradle.api.tasks.Upload + +/** + * Plugin to allow optional and provided dependency configurations to work with + * the standard gradle 'maven' plugin + * + * @author Phillip Webb + * @author John Blum + */ +class PropDepsMavenPlugin implements Plugin { + + void apply(Project project) { + + project.plugins.apply(PropDepsPlugin) + project.plugins.apply(MavenPlugin) + + Conf2ScopeMappingContainer scopeMappings = project.conf2ScopeMappings + + scopeMappings.addMapping(MavenPlugin.COMPILE_PRIORITY + 1, + project.configurations.getByName("provided"), Conf2ScopeMappingContainer.PROVIDED) + + // Add a temporary new optional scope + scopeMappings.addMapping(MavenPlugin.COMPILE_PRIORITY + 2, + project.configurations.getByName("optional"), "optional") + + // Add a hook to replace the optional scope + project.afterEvaluate { + project.tasks.withType(Upload).each { applyToUploadTask(project, it) } + } + } + + private void applyToUploadTask(Project project, Upload upload) { + upload.repositories.withType(PomFilterContainer).each{ applyToPom(project, it) } + } + + private void applyToPom(Project project, PomFilterContainer pomContainer) { + pomContainer.pom.whenConfigured { MavenPom pom -> + pom.dependencies.findAll{ it.scope == "optional" }.each { + it.scope = "compile" + it.optional = true + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsPlugin.groovy new file mode 100644 index 00000000..781e4e69 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/propdeps/PropDepsPlugin.groovy @@ -0,0 +1,82 @@ +/* + * Copyright 2017-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package io.spring.gradle.propdeps + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.plugins.JavaLibraryPlugin +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.javadoc.Javadoc + +/** + * Gradle {@link Plugin} to allow {@literal optional} and {@literal provided} dependency configurations. + * + * As stated in the Maven documentation, {@literal provided} scope {@literal "is only available on the compilation + * and test classpath, and is not transitive"}. + * + * This {@link Plugin} creates two new configurations, and each one: + * + *
    + *
  • is a parent of the compile configuration
  • + *
  • is not visible, not transitive
  • + *
  • all dependencies are excluded from the default configuration
  • + *
+ * + * @author Phillip Webb + * @author Brian Clozel + * @author Rob Winch + * @author John Blum + * + * @see org.gradle.api.Plugin + * @see org.gradle.api.Project + * @see PropDepsEclipsePlugin + * @see PropDepsIdeaPlugin + * @see Maven documentation + * @see Gradle configurations + */ +class PropDepsPlugin implements Plugin { + + void apply(Project project) { + + project.plugins.apply(JavaPlugin) + + Configuration optional = addConfiguration(project, "optional") + Configuration provided = addConfiguration(project, "provided") + + Javadoc javadoc = project.tasks.getByName(JavaPlugin.JAVADOC_TASK_NAME) + + javadoc.classpath = javadoc.classpath + provided + optional + } + + private Configuration addConfiguration(Project project, String name) { + + Configuration configuration = project.configurations.create(name) + + configuration.extendsFrom(project.configurations.implementation) + + project.plugins.withType(JavaLibraryPlugin, { + configuration.extendsFrom(project.configurations.api) + }) + + project.sourceSets.all { + compileClasspath += configuration + runtimeClasspath += configuration + } + + return configuration + } +} diff --git a/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java b/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java index 548dfbca..f8c54117 100644 --- a/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java +++ b/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java @@ -1,38 +1,21 @@ /* - * Copyright 2019-2020 the original author or authors. + * Copyright 2017-present the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - package io.spring.gradle.convention; -import org.asciidoctor.gradle.base.AsciidoctorAttributeProvider; -import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask; -import org.asciidoctor.gradle.jvm.AsciidoctorJExtension; -import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin; -import org.asciidoctor.gradle.jvm.AsciidoctorTask; -import org.gradle.api.Action; -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.DependencySet; -import org.gradle.api.artifacts.dsl.RepositoryHandler; -import org.gradle.api.file.CopySpec; -import org.gradle.api.file.FileTree; -import org.gradle.api.tasks.Sync; - import java.io.File; -import java.net.URI; import java.time.LocalDate; import java.util.ArrayList; import java.util.Collections; @@ -40,169 +23,220 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; -import java.util.function.Consumer; + +import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask; +import org.asciidoctor.gradle.jvm.AsciidoctorJExtension; +import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin; +import org.asciidoctor.gradle.jvm.AsciidoctorTask; +import org.gradle.api.JavaVersion; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.file.DuplicatesStrategy; +import org.gradle.api.file.FileTree; +import org.gradle.api.tasks.Sync; /** - * Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. When - * the plugin is applied: + * Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. + *

+ * When the plugin is applied: * *

    *
  • All warnings are made fatal. - *
  • A task is created to resolve and unzip our documentation resources (CSS and - * Javascript). + *
  • A task is created to resolve and unzip our documentation resources (CSS and Javascript). *
  • For each {@link AsciidoctorTask} (HTML only): *
      *
    • A configuration named asciidoctorExtensions is ued to add the - * block - * switch extension + * block switch extension *
    • {@code doctype} {@link AsciidoctorTask#options(Map) option} is configured. - *
    • {@link AsciidoctorTask#attributes(Map) Attributes} are configured for syntax - * highlighting, CSS styling, docinfo, etc. + *
    • {@link AsciidoctorTask#attributes(Map) Attributes} are configured for syntax highlighting, CSS styling, + * docinfo, etc. *
    *
  • For each {@link AbstractAsciidoctorTask} (HTML and PDF): *
      - *
    • {@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable - * warnings for references to missing attributes, the year is added as @{code today-year}, - * etc - *
    • {@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()} - * is enabled. + *
    • {@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable warnings for references to + * missing attributes, the year is added as @{code today-year}, etc + *
    • {@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()} is enabled. *
    *
* * @author Andy Wilkinson * @author Rob Winch + * @author John Blum */ public class AsciidoctorConventionPlugin implements Plugin { + private static final String ASCIIDOCTORJ_VERSION = "2.4.3"; + private static final String SPRING_ASCIIDOCTOR_BACKENDS_VERSION = "0.0.5"; + private static final String SPRING_DOC_RESOURCES_VERSION = "0.2.5"; + + private static final String SPRING_ASCIIDOCTOR_BACKENDS_DEPENDENCY = + String.format("io.spring.asciidoctor.backends:spring-asciidoctor-backends:%s", + SPRING_ASCIIDOCTOR_BACKENDS_VERSION); + + @SuppressWarnings("unused") + private static final String SPRING_DOC_RESOURCES_DEPENDENCY = + String.format("io.spring.docresources:spring-doc-resources:%s", SPRING_DOC_RESOURCES_VERSION); + + @Override public void apply(Project project) { - project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> { - createDefaultAsciidoctorRepository(project); + + project.getPlugins().withType(AsciidoctorJPlugin.class, asciidoctorPlugin -> { + + setAsciidoctorJVersion(project); makeAllWarningsFatal(project); + createAsciidoctorExtensionsConfiguration(project); + Sync unzipResources = createUnzipDocumentationResourcesTask(project); - project.getTasks().withType(AbstractAsciidoctorTask.class, (asciidoctorTask) -> { + + project.getTasks().withType(AbstractAsciidoctorTask.class, asciidoctorTask -> { + asciidoctorTask.dependsOn(unzipResources); - configureExtensions(project, asciidoctorTask); - configureCommonAttributes(project, asciidoctorTask); - configureOptions(asciidoctorTask); - asciidoctorTask.baseDirFollowsSourceDir(); - asciidoctorTask.useIntermediateWorkDir(); - asciidoctorTask.resources(new Action() { - @Override - public void execute(CopySpec resourcesSpec) { - resourcesSpec.from(unzipResources); - resourcesSpec.from(asciidoctorTask.getSourceDir(), new Action() { - @Override - public void execute(CopySpec resourcesSrcDirSpec) { - // https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/523 - // For now copy the entire sourceDir over so that include files are - // available in the intermediateWorkDir - // resourcesSrcDirSpec.include("images/**"); - } - }); - } + configureAsciidoctorTask(project, asciidoctorTask); + + asciidoctorTask.resources(resourcesSpec -> { + resourcesSpec.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE); + resourcesSpec.from(unzipResources); + resourcesSpec.from(asciidoctorTask.getSourceDir(), resourcesSrcDirSpec -> { + // https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/523 + // For now copy the entire sourceDir over so that include files are + // available in the intermediateWorkDir + // resourcesSrcDirSpec.include("images/**"); + }); }); - if (asciidoctorTask instanceof AsciidoctorTask) { - configureHtmlOnlyAttributes(project, asciidoctorTask); - } }); }); } - private void createDefaultAsciidoctorRepository(Project project) { - project.getGradle().afterProject(new Action() { - @Override - public void execute(Project project) { - RepositoryHandler repositories = project.getRepositories(); - if (repositories.isEmpty()) { - repositories.mavenCentral(); - repositories.maven(repo -> { - repo.setUrl(URI.create("https://repo.spring.io/release")); - }); - } - } - }); + private void setAsciidoctorJVersion(Project project) { + project.getExtensions().getByType(AsciidoctorJExtension.class).setVersion(ASCIIDOCTORJ_VERSION); } private void makeAllWarningsFatal(Project project) { project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*"); } - private void configureExtensions(Project project, AbstractAsciidoctorTask asciidoctorTask) { - Configuration extensionsConfiguration = project.getConfigurations().maybeCreate("asciidoctorExtensions"); - extensionsConfiguration.defaultDependencies(new Action() { - @Override - public void execute(DependencySet dependencies) { - dependencies.add(project.getDependencies().create("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.4.2.RELEASE")); - } + private void createAsciidoctorExtensionsConfiguration(Project project) { + + project.getConfigurations().create("asciidoctorExtensions", configuration -> { + + project.getConfigurations() + .matching(it -> "dependencyManagement".equals(it.getName())) + .all(configuration::extendsFrom); + + configuration.getDependencies() + .add(project.getDependencies().create(SPRING_ASCIIDOCTOR_BACKENDS_DEPENDENCY)); + + // TODO: Why is the asiidoctorj-pdf dependency needed? + configuration.getDependencies() + .add(project.getDependencies().create("org.asciidoctor:asciidoctorj-pdf:1.5.3")); }); - asciidoctorTask.configurations(extensionsConfiguration); } + /** + * Requests the base Spring Documentation Resources from {@literal Maven Central} and uses it to format + * and render documentation. + * + * @param project {@literal this} Gradle {@link Project}. + * @return a {@link Sync} task that copies Spring Documentation Resources to the build directory + * used to generate documentation. + * @see org.gradle.api.tasks.Sync + * @see org.gradle.api.Project + */ + @SuppressWarnings("all") private Sync createUnzipDocumentationResourcesTask(Project project) { - Configuration documentationResources = project.getConfigurations().maybeCreate("documentationResources"); + + Configuration documentationResources = project.getConfigurations().create("documentationResources"); + documentationResources.getDependencies() - .add(project.getDependencies().create("io.spring.docresources:spring-doc-resources:0.2.5")); - Sync unzipResources = project.getTasks().create("unzipDocumentationResources", - Sync.class, new Action() { - @Override - public void execute(Sync sync) { - sync.dependsOn(documentationResources); - sync.from(new Callable>() { - @Override - public List call() throws Exception { - List result = new ArrayList<>(); - documentationResources.getAsFileTree().forEach(new Consumer() { - @Override - public void accept(File file) { - result.add(project.zipTree(file)); - } - }); - return result; - } - }); - File destination = new File(project.getBuildDir(), "docs/resources"); - sync.into(project.relativePath(destination)); - } + .add(project.getDependencies().create(SPRING_ASCIIDOCTOR_BACKENDS_DEPENDENCY)); + + Sync unzipResources = project.getTasks().create("unzipDocumentationResources", Sync.class, sync -> { + + sync.dependsOn(documentationResources); + + Callable> source = () -> { + List result = new ArrayList<>(); + documentationResources.getAsFileTree().forEach(file -> result.add(project.zipTree(file))); + return result; + }; + + sync.from(source); + + File destination = new File(project.getBuildDir(), "docs/resources"); + + sync.into(project.relativePath(destination)); + }); + return unzipResources; } + private void configureAsciidoctorTask(Project project, AbstractAsciidoctorTask asciidoctorTask) { + + asciidoctorTask.baseDirFollowsSourceDir(); + asciidoctorTask.configurations("asciidoctorExtensions"); + //asciidoctorTask.useIntermediateWorkDir(); + + configureAttributes(project, asciidoctorTask); + configureForkOptions(asciidoctorTask); + configureOptions(asciidoctorTask); + + if (asciidoctorTask instanceof AsciidoctorTask) { + boolean pdf = asciidoctorTask.getName().toLowerCase().contains("pdf"); + String backend = pdf ? "spring-pdf" : "spring-html"; + ((AsciidoctorTask) asciidoctorTask).outputOptions((outputOptions) -> outputOptions.backends(backend)); + configureHtmlOnlyAttributes(asciidoctorTask); + } + } + + private void configureAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { + + Map attributes = new HashMap<>(); + + attributes.put("attribute-missing", "warn"); + attributes.put("docinfo", "shared"); + attributes.put("idprefix", ""); + attributes.put("idseparator", "-"); + attributes.put("sectanchors", ""); + attributes.put("sectnums", ""); + attributes.put("today-year", LocalDate.now().getYear()); + + Object version = project.getVersion(); + + if (version != null && !Project.DEFAULT_VERSION.equals(version)) { + attributes.put("revnumber", version); + } + + asciidoctorTask.attributes(attributes); + } + + private void configureForkOptions(AbstractAsciidoctorTask asciidoctorTask) { + if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_16)) { + asciidoctorTask.forkOptions(options -> options.jvmArgs( + "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", + "--add-opens", "java.base/java.io=ALL-UNNAMED") + ); + } + } + + private void configureHtmlOnlyAttributes(AbstractAsciidoctorTask asciidoctorTask) { + + Map attributes = new HashMap<>(); + + attributes.put("highlightjsdir", "js/highlight"); + attributes.put("highlightjs-theme", "github"); + attributes.put("source-highlighter", "highlight.js"); + attributes.put("icons", "font"); + attributes.put("imagesdir", "./images"); + attributes.put("linkcss", true); + attributes.put("stylesdir", "css/"); + //attributes.put("stylesheet", "spring.css"); + + asciidoctorTask.attributes(attributes); + } + private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) { asciidoctorTask.options(Collections.singletonMap("doctype", "book")); } - - private void configureHtmlOnlyAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { - Map attributes = new HashMap<>(); - attributes.put("source-highlighter", "highlight.js"); - attributes.put("highlightjsdir", "js/highlight"); - attributes.put("highlightjs-theme", "github"); - attributes.put("linkcss", true); - attributes.put("icons", "font"); - attributes.put("stylesheet", "css/spring.css"); - asciidoctorTask.getAttributeProviders().add(new AsciidoctorAttributeProvider() { - @Override - public Map getAttributes() { - Object version = project.getVersion(); - Map attrs = new HashMap<>(); - if (version != null && version.toString() != Project.DEFAULT_VERSION) { - attrs.put("revnumber", version); - } - return attrs; - } - }); - asciidoctorTask.attributes(attributes); - } - - private void configureCommonAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { - Map attributes = new HashMap<>(); - attributes.put("attribute-missing", "warn"); - attributes.put("icons", "font"); - attributes.put("idprefix", ""); - attributes.put("idseparator", "-"); - attributes.put("docinfo", "shared"); - attributes.put("sectanchors", ""); - attributes.put("sectnums", ""); - attributes.put("today-year", LocalDate.now().getYear()); - asciidoctorTask.attributes(attributes); - } } diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/gemfire-server.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/gemfire-server.properties new file mode 100644 index 00000000..bb6a3057 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/gemfire-server.properties @@ -0,0 +1 @@ +implementation-class=build.GemFireServerPlugin \ No newline at end of file