From c93ae250f9511fc10581c807e3929d8f86b261bb Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 22 Aug 2023 17:46:04 +0200 Subject: [PATCH] Stop publishing distribution zip artifact Prior to this commit, the Spring Framework build would publish several zip artifacts: * a "*-schema.zip" containing all the XSD schemas produced * a "*-docs.zip" containing the API docs * a "*-dist.zip" containing all of the above, plus module jars Since the reference docs are now produced by Antora in a separate process, the "*-docs.zip" does not contain the reference docs anymore. But it still contains the API docs which are automatically fetched from the artifact repository and published on the docs.spring.io website. This commit intends to update the current arrangement and optimize the build. First, the "*-dist.zip" is not published anymore, since it cannot be consumed anyway by the community: repo.spring.io does not distribute release artifacts publicly, developers are expected to get them from Maven Central. This arrangement is quite dated anyway and is not really useful in current application build setups. The generation of API docs is moved to a new "framework-api" module, separating it from the reference docs module ("framework-docs") which contains Java, Kotlin and Asciidoctor sources. This removes the custom javadoc aggregation task and instead uses a dedicated Gradle plugin. This change also adds a new `-PskipDocs` Gradle project property that skips entirely the documentation tasks (javadoc, kdocs) as well as the "distrbution" tasks managed in the framework-api module. This allows developers to publish locally a SNAPSHOT of Spring Framework without creating the entire documentation distribution. This is particularly useful for local testing. For example, `$ ./gradlew pTML -PskipDocs`. Closes gh-31049 --- .gitignore | 3 +- build.gradle | 5 +- buildSrc/build.gradle | 4 + .../build/dev/LocalDevelopmentPlugin.java | 49 ++++++ ci/pipeline.yml | 10 +- framework-api/framework-api.gradle | 129 ++++++++++++++ framework-docs/framework-docs.gradle | 159 +----------------- settings.gradle | 1 + 8 files changed, 191 insertions(+), 169 deletions(-) create mode 100644 buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java create mode 100644 framework-api/framework-api.gradle diff --git a/.gitignore b/.gitignore index 211080d88ab..14252754d45 100644 --- a/.gitignore +++ b/.gitignore @@ -21,8 +21,7 @@ derby.log /build buildSrc/build /spring-*/build -/framework-bom/build -/framework-docs/build +/framework-*/build /integration-tests/build /src/asciidoc/build spring-test/test-output/ diff --git a/build.gradle b/build.gradle index db847262c5e..bb357f0a6a9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'io.freefair.aspectj' version '8.0.1' apply false + id 'io.freefair.aspectj' version '8.2.2' apply false // kotlinVersion is managed in gradle.properties id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false id 'org.jetbrains.dokka' version '1.8.20' @@ -13,10 +13,11 @@ plugins { ext { moduleProjects = subprojects.findAll { it.name.startsWith("spring-") } - javaProjects = subprojects - project(":framework-bom") - project(":framework-platform") + javaProjects = subprojects.findAll { !it.name.startsWith("framework-") } } configure(allprojects) { project -> + apply plugin: "org.springframework.build.localdev" repositories { mavenCentral() maven { diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 1d0ee25bdf9..44280aa54f2 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -38,6 +38,10 @@ gradlePlugin { id = "org.springframework.build.conventions" implementationClass = "org.springframework.build.ConventionsPlugin" } + localDevPlugin { + id = "org.springframework.build.localdev" + implementationClass = "org.springframework.build.dev.LocalDevelopmentPlugin" + } optionalDependenciesPlugin { id = "org.springframework.build.optional-dependencies" implementationClass = "org.springframework.build.optional.OptionalDependenciesPlugin" diff --git a/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java b/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java new file mode 100644 index 00000000000..8c8a2fd4523 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2023 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 org.springframework.build.dev; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaBasePlugin; + +/** + * {@link Plugin} that skips documentation tasks when the {@code "-PskipDocs"} property is defined. + * + * @author Brian Clozel + */ +public class LocalDevelopmentPlugin implements Plugin { + + private static final String SKIP_DOCS_PROPERTY = "skipDocs"; + + @Override + public void apply(Project target) { + if (target.hasProperty(SKIP_DOCS_PROPERTY)) { + skipDocumentationTasks(target); + target.subprojects(this::skipDocumentationTasks); + } + } + + private void skipDocumentationTasks(Project project) { + project.afterEvaluate(p -> { + p.getTasks().matching(task -> { + return JavaBasePlugin.DOCUMENTATION_GROUP.equals(task.getGroup()) + || "distribution".equals(task.getGroup()); + }) + .forEach(task -> task.setEnabled(false)); + }); + } +} diff --git a/ci/pipeline.yml b/ci/pipeline.yml index aec368cff2d..2de5b054fd5 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -198,21 +198,17 @@ jobs: threads: 8 artifact_set: - include: - - "/**/framework-docs-*.zip" + - "/**/framework-api-*.zip" properties: "zip.name": "spring-framework" "zip.displayname": "Spring Framework" "zip.deployed": "false" - include: - - "/**/framework-docs-*-docs.zip" + - "/**/framework-api-*-api.zip" properties: "zip.type": "docs" - include: - - "/**/framework-docs-*-dist.zip" - properties: - "zip.type": "dist" - - include: - - "/**/framework-docs-*-schema.zip" + - "/**/framework-api-*-schema.zip" properties: "zip.type": "schema" get_params: diff --git a/framework-api/framework-api.gradle b/framework-api/framework-api.gradle new file mode 100644 index 00000000000..31b2a096c78 --- /dev/null +++ b/framework-api/framework-api.gradle @@ -0,0 +1,129 @@ +plugins { + id 'java-platform' + id 'io.freefair.aggregate-javadoc' version '8.2.2' +} + +description = "Spring Framework API Docs" + +apply from: "${rootDir}/gradle/publications.gradle" + +repositories { + maven { + url "https://repo.spring.io/release" + } +} + +configurations { + dependencyManagement { + canBeConsumed = false + canBeResolved = false + visible = false + } + matching { it.name.endsWith("Classpath") }.all { it.extendsFrom(dependencyManagement) } +} + + +dependencies { + dependencyManagement(enforcedPlatform(dependencies.project(path: ":framework-platform"))) + rootProject.subprojects.findAll { it.name.startsWith("spring-") }.each { moduleProject -> + javadoc moduleProject + } +} + +javadoc { + title = "${rootProject.description} ${version} API" + options { + encoding = "UTF-8" + memberLevel = JavadocMemberLevel.PROTECTED + author = true + header = rootProject.description + use = true + overview = "framework-docs/src/docs/api/overview.html" + splitIndex = true + links(rootProject.ext.javadocLinks) + addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint + addBooleanOption('Werror', true) // fail build on Javadoc warnings + } + maxMemory = "1024m" + destinationDir = file("$buildDir/docs/javadoc") + doFirst { + classpath += files( + // ensure the javadoc process can resolve types compiled from .aj sources + project(":spring-aspects").sourceSets.main.output + ) + } +} + +/** + * Produce KDoc for all Spring Framework modules in "build/docs/kdoc" + */ +rootProject.tasks.dokkaHtmlMultiModule.configure { + dependsOn { + tasks.named("javadoc") + } + moduleName.set("spring-framework") + outputDirectory.set(project.file("$buildDir/docs/kdoc")) +} + +/** + * Zip all Java docs (javadoc & kdoc) into a single archive + */ +tasks.register('docsZip', Zip) { + dependsOn = ['javadoc', rootProject.tasks.dokkaHtmlMultiModule] + group = "distribution" + description = "Builds -${archiveClassifier} archive containing api and reference " + + "for deployment at https://docs.spring.io/spring-framework/docs/." + + archiveBaseName.set("spring-framework") + archiveClassifier.set("docs") + from("src/dist") { + include "changelog.txt" + } + from(javadoc) { + into "javadoc-api" + } + from(rootProject.tasks.dokkaHtmlMultiModule.outputDirectory) { + into "kdoc-api" + } +} + +/** + * Zip all Spring Framework schemas into a single archive + */ +tasks.register('schemaZip', Zip) { + group = "distribution" + archiveBaseName.set("spring-framework") + archiveClassifier.set("schema") + description = "Builds -${archiveClassifier} archive containing all " + + "XSDs for deployment at https://springframework.org/schema." + duplicatesStrategy DuplicatesStrategy.EXCLUDE + moduleProjects.each { module -> + def Properties schemas = new Properties(); + + module.sourceSets.main.resources.find { + (it.path.endsWith("META-INF/spring.schemas") || it.path.endsWith("META-INF\\spring.schemas")) + }?.withInputStream { schemas.load(it) } + + for (def key : schemas.keySet()) { + def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1') + assert shortName != key + File xsdFile = module.sourceSets.main.resources.find { + (it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/', '\\\\'))) + } + assert xsdFile != null + into(shortName) { + from xsdFile.path + } + } + } +} + +publishing { + group "distribution" + publications { + mavenJava(MavenPublication) { + artifact docsZip + artifact schemaZip + } + } +} \ No newline at end of file diff --git a/framework-docs/framework-docs.gradle b/framework-docs/framework-docs.gradle index f2b21206d99..576f7709ade 100644 --- a/framework-docs/framework-docs.gradle +++ b/framework-docs/framework-docs.gradle @@ -41,7 +41,7 @@ tasks.named("generateAntoraYml") { } ) } -tasks.create("generateAntoraResources") { +tasks.register("generateAntoraResources") { dependsOn 'generateAntoraYml' } @@ -68,161 +68,4 @@ dependencies { implementation(project(":spring-core-test")) implementation("org.assertj:assertj-core") -} - -/** - * Produce Javadoc for all Spring Framework modules in "build/docs/javadoc" - */ -task api(type: Javadoc) { - group = "Documentation" - description = "Generates aggregated Javadoc API documentation." - title = "${rootProject.description} ${version} API" - - dependsOn { - moduleProjects.collect { - it.tasks.getByName("jar") - } - } - doFirst { - classpath = files( - // ensure the javadoc process can resolve types compiled from .aj sources - project(":spring-aspects").sourceSets.main.output - ) - classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath }) - } - - options { - encoding = "UTF-8" - memberLevel = JavadocMemberLevel.PROTECTED - author = true - header = rootProject.description - use = true - overview = "framework-docs/src/docs/api/overview.html" - splitIndex = true - links(project.ext.javadocLinks) - addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint - addBooleanOption('Werror', true) // fail build on Javadoc warnings - } - source moduleProjects.collect { project -> - project.sourceSets.main.allJava - } - maxMemory = "1024m" - destinationDir = file("$buildDir/docs/javadoc") -} - -/** - * Produce KDoc for all Spring Framework modules in "build/docs/kdoc" - */ -rootProject.tasks.dokkaHtmlMultiModule.configure { - dependsOn { - tasks.getByName("api") - } - moduleName.set("spring-framework") - outputDirectory.set(project.file("$buildDir/docs/kdoc")) -} - -/** - * Zip all Java docs (javadoc & kdoc) into a single archive - */ -task docsZip(type: Zip, dependsOn: ['api', rootProject.tasks.dokkaHtmlMultiModule]) { - group = "Distribution" - description = "Builds -${archiveClassifier} archive containing api and reference " + - "for deployment at https://docs.spring.io/spring-framework/docs/." - - archiveBaseName.set("spring-framework") - archiveClassifier.set("docs") - from("src/dist") { - include "changelog.txt" - } - from (api) { - into "javadoc-api" - } - from (rootProject.tasks.dokkaHtmlMultiModule.outputDirectory) { - into "kdoc-api" - } -} - -/** - * Zip all Spring Framework schemas into a single archive - */ -task schemaZip(type: Zip) { - group = "Distribution" - archiveBaseName.set("spring-framework") - archiveClassifier.set("schema") - description = "Builds -${archiveClassifier} archive containing all " + - "XSDs for deployment at https://springframework.org/schema." - duplicatesStrategy DuplicatesStrategy.EXCLUDE - moduleProjects.each { module -> - def Properties schemas = new Properties(); - - module.sourceSets.main.resources.find { - (it.path.endsWith("META-INF/spring.schemas") || it.path.endsWith("META-INF\\spring.schemas")) - }?.withInputStream { schemas.load(it) } - - for (def key : schemas.keySet()) { - def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1') - assert shortName != key - File xsdFile = module.sourceSets.main.resources.find { - (it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/','\\\\'))) - } - assert xsdFile != null - into (shortName) { - from xsdFile.path - } - } - } -} - -/** - * Create a distribution zip with everything: - * docs, schemas, jars, source jars, javadoc jars - */ -task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) { - group = "Distribution" - archiveBaseName.set("spring-framework") - archiveClassifier.set("dist") - description = "Builds -${archiveClassifier} archive, containing all jars and docs, " + - "suitable for community download page." - - ext.baseDir = "spring-framework-${project.version}"; - - from("src/docs/dist") { - include "readme.txt" - include "license.txt" - include "notice.txt" - into "${baseDir}" - expand(copyright: new Date().format("yyyy"), version: project.version) - } - - from(zipTree(docsZip.archiveFile)) { - into "${baseDir}/docs" - } - - from(zipTree(schemaZip.archiveFile)) { - into "${baseDir}/schema" - } - - moduleProjects.each { module -> - into ("${baseDir}/libs") { - from module.jar - if (module.tasks.findByPath("sourcesJar")) { - from module.sourcesJar - } - if (module.tasks.findByPath("javadocJar")) { - from module.javadocJar - } - } - } -} - -distZip.mustRunAfter moduleProjects.check - -publishing { - publications { - mavenJava(MavenPublication) { - artifact docsZip - artifact schemaZip - artifact distZip - } - } } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index db9424fc990..9efe27ce3da 100644 --- a/settings.gradle +++ b/settings.gradle @@ -34,6 +34,7 @@ include "spring-web" include "spring-webflux" include "spring-webmvc" include "spring-websocket" +include "framework-api" include "framework-bom" include "framework-docs" include "framework-platform"