diff --git a/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java b/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java new file mode 100644 index 00000000000..de14fd69101 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java @@ -0,0 +1,175 @@ +package org.springframework.build.shadow; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.gradle.api.DefaultTask; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.component.ModuleComponentSelector; +import org.gradle.api.artifacts.query.ArtifactResolutionQuery; +import org.gradle.api.artifacts.result.ArtifactResolutionResult; +import org.gradle.api.artifacts.result.ComponentArtifactsResult; +import org.gradle.api.artifacts.result.DependencyResult; +import org.gradle.api.artifacts.result.ResolutionResult; +import org.gradle.api.artifacts.result.ResolvedArtifactResult; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.FileCopyDetails; +import org.gradle.api.file.FileTree; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Nested; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.TaskAction; +import org.gradle.jvm.JvmLibrary; +import org.gradle.language.base.artifact.SourcesArtifact; + +/** + * Gradle task to add source from shadowed jars into our own source jars. + * + * @author Phillip Webb + * @author Andy Wilkinson + */ +public class ShadowSource extends DefaultTask { + + private final DirectoryProperty outputDirectory = getProject().getObjects().directoryProperty(); + + private List configurations = new ArrayList<>(); + + private final List relocations = new ArrayList<>(); + + + @Classpath + @Optional + public List getConfigurations() { + return this.configurations; + } + + public void setConfigurations(List configurations) { + this.configurations = configurations; + } + + @Nested + public List getRelocations() { + return this.relocations; + } + + public void relocate(String pattern, String destination) { + this.relocations.add(new Relocation(pattern, destination)); + } + + @OutputDirectory + DirectoryProperty getOutputDirectory() { + return this.outputDirectory; + } + + @TaskAction + void syncSourceJarFiles() { + sync(getSourceJarFiles()); + } + + private List getSourceJarFiles() { + List sourceJarFiles = new ArrayList<>(); + for (Configuration configuration : this.configurations) { + ResolutionResult resolutionResult = configuration.getIncoming().getResolutionResult(); + resolutionResult.getRootComponent().get().getDependencies().forEach(dependency -> { + Set artifactsResults = resolveSourceArtifacts(dependency); + for (ComponentArtifactsResult artifactResult : artifactsResults) { + artifactResult.getArtifacts(SourcesArtifact.class).forEach(sourceArtifact -> { + sourceJarFiles.add(((ResolvedArtifactResult) sourceArtifact).getFile()); + }); + } + }); + } + return Collections.unmodifiableList(sourceJarFiles); + } + + private Set resolveSourceArtifacts(DependencyResult dependency) { + ModuleComponentSelector componentSelector = (ModuleComponentSelector) dependency.getRequested(); + ArtifactResolutionQuery query = getProject().getDependencies().createArtifactResolutionQuery() + .forModule(componentSelector.getGroup(), componentSelector.getModule(), componentSelector.getVersion()); + return executeQuery(query).getResolvedComponents(); + } + + @SuppressWarnings("unchecked") + private ArtifactResolutionResult executeQuery(ArtifactResolutionQuery query) { + return query.withArtifacts(JvmLibrary.class, SourcesArtifact.class).execute(); + } + + private void sync(List sourceJarFiles) { + getProject().sync(spec -> { + spec.into(this.outputDirectory); + spec.eachFile(this::relocateFile); + spec.filter(this::transformContent); + spec.exclude("META-INF/**"); + spec.setIncludeEmptyDirs(false); + sourceJarFiles.forEach(sourceJar -> spec.from(zipTree(sourceJar))); + }); + } + + private void relocateFile(FileCopyDetails details) { + String path = details.getPath(); + for (Relocation relocation : this.relocations) { + path = relocation.relocatePath(path); + } + details.setPath(path); + } + + private String transformContent(String content) { + for (Relocation relocation : this.relocations) { + content = relocation.transformContent(content); + } + return content; + } + + private FileTree zipTree(File sourceJar) { + return getProject().zipTree(sourceJar); + } + + + /** + * A single relocation. + */ + static class Relocation { + + private final String pattern; + + private final String pathPattern; + + private final String destination; + + private final String pathDestination; + + + Relocation(String pattern, String destination) { + this.pattern = pattern; + this.pathPattern = pattern.replace('.', '/'); + this.destination = destination; + this.pathDestination = destination.replace('.', '/'); + } + + + @Input + public String getPattern() { + return this.pattern; + } + + @Input + public String getDestination() { + return this.destination; + } + + String relocatePath(String path) { + return path.replace(this.pathPattern, this.pathDestination); + } + + public String transformContent(String content) { + return content.replaceAll("\\b" + this.pattern, this.destination); + } + + } + +} diff --git a/spring-core/spring-core.gradle b/spring-core/spring-core.gradle index 523974786df..a431678bd56 100644 --- a/spring-core/spring-core.gradle +++ b/spring-core/spring-core.gradle @@ -1,4 +1,5 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.springframework.build.shadow.ShadowSource description = "Spring Core" @@ -27,6 +28,13 @@ task cglibRepackJar(type: ShadowJar) { relocate 'org.objectweb.asm', 'org.springframework.asm' } +task cglibSource(type: ShadowSource) { + configurations = [project.configurations.cglib] + relocate 'net.sf.cglib', 'org.springframework.cglib' + relocate 'org.objectweb.asm', 'org.springframework.asm' + outputDirectory = file("build/shadow-source/cglib") +} + task javapoetRepackJar(type: ShadowJar) { archiveBaseName.set('spring-javapoet-repack') archiveVersion.set(javapoetVersion) @@ -34,6 +42,12 @@ task javapoetRepackJar(type: ShadowJar) { relocate 'com.squareup.javapoet', 'org.springframework.javapoet' } +task javapoetSource(type: ShadowSource) { + configurations = [project.configurations.javapoet] + relocate 'com.squareup.javapoet', 'org.springframework.javapoet' + outputDirectory = file("build/shadow-source/javapoet") +} + task objenesisRepackJar(type: ShadowJar) { archiveBaseName.set('spring-objenesis-repack') archiveVersion.set(objenesisVersion) @@ -41,6 +55,12 @@ task objenesisRepackJar(type: ShadowJar) { relocate 'org.objenesis', 'org.springframework.objenesis' } +task objenesisSource(type: ShadowSource) { + configurations = [project.configurations.objenesis] + relocate 'org.objenesis', 'org.springframework.objenesis' + outputDirectory = file("build/shadow-source/objenesis") +} + dependencies { cglib("cglib:cglib:${cglibVersion}@jar") javapoet("com.squareup:javapoet:${javapoetVersion}@jar") @@ -122,3 +142,9 @@ test { "-XX:+AllowRedefinitionToAddDeleteMethods" ] } + +sourcesJar { + from cglibSource + from javapoetSource + from objenesisSource +}