diff --git a/build.gradle.kts b/build.gradle.kts index 42fbbde71..82187aafe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -54,6 +54,8 @@ val intiTestRuntimeOnly: Configuration by configurations.getting { val funcTest: SourceSet by sourceSets.creating val funcTestImplementation: Configuration by configurations.getting { extendsFrom(configurations.testImplementation.get()) + // TODO: this will be removed after we migrated all functional tests to Kotlin. + extendsFrom(intiTestImplementation) } val funcTestRuntimeOnly: Configuration by configurations.getting { extendsFrom(configurations.testRuntimeOnly.get()) @@ -88,6 +90,10 @@ dependencies { funcTestImplementation(sourceSets.main.get().output) funcTestImplementation(intiTest.output) + intiTestImplementation(libs.okio) + intiTestImplementation(libs.apache.maven.modelBuilder) + intiTestImplementation(libs.apache.maven.repositoryMetadata) + lintChecks(libs.androidx.gradlePluginLints) lintChecks(libs.assertk.lint) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7f27a236e..9112523a9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,20 @@ +[versions] +maven = "3.9.9" + [libraries] apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.18.0" apache-commonsLang = "org.apache.commons:commons-lang3:3.17.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.3" +apache-maven-modelBuilder = { module = "org.apache.maven:maven-model-builder", version.ref = "maven" } +apache-maven-repositoryMetadata = { module = "org.apache.maven:maven-repository-metadata", version.ref = "maven" } asm = "org.ow2.asm:asm-commons:9.7.1" jdependency = "org.vafer:jdependency:2.11" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" +okio = "com.squareup.okio:okio:3.9.1" pluginPublish = "com.gradle.publish:plugin-publish-plugin:1.3.0" mavenPublish = "com.vanniktech:gradle-maven-publish-plugin:0.30.0" diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy index ff6f72cb6..77c888a38 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import org.apache.tools.zip.ZipFile import org.gradle.testkit.runner.BuildResult import spock.lang.Issue @@ -8,7 +7,7 @@ import spock.lang.Issue import java.util.jar.Attributes import java.util.jar.JarFile -class ApplicationSpec extends PluginSpecification { +class ApplicationSpec extends BasePluginSpecification { def 'integration with application plugin'() { given: diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy similarity index 87% rename from src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy index 780763716..f3393d6ec 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy @@ -1,5 +1,7 @@ -package com.github.jengelman.gradle.plugins.shadow.util +package com.github.jengelman.gradle.plugins.shadow +import com.github.jengelman.gradle.plugins.shadow.util.AppendableJar +import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository import org.codehaus.plexus.util.IOUtil import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner @@ -7,11 +9,12 @@ import spock.lang.Specification import spock.lang.TempDir import java.nio.file.Path +import java.nio.file.Paths import java.util.function.Function import java.util.jar.JarEntry import java.util.jar.JarFile -abstract class PluginSpecification extends Specification { +abstract class BasePluginSpecification extends Specification { @TempDir Path dir @@ -118,7 +121,7 @@ abstract class PluginSpecification extends Specification { } AppendableMavenFileRepository repo(String path = 'maven-repo') { - new AppendableMavenFileRepository(dir.resolve(path).toFile()) + new AppendableMavenFileRepository(dir.resolve(path)) } void assertJarFileContentsEqual(File f, String path, String contents) { @@ -161,7 +164,7 @@ abstract class PluginSpecification extends Specification { } AppendableJar buildJar(String path) { - return new AppendableJar(file(path)) + return new AppendableJar(file(path).toPath()) } protected File getOutput() { @@ -172,8 +175,8 @@ abstract class PluginSpecification extends Specification { getFile("build/libs/${name}") } - protected File getTestJar(String name = 'junit-3.8.2.jar') { - return new File(this.class.classLoader.getResource(name).toURI()) + protected Path getTestJar(String name = 'junit-3.8.2.jar') { + return Paths.get(this.class.classLoader.getResource(name).toURI()) } protected static File getTestKitDir() { @@ -183,4 +186,8 @@ abstract class PluginSpecification extends Specification { } return new File(gradleUserHome, "testkit") } + + protected static String escapedPath(Path path) { + return path.toString().replaceAll('\\\\', '\\\\\\\\') + } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy index 9bb5ef2a0..253db82bc 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy @@ -1,8 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification - -class ConfigurationCacheSpec extends PluginSpecification { +class ConfigurationCacheSpec extends BasePluginSpecification { @Override def setup() { diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy index 2c5af9c15..654ef5800 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy @@ -1,9 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification - - -class ConfigureShadowRelocationSpec extends PluginSpecification { +class ConfigureShadowRelocationSpec extends BasePluginSpecification { def "auto relocate plugin dependencies"() { given: diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy index 970e33e8b..60dc6ac47 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy @@ -1,12 +1,11 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome import spock.lang.Ignore import spock.lang.Issue -class FilteringSpec extends PluginSpecification { +class FilteringSpec extends BasePluginSpecification { @Override def setup() { diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy index e6e399b01..6f2616897 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy @@ -1,14 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import groovy.json.JsonSlurper import groovy.xml.XmlSlurper import org.gradle.api.attributes.Bundling import org.gradle.api.attributes.Usage import spock.lang.Issue -class PublishingSpec extends PluginSpecification { +class PublishingSpec extends BasePluginSpecification { AppendableMavenFileRepository publishingRepo @@ -60,14 +59,14 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').canonicalFile + File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').toFile().canonicalFile assert publishedFile.exists() and: contains(publishedFile, ['a.properties', 'a2.properties']) and: - File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').canonicalFile + File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile assert pom.exists() def contents = new XmlSlurper().parse(pom) @@ -128,7 +127,7 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0-my-classifier.my-ext').canonicalFile + File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0-my-classifier.my-ext').toFile().canonicalFile assert publishedFile.exists() } @@ -209,14 +208,14 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').canonicalFile + File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').toFile().canonicalFile assert publishedFile.exists() and: contains(publishedFile, ['a.properties', 'a2.properties']) and: - File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').canonicalFile + File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile assert pom.exists() def contents = new XmlSlurper().parse(pom) @@ -273,8 +272,8 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File mainJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.jar').canonicalFile - File shadowJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0-all.jar').canonicalFile + File mainJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.jar').toFile().canonicalFile + File shadowJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0-all.jar').toFile().canonicalFile assert mainJar.exists() assert shadowJar.exists() @@ -282,8 +281,8 @@ class PublishingSpec extends PluginSpecification { contains(shadowJar, ['a.properties', 'a2.properties']) and: "publishes both a POM file and a Gradle metadata file" - File pom = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.pom').canonicalFile - File gmm = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.module').canonicalFile + File pom = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.pom').toFile().canonicalFile + File gmm = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.module').toFile().canonicalFile pom.exists() gmm.exists() @@ -328,13 +327,13 @@ class PublishingSpec extends PluginSpecification { and: "verify shadow publication" assertions { - shadowJar = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0-all.jar').canonicalFile + shadowJar = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0-all.jar').toFile().canonicalFile assert shadowJar.exists() contains(shadowJar, ['a.properties', 'a2.properties']) } assertions { - pom = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.pom').canonicalFile + pom = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile assert pom.exists() pomContents = new XmlSlurper().parse(pom) assert pomContents.dependencies[0].dependency.size() == 1 @@ -351,7 +350,7 @@ class PublishingSpec extends PluginSpecification { } assertions { - gmm = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.module').canonicalFile + gmm = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.module').toFile().canonicalFile assert gmm.exists() gmmContents = new JsonSlurper().parse(gmm) assert gmmContents.variants.size() == 1 diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy index a72008197..244fbbcdf 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy @@ -1,13 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import spock.lang.Ignore import spock.lang.Issue import java.util.jar.Attributes import java.util.jar.JarFile -class RelocationSpec extends PluginSpecification { +class RelocationSpec extends BasePluginSpecification { @Issue('SHADOW-58') def "relocate dependency files"() { diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy index f214975d5..33d49dcc9 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy @@ -1,7 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import org.gradle.api.JavaVersion import org.gradle.api.Project import org.gradle.api.artifacts.Configuration @@ -17,7 +16,7 @@ import spock.lang.Unroll import java.util.jar.Attributes import java.util.jar.JarFile -class ShadowPluginSpec extends PluginSpecification { +class ShadowPluginSpec extends BasePluginSpecification { def 'apply plugin'() { given: @@ -63,7 +62,7 @@ class ShadowPluginSpec extends PluginSpecification { @Unroll def 'Compatible with Gradle #version'() { given: - File one = buildJar('one.jar').insertFile('META-INF/services/shadow.Shadow', + def one = buildJar('one.jar').insert('META-INF/services/shadow.Shadow', 'one # NOTE: No newline terminates this line/file').write() repo.module('shadow', 'two', '1.0').insertFile('META-INF/services/shadow.Shadow', @@ -1272,8 +1271,4 @@ class ShadowPluginSpec extends PluginSpecification { def jarFile = new JarFile(output("shadow-1.0-tests.jar")) assert jarFile.getEntry('junit') != null } - - private String escapedPath(File file) { - file.path.replaceAll('\\\\', '\\\\\\\\') - } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy index aac98bc18..1e3686980 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy @@ -4,27 +4,26 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransfor import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import spock.lang.Issue import spock.lang.Unroll import java.util.jar.JarInputStream import java.util.jar.Manifest -class TransformerSpec extends PluginSpecification { +class TransformerSpec extends BasePluginSpecification { def 'service resource transformer'() { given: - File one = buildJar('one.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', + def one = buildJar('one.jar') + .insert('META-INF/services/org.apache.maven.Shade', 'one # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'one') + .insert('META-INF/services/com.acme.Foo', 'one') .write() - File two = buildJar('two.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', + def two = buildJar('two.jar') + .insert('META-INF/services/org.apache.maven.Shade', 'two # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'two') + .insert('META-INF/services/com.acme.Foo', 'two') .write() buildFile << """ @@ -61,10 +60,10 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'service resource transformer alternate path'() { given: - File one = buildJar('one.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + def one = buildJar('one.jar').insert('META-INF/foo/org.apache.maven.Shade', 'one # NOTE: No newline terminates this line/file').write() - File two = buildJar('two.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + def two = buildJar('two.jar').insert('META-INF/foo/org.apache.maven.Shade', 'two # NOTE: No newline terminates this line/file').write() buildFile << """ @@ -96,16 +95,16 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'service resource transformer short syntax'() { given: - File one = buildJar('one.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', + def one = buildJar('one.jar') + .insert('META-INF/services/org.apache.maven.Shade', 'one # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'one') + .insert('META-INF/services/com.acme.Foo', 'one') .write() - File two = buildJar('two.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', + def two = buildJar('two.jar') + .insert('META-INF/services/org.apache.maven.Shade', 'two # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'two') + .insert('META-INF/services/com.acme.Foo', 'two') .write() buildFile << """ @@ -141,23 +140,23 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'service resource transformer short syntax relocation'() { given: - File one = buildJar('one.jar') - .insertFile('META-INF/services/java.sql.Driver', + def one = buildJar('one.jar') + .insert('META-INF/services/java.sql.Driver', '''oracle.jdbc.OracleDriver org.apache.hive.jdbc.HiveDriver'''.stripIndent()) - .insertFile('META-INF/services/org.apache.axis.components.compiler.Compiler', + .insert('META-INF/services/org.apache.axis.components.compiler.Compiler', 'org.apache.axis.components.compiler.Javac') - .insertFile('META-INF/services/org.apache.commons.logging.LogFactory', + .insert('META-INF/services/org.apache.commons.logging.LogFactory', 'org.apache.commons.logging.impl.LogFactoryImpl') .write() - File two = buildJar('two.jar') - .insertFile('META-INF/services/java.sql.Driver', + def two = buildJar('two.jar') + .insert('META-INF/services/java.sql.Driver', '''org.apache.derby.jdbc.AutoloadedDriver com.mysql.jdbc.Driver'''.stripIndent()) - .insertFile('META-INF/services/org.apache.axis.components.compiler.Compiler', + .insert('META-INF/services/org.apache.axis.components.compiler.Compiler', 'org.apache.axis.components.compiler.Jikes') - .insertFile('META-INF/services/org.apache.commons.logging.LogFactory', + .insert('META-INF/services/org.apache.commons.logging.LogFactory', 'org.mortbay.log.Factory') .write() @@ -207,10 +206,10 @@ org.mortbay.log.Factory'''.stripIndent() def 'service resource transformer short syntax alternate path'() { given: - File one = buildJar('one.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + def one = buildJar('one.jar').insert('META-INF/foo/org.apache.maven.Shade', 'one # NOTE: No newline terminates this line/file').write() - File two = buildJar('two.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + def two = buildJar('two.jar').insert('META-INF/foo/org.apache.maven.Shade', 'two # NOTE: No newline terminates this line/file').write() buildFile << """ @@ -240,7 +239,7 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() @Issue(['SHADOW-70', 'SHADOW-71']) def 'apply transformers to project resources'() { given: - File one = buildJar('one.jar').insertFile('META-INF/services/shadow.Shadow', + def one = buildJar('one.jar').insert('META-INF/services/shadow.Shadow', 'one # NOTE: No newline terminates this line/file').write() repo.module('shadow', 'two', '1.0').insertFile('META-INF/services/shadow.Shadow', @@ -277,10 +276,10 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'appending transformer'() { given: - File one = buildJar('one.jar').insertFile('test.properties', + def one = buildJar('one.jar').insert('test.properties', 'one # NOTE: No newline terminates this line/file').write() - File two = buildJar('two.jar').insertFile('test.properties', + def two = buildJar('two.jar').insert('test.properties', 'two # NOTE: No newline terminates this line/file').write() buildFile << """ @@ -313,10 +312,10 @@ two # NOTE: No newline terminates this line/file def 'appending transformer short syntax'() { given: - File one = buildJar('one.jar').insertFile('test.properties', + def one = buildJar('one.jar').insert('test.properties', 'one # NOTE: No newline terminates this line/file').write() - File two = buildJar('two.jar').insertFile('test.properties', + def two = buildJar('two.jar').insert('test.properties', 'two # NOTE: No newline terminates this line/file').write() buildFile << """ @@ -428,7 +427,7 @@ two # NOTE: No newline terminates this line/file def 'append xml files'() { given: - File xml1 = buildJar('xml1.jar').insertFile('properties.xml', + def xml1 = buildJar('xml1.jar').insert('properties.xml', ''' @@ -437,7 +436,7 @@ two # NOTE: No newline terminates this line/file '''.stripIndent() ).write() - File xml2 = buildJar('xml2.jar').insertFile('properties.xml', + def xml2 = buildJar('xml2.jar').insert('properties.xml', ''' @@ -602,14 +601,14 @@ two # NOTE: No newline terminates this line/file def 'Groovy extension module transformer'() { given: def one = buildJar('one.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=foo moduleVersion=1.0.5 extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() def two = buildJar('two.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=bar moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension @@ -646,14 +645,14 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( def 'Groovy extension module transformer works for Groovy2_5+'() { given: def one = buildJar('one.jar') - .insertFile('META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=foo moduleVersion=1.0.5 extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() def two = buildJar('two.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=bar moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension @@ -691,14 +690,14 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( def 'Groovy extension module transformer short syntax'() { given: def one = buildJar('one.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=foo moduleVersion=1.0.5 extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() def two = buildJar('two.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=bar moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension @@ -766,8 +765,4 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( 'ServiceFileTransformer' | '' 'XmlAppendingTransformer' | '' } - - private String escapedPath(File file) { - file.path.replaceAll('\\\\', '\\\\\\\\') - } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy index 72ebb78d4..15a08ced3 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy @@ -1,6 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.caching -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification +import com.github.jengelman.gradle.plugins.shadow.BasePluginSpecification import org.apache.commons.io.FileUtils import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome @@ -11,7 +11,7 @@ import java.nio.file.Path import static org.gradle.testkit.runner.TaskOutcome.FROM_CACHE import static org.gradle.testkit.runner.TaskOutcome.SUCCESS -abstract class AbstractCachingSpec extends PluginSpecification { +abstract class AbstractCachingSpec extends BasePluginSpecification { @TempDir Path alternateDir @@ -46,10 +46,6 @@ abstract class AbstractCachingSpec extends PluginSpecification { return runner.withProjectDir(alternateDir.toFile()).withArguments(cacheArguments).build() } - private String escapedPath(File file) { - file.path.replaceAll('\\\\', '\\\\\\\\') - } - void assertShadowJarHasResult(TaskOutcome expectedOutcome) { def result = runWithCacheEnabled(shadowJarTask) assert result.task(shadowJarTask).outcome == expectedOutcome diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy deleted file mode 100644 index 2e9022905..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy +++ /dev/null @@ -1,60 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import com.github.jengelman.gradle.plugins.shadow.util.repo.maven.MavenFileModule -import groovy.transform.InheritConstructors -import org.apache.commons.io.IOUtils - -@InheritConstructors -class AppendableMavenFileModule extends MavenFileModule { - - Map> contents = [:].withDefault { [:] } as Map> - Map files = [:] - - AppendableMavenFileModule use(File file) { - return use('', file) - } - - AppendableMavenFileModule use(String classifier, File file) { - files[classifier] = file - return this - } - - AppendableMavenFileModule insertFile(String path, String content) { - insertFile('', path, content) - return this - } - - AppendableMavenFileModule insertFile(String classifier, String path, String content) { - contents[classifier][path] = content - return this - } - - @Override - File publishArtifact(Map artifact) { - def artifactFile = artifactFile(artifact) - if (type == 'pom') { - return artifactFile - } - String classifier = (String) artifact['classifier'] ?: '' - if (files.containsKey(classifier)) { - publishWithStream(artifactFile) { OutputStream os -> - IOUtils.copy(files[classifier].newInputStream(), os) - } - } else { - publishWithStream(artifactFile) { OutputStream os -> - writeJar(os, contents[classifier]) - } - } - return artifactFile - } - - static void writeJar(OutputStream os, Map contents) { - if (contents) { - JarBuilder builder = new JarBuilder(os) - contents.each { path, content -> - builder.withFile(path, content) - } - builder.build() - } - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy deleted file mode 100644 index 18e4331da..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import com.github.jengelman.gradle.plugins.shadow.util.repo.maven.MavenFileRepository -import groovy.transform.InheritConstructors - -@InheritConstructors -class AppendableMavenFileRepository extends MavenFileRepository { - - @Override - AppendableMavenFileModule module(String groupId, String artifactId, String version = '1.0') { - def artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") - return new AppendableMavenFileModule(artifactDir, groupId, artifactId, version as String) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy deleted file mode 100644 index 936c0bbf9..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy +++ /dev/null @@ -1,26 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -/** - * TODO: this is used as extensions for Groovy, could be replaced after migrated to Kotlin. - * Registered in resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule. - */ -final class FileExtensions { - static final File resolve(File file, String relativePath) { - try { - return new File(file, relativePath) - } catch (RuntimeException e) { - throw new RuntimeException(String.format("Could not locate file '%s' relative to '%s'.", Arrays.toString(relativePath), file), e) - } - } - - static final File createDir(File file) { - if (file.mkdirs()) { - return file - } - if (file.isDirectory()) { - return file - } - throw new AssertionError("Problems creating dir: " + file - + ". Diagnostics: exists=" + file.exists() + ", isFile=" + file.isFile() + ", isDirectory=" + file.isDirectory()) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy deleted file mode 100644 index 90dddd2f6..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy +++ /dev/null @@ -1,218 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import com.github.jengelman.gradle.plugins.shadow.util.repo.AbstractModule -import groovy.xml.MarkupBuilder -import groovy.xml.XmlParser - -import java.text.SimpleDateFormat - -abstract class AbstractMavenModule extends AbstractModule implements MavenModule { - protected static final String MAVEN_METADATA_FILE = "maven-metadata.xml" - final File moduleDir - final String groupId - final String artifactId - final String version - String parentPomSection - String type = 'jar' - String packaging - int publishCount = 1 - private final List dependencies = [] - private final List artifacts = [] - final updateFormat = new SimpleDateFormat("yyyyMMddHHmmss") - final timestampFormat = new SimpleDateFormat("yyyyMMdd.HHmmss") - - AbstractMavenModule(File moduleDir, String groupId, String artifactId, String version) { - this.moduleDir = moduleDir - this.groupId = groupId - this.artifactId = artifactId - this.version = version - } - - abstract boolean getUniqueSnapshots() - - String getPublishArtifactVersion() { - if (uniqueSnapshots && version.endsWith("-SNAPSHOT")) { - return "${version.replaceFirst('-SNAPSHOT$', '')}-${getUniqueSnapshotVersion()}" - } - return version - } - - private String getUniqueSnapshotVersion() { - assert uniqueSnapshots && version.endsWith('-SNAPSHOT') - if (metaDataFile.isFile()) { - def metaData = new XmlParser().parse(metaDataFile) - def timestamp = metaData.versioning.snapshot.timestamp[0].text().trim() - def build = metaData.versioning.snapshot.buildNumber[0].text().trim() - return "${timestamp}-${build}" - } - return "${timestampFormat.format(publishTimestamp)}-${publishCount}" - } - - MavenModule dependsOn(String... dependencyArtifactIds) { - for (String id : dependencyArtifactIds) { - dependsOn(groupId, id, '1.0') - } - return this - } - - @Override - MavenModule dependsOn(String groupId, String artifactId, String version) { - this.dependencies << [groupId: groupId, artifactId: artifactId, version: version, type: type] - return this - } - - String getPackaging() { - return packaging - } - - List getDependencies() { - return dependencies - } - - @Override - File getPomFile() { - return moduleDir.resolve("$artifactId-${publishArtifactVersion}.pom") - } - - @Override - File getMetaDataFile() { - moduleDir.resolve(MAVEN_METADATA_FILE) - } - - File getRootMetaDataFile() { - moduleDir.parentFile.resolve(MAVEN_METADATA_FILE) - } - - File artifactFile(Map options) { - def artifact = toArtifact(options) - def fileName = "$artifactId-${publishArtifactVersion}.${artifact.type}" - if (artifact.classifier) { - fileName = "$artifactId-$publishArtifactVersion-${artifact.classifier}.${artifact.type}" - } - return moduleDir.resolve(fileName) - } - - protected Map toArtifact(Map options) { - options = new HashMap(options) - def artifact = [type: options.remove('type') ?: type, classifier: options.remove('classifier') ?: null] - assert options.isEmpty(): "Unknown options : ${options.keySet()}" - return artifact - } - - Date getPublishTimestamp() { - return new Date(updateFormat.parse("20100101120000").time + publishCount * 1000) - } - - @Override - MavenModule publishPom() { - moduleDir.createDir() - def rootMavenMetaData = getRootMetaDataFile() - - updateRootMavenMetaData(rootMavenMetaData) - - if (publishesMetaDataFile()) { - publishWithWriter(metaDataFile) { Writer writer -> - writer << getMetaDataFileContent() - } - } - - publishWithWriter(pomFile) { Writer writer -> - def pomPackaging = packaging ?: type - writer << """ - - - 4.0.0 - $groupId - $artifactId - $pomPackaging - $version - Published on $publishTimestamp - """.stripIndent() - - if (parentPomSection) { - writer << "\n$parentPomSection\n" - } - - if (!dependencies.empty) { - writer << "" - } - - dependencies.each { dependency -> - def typeAttribute = dependency['type'] == null ? "" : "$dependency.type" - writer << """ - - $dependency.groupId - $dependency.artifactId - $dependency.version - $typeAttribute - """.stripIndent() - } - - if (!dependencies.empty) { - writer << "" - } - - writer << "\n" - } - return this - } - - private void updateRootMavenMetaData(File rootMavenMetaData) { - def allVersions = rootMavenMetaData.exists() ? new XmlParser().parseText(rootMavenMetaData.text).versioning.versions.version*.value().flatten() : [] - allVersions << version - publishWithWriter(rootMavenMetaData) { Writer writer -> - def builder = new MarkupBuilder(writer) - builder.metadata { - groupId(groupId) - artifactId(artifactId) - version(allVersions.max()) - versioning { - if (uniqueSnapshots && version.endsWith("-SNAPSHOT")) { - snapshot { - timestamp(timestampFormat.format(publishTimestamp)) - buildNumber(publishCount) - lastUpdated(updateFormat.format(publishTimestamp)) - } - } else { - versions { - allVersions.each { currVersion -> - version(currVersion) - } - } - } - } - } - } - } - - abstract String getMetaDataFileContent() - - @Override - MavenModule publish() { - - publishPom() - artifacts.each { artifact -> - publishArtifact(artifact as Map) - } - publishArtifact([:]) - return this - } - - File publishArtifact(Map artifact) { - def artifactFile = artifactFile(artifact) - if (type == 'pom') { - return artifactFile - } - publishWithWriter(artifactFile) { Writer writer -> - writer << "${artifactFile.name} : $artifactContent" - } - return artifactFile - } - - protected String getArtifactContent() { - // Some content to include in each artifact, so that its size and content varies on each publish - return (0..publishCount).join("-") - } - - protected abstract boolean publishesMetaDataFile() -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy deleted file mode 100644 index 80d482224..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy +++ /dev/null @@ -1,46 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import org.jetbrains.annotations.NotNull - -class MavenFileModule extends AbstractMavenModule { - private boolean uniqueSnapshots = true - - MavenFileModule(File moduleDir, String groupId, String artifactId, String version) { - super(moduleDir, groupId, artifactId, version) - } - - @Override - boolean getUniqueSnapshots() { - return uniqueSnapshots - } - - @Override - String getMetaDataFileContent() { - """ - - - $groupId - $artifactId - $version - - - ${timestampFormat.format(publishTimestamp)} - $publishCount - - ${updateFormat.format(publishTimestamp)} - - - """.stripIndent() - } - - @Override - protected void onPublish(@NotNull File file) { - sha1File(file) - md5File(file) - } - - @Override - protected boolean publishesMetaDataFile() { - uniqueSnapshots && version.endsWith("-SNAPSHOT") - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy deleted file mode 100644 index fdff3a8ed..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -/** - * A fixture for dealing with file Maven repositories. - */ -class MavenFileRepository implements MavenRepository { - final File rootDir - - MavenFileRepository(File rootDir) { - this.rootDir = rootDir - } - - @Override - URI getUri() { - return rootDir.toURI() - } - - @Override - MavenFileModule module(String groupId, String artifactId, String version = '1.0') { - def artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") - return new MavenFileModule(artifactDir, groupId, artifactId, version as String) - } -} diff --git a/src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule b/src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule deleted file mode 100644 index bb7d5044f..000000000 --- a/src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule +++ /dev/null @@ -1,3 +0,0 @@ -moduleName = groovy-extensions -moduleVersion = ${moduleVersion} -extensionClasses =com.github.jengelman.gradle.plugins.shadow.util.FileExtensions diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt index 798a06bf7..41b102d82 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt @@ -1,20 +1,28 @@ package com.github.jengelman.gradle.plugins.shadow.util -import java.io.File +import java.io.OutputStream +import java.nio.file.Path +import kotlin.io.path.outputStream -class AppendableJar(private val file: File) { +class AppendableJar(private val outputPath: Path) { private val contents = mutableMapOf() - fun insertFile(path: String, content: String): AppendableJar = apply { + fun insert(path: String, content: String): AppendableJar = apply { contents[path] = content } - fun write(): File { - val builder = JarBuilder(file.outputStream()) - contents.forEach { (path, content) -> - builder.withFile(path, content) + fun write(): Path { + write(contents, outputPath.outputStream()) + return outputPath + } + + companion object { + fun write(contents: Map, outputStream: OutputStream) { + val builder = JarBuilder(outputStream) + contents.forEach { (path, content) -> + builder.withPath(path, content) + } + builder.build() } - builder.build() - return file } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt new file mode 100644 index 000000000..d41213392 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt @@ -0,0 +1,46 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import com.github.jengelman.gradle.plugins.shadow.util.repo.maven.MavenFileModule +import java.nio.file.Path +import kotlin.io.path.inputStream + +class AppendableMavenFileModule(module: MavenFileModule) : MavenFileModule(module.moduleDir, module.groupId, module.artifactId, module.version) { + + private val contents = mutableMapOf>().withDefault { mutableMapOf() } + private val paths = mutableMapOf() + + fun use(path: Path): AppendableMavenFileModule { + return use("", path) + } + + fun use(classifier: String, path: Path): AppendableMavenFileModule = apply { + paths[classifier] = path + } + + fun insertFile(path: String, content: String): AppendableMavenFileModule { + return insertFile("", path, content) + } + + fun insertFile(classifier: String, path: String, content: String): AppendableMavenFileModule = apply { + contents.getOrPut(classifier) { mutableMapOf() }[path] = content + } + + override fun publishArtifact(artifact: Map): Path { + val artifactPath = artifactPath(artifact) + if (type == "pom") { + return artifactPath + } + val classifier = artifact["classifier"] as? String ?: "" + val classifierPath = paths[classifier] + if (classifierPath != null) { + publish(artifactPath) { os -> + classifierPath.inputStream().copyTo(os) + } + } else { + publish(artifactPath) { os -> + AppendableJar.write(contents[classifier].orEmpty(), os) + } + } + return artifactPath + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt new file mode 100644 index 000000000..e2ce40630 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt @@ -0,0 +1,11 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import com.github.jengelman.gradle.plugins.shadow.util.repo.maven.MavenFileRepository +import java.nio.file.Path + +class AppendableMavenFileRepository(rootDir: Path) : MavenFileRepository(rootDir) { + + override fun module(groupId: String, artifactId: String, version: String): AppendableMavenFileModule { + return AppendableMavenFileModule(super.module(groupId, artifactId, version)) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt deleted file mode 100644 index 492a8a526..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util -import java.io.File -import java.io.FileInputStream -import java.io.FileNotFoundException -import java.io.IOException -import java.io.InputStream -import java.io.UncheckedIOException -import java.security.MessageDigest -import java.security.NoSuchAlgorithmException -import org.gradle.internal.UncheckedException - -object HashUtil { - fun createHash(file: File, algorithm: String): HashValue { - try { - return createHash(FileInputStream(file), algorithm) - } catch (e: UncheckedIOException) { - // Catch any unchecked io exceptions and add the file path for troubleshooting - throw UncheckedIOException( - "Failed to create $algorithm hash for file ${file.absolutePath}.", - e.cause, - ) - } catch (e: FileNotFoundException) { - throw UncheckedIOException(e) - } - } - - private fun createHash(inputStream: InputStream, algorithm: String): HashValue { - val messageDigest: MessageDigest - try { - messageDigest = createMessageDigest(algorithm) - val buffer = ByteArray(4096) - inputStream.use { - while (true) { - val nread = it.read(buffer) - if (nread < 0) { - break - } - messageDigest.update(buffer, 0, nread) - } - } - } catch (e: IOException) { - throw UncheckedIOException(e) - } - return HashValue(messageDigest.digest()) - } - - private fun createMessageDigest(algorithm: String): MessageDigest { - try { - return MessageDigest.getInstance(algorithm) - } catch (e: NoSuchAlgorithmException) { - throw UncheckedException.throwAsUncheckedException(e) - } - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt deleted file mode 100644 index 821a0953b..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import java.math.BigInteger - -data class HashValue(val digest: BigInteger) { - constructor(digest: ByteArray) : this(BigInteger(1, digest)) -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt index d4c81cabb..2372d5823 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt @@ -5,33 +5,28 @@ import java.util.jar.JarEntry import java.util.jar.JarOutputStream class JarBuilder(os: OutputStream) { - private val entries = mutableListOf() + private val entries = mutableSetOf() private val jos = JarOutputStream(os) private fun addDirectory(name: String) { - if (!entries.contains(name)) { + if (entries.add(name)) { val parent = name.substringBeforeLast('/', "") if (parent.isNotEmpty() && !entries.contains(parent)) { addDirectory(parent) } - // directory entries must end in "/" - val entry = JarEntry("$name/") - jos.putNextEntry(entry) - entries.add(name) + jos.putNextEntry(JarEntry("$name/")) } } - fun withFile(path: String, data: String): JarBuilder { + fun withPath(path: String, data: String): JarBuilder { val idx = path.lastIndexOf('/') if (idx != -1) { addDirectory(path.substring(0, idx)) } - if (!entries.contains(path)) { - val entry = JarEntry(path) - jos.putNextEntry(entry) - entries.add(path) - data.byteInputStream().use { it.copyTo(jos) } + if (entries.add(path)) { + jos.putNextEntry(JarEntry(path)) + data.byteInputStream().copyTo(jos) } return this } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt index 51d740e5d..827964aa4 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt @@ -1,63 +1,79 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo -import com.github.jengelman.gradle.plugins.shadow.util.HashUtil -import java.io.File import java.io.OutputStream -import java.io.Writer +import java.io.UncheckedIOException import java.math.BigInteger +import java.nio.file.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.exists +import kotlin.io.path.moveTo +import kotlin.io.path.name +import kotlin.io.path.outputStream +import kotlin.io.path.readBytes +import kotlin.io.path.writeText +import okio.ByteString.Companion.toByteString abstract class AbstractModule { - protected abstract fun onPublish(file: File) + protected open fun postPublish(path: Path) = Unit - protected fun publishWithWriter(file: File, action: (Writer) -> Unit) { - publishCommon(file) { it.writer().use(action) } - } - - protected fun publishWithStream(file: File, action: (OutputStream) -> Unit) { - publishCommon(file) { it.outputStream().use(action) } - } + protected fun publish(path: Path, action: (OutputStream) -> Unit) { + val hashBefore = if (path.exists()) getHash(path, "sha1") else null + val tempPath = path.resolveSibling("${path.name}.tmp") + tempPath.outputStream().use(action) - private fun publishCommon(file: File, action: (File) -> Unit) { - val hashBefore = if (file.exists()) getHash(file, "sha1") else null - val tempFile = file.resolveSibling("${file.name}.tmp") - action(tempFile) - - val hashAfter = getHash(tempFile, "sha1") + val hashAfter = getHash(tempPath, "sha1") if (hashAfter == hashBefore) { // Already published return } - check(!file.exists() || file.delete()) - check(tempFile.renameTo(file)) - onPublish(file) + tempPath.moveTo(path, true) + check(path.exists()) + writeSha1Path(path) + writeMd5Path(path) + + postPublish(path) } companion object { - @JvmStatic - fun sha1File(file: File): File { - return hashFile(file, "sha1", 40) + fun writeSha1Path(path: Path): Path { + return writeHashPath(path, "sha1", 40) } - @JvmStatic - fun md5File(file: File): File { - return hashFile(file, "md5", 32) + fun writeMd5Path(path: Path): Path { + return writeHashPath(path, "md5", 32) } - private fun hashFile(file: File, algorithm: String, len: Int): File { - val hashFile = getHashFile(file, algorithm) - val hash = getHash(file, algorithm) - hashFile.writeText("$hash${len}x") - return hashFile + private fun writeHashPath(path: Path, algorithm: String, len: Int): Path { + val hashPath = getHashPath(path, algorithm) + val hash = getHash(path, algorithm) + hashPath.writeText("$hash${len}x") + return hashPath } - private fun getHashFile(file: File, algorithm: String): File { - return File(file.parentFile, "${file.name}.$algorithm") + private fun getHashPath(path: Path, algorithm: String): Path { + return path.resolveSibling("${path.name}.$algorithm") } - private fun getHash(file: File, algorithm: String): BigInteger { - return HashUtil.createHash(file, algorithm.uppercase()).digest + private fun getHash(path: Path, algorithm: String): BigInteger { + try { + val byteString = path.readBytes().toByteString() + val byteArray = when (algorithm.uppercase()) { + "MD5" -> byteString.md5() + "SHA1" -> byteString.sha1() + "SHA256" -> byteString.sha256() + "SHA512" -> byteString.sha512() + else -> throw IllegalArgumentException("Unsupported algorithm: $algorithm") + }.toByteArray() + return BigInteger(1, byteArray) + } catch (e: UncheckedIOException) { + // Catch any unchecked io exceptions and add the file path for troubleshooting + throw UncheckedIOException( + "Failed to create $algorithm hash for file ${path.absolutePathString()}.", + e.cause, + ) + } } } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt new file mode 100644 index 000000000..8d4ada5d6 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt @@ -0,0 +1,184 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo.maven + +import com.github.jengelman.gradle.plugins.shadow.util.repo.AbstractModule +import java.nio.file.Path +import java.text.SimpleDateFormat +import java.util.Date +import kotlin.io.path.createDirectories +import kotlin.io.path.exists +import kotlin.io.path.isRegularFile +import kotlin.io.path.name +import kotlin.io.path.reader +import org.apache.maven.artifact.repository.metadata.Metadata +import org.apache.maven.artifact.repository.metadata.Snapshot +import org.apache.maven.artifact.repository.metadata.Versioning +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer +import org.apache.maven.model.Dependency +import org.apache.maven.model.Model +import org.apache.maven.model.io.xpp3.MavenXpp3Writer + +abstract class AbstractMavenModule( + val moduleDir: Path, + val groupId: String, + val artifactId: String, + val version: String, +) : AbstractModule(), + MavenModule { + + protected val updateFormat = SimpleDateFormat("yyyyMMddHHmmss") + protected val timestampFormat = SimpleDateFormat("yyyyMMdd.HHmmss") + protected val dependencies = mutableListOf() + protected val artifacts = mutableListOf>() + + protected var type: String = "jar" + protected var packaging: String? = null + protected var publishCount: Int = 1 + + protected abstract val isUniqueSnapshots: Boolean + protected abstract val isPublishesMetaDataFile: Boolean + + fun dependsOn(artifactId: String): MavenModule { + return dependsOn(groupId = groupId, artifactId = artifactId, version = version) + } + + override fun dependsOn(groupId: String, artifactId: String, version: String): MavenModule = apply { + val dep = Dependency().also { + it.groupId = groupId + it.artifactId = artifactId + it.version = version + } + dependencies.add(dep) + } + + override val pomPath: Path + get() = moduleDir.resolve("$artifactId-$publishArtifactVersion.pom") + + override val metaDataPath: Path + get() = moduleDir.resolve(MAVEN_METADATA_FILE) + + val rootMetaDataPath: Path + get() = moduleDir.resolveSibling(MAVEN_METADATA_FILE) + + fun artifactPath(options: Map): Path { + val artifact = toArtifact(options) + var fileName = "$artifactId-$publishArtifactVersion.${artifact["type"]}" + if (artifact["classifier"] != null) { + fileName = "$artifactId-$publishArtifactVersion-${artifact["classifier"]}.${artifact["type"]}" + } + return moduleDir.resolve(fileName) + } + + override fun publishPom(): MavenModule = apply { + moduleDir.createDirectories() + val rootMavenMetaData = rootMetaDataPath + updateRootMavenMetaData(rootMavenMetaData) + + if (isPublishesMetaDataFile) { + publish(metaDataPath) { outputStream -> + MetadataXpp3Writer().write(outputStream, getMetaData(emptyList())) + } + } + + publish(pomPath) { outputStream -> + val pomPackaging = packaging ?: type + val model = Model().also { + it.modelVersion = "4.0.0" + it.groupId = groupId + it.artifactId = artifactId + it.version = version + it.packaging = pomPackaging + it.description = "Published on $publishTimestamp" + it.dependencies = dependencies + } + MavenXpp3Writer().write(outputStream, model) + } + } + + open fun getMetaData(versions: List): Metadata = Metadata().also { + it.groupId = groupId + it.artifactId = artifactId + it.version = version + it.versioning = Versioning().also { versioning -> + versioning.versions = versions + versioning.lastUpdated = updateFormat.format(publishTimestamp) + if (isUniqueSnapshots && version.endsWith("-SNAPSHOT")) { + versioning.snapshot = Snapshot().apply { + timestamp = timestampFormat.format(publishTimestamp) + buildNumber = publishCount + } + } + } + } + + override fun publish(): MavenModule = apply { + publishPom() + artifacts.forEach { artifact -> + publishArtifact(artifact) + } + publishArtifact(emptyMap()) + } + + open fun publishArtifact(artifact: Map): Path { + val artifactPath = artifactPath(artifact) + if (type == "pom") { + return artifactPath + } + publish(artifactPath) { outputStream -> + outputStream.write("${artifactPath.name} : $artifactContent".toByteArray()) + } + return artifactPath + } + + protected fun toArtifact(options: Map): Map { + val artifact = mutableMapOf( + "type" to (options["type"] ?: type), + "classifier" to options["classifier"], + ) + require(options.keys.isEmpty()) { "Unknown options : ${options.keys}" } + return artifact + } + + protected val publishArtifactVersion: String + get() = if (isUniqueSnapshots && version.endsWith("-SNAPSHOT")) { + "${version.removeSuffix("-SNAPSHOT")}-$uniqueSnapshotVersion" + } else { + version + } + + protected val publishTimestamp: Date + get() = Date(updateFormat.parse("20100101120000").time + publishCount * 1000) + + private fun updateRootMavenMetaData(rootMavenMetaData: Path) { + val allVersions = if (rootMavenMetaData.exists()) { + MetadataXpp3Reader().read(rootMavenMetaData.reader()).versioning.versions + } else { + mutableListOf() + } + allVersions.add(version) + publish(rootMavenMetaData) { outputStream -> + MetadataXpp3Writer().write(outputStream, getMetaData(allVersions)) + } + } + + private val artifactContent: String + // Some content to include in each artifact, so that its size and content varies on each pu + get() = (0..publishCount).joinToString("-") + + private val uniqueSnapshotVersion: String + get() { + require(isUniqueSnapshots && version.endsWith("-SNAPSHOT")) + return if (metaDataPath.isRegularFile()) { + val metaData = MetadataXpp3Reader().read(metaDataPath.reader()) + val timestamp = metaData.versioning.snapshot.timestamp + val build = metaData.versioning.snapshot.buildNumber + "$timestamp-$build" + } else { + "${timestampFormat.format(publishTimestamp)}-$publishCount" + } + } + + protected companion object { + const val MAVEN_METADATA_FILE = "maven-metadata.xml" + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt new file mode 100644 index 000000000..0807d4021 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt @@ -0,0 +1,35 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo.maven + +import java.nio.file.Path +import org.apache.maven.artifact.repository.metadata.Metadata +import org.apache.maven.artifact.repository.metadata.Snapshot +import org.apache.maven.artifact.repository.metadata.Versioning + +open class MavenFileModule( + moduleDir: Path, + groupId: String, + artifactId: String, + version: String, +) : AbstractMavenModule(moduleDir, groupId, artifactId, version) { + + override val isUniqueSnapshots: Boolean = true + + override fun getMetaData(versions: List): Metadata { + return Metadata().also { + it.groupId = groupId + it.artifactId = artifactId + it.version = version + it.versioning = Versioning().also { versioning -> + versioning.versions = versions + versioning.snapshot = Snapshot().apply { + timestamp = timestampFormat.format(publishTimestamp) + buildNumber = publishCount + } + versioning.lastUpdated = updateFormat.format(publishTimestamp) + } + } + } + + override val isPublishesMetaDataFile: Boolean + get() = isUniqueSnapshots && version.endsWith("-SNAPSHOT") +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt new file mode 100644 index 000000000..e1ba0c089 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt @@ -0,0 +1,17 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo.maven + +import java.net.URI +import java.nio.file.Path + +/** + * A fixture for dealing with file Maven repositories. + */ +open class MavenFileRepository(val rootDir: Path) : MavenRepository { + + override val uri: URI = rootDir.toUri() + + override fun module(groupId: String, artifactId: String, version: String): MavenFileModule { + val artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") + return MavenFileModule(artifactDir, groupId, artifactId, version) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt index b77d25886..e484f2a96 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt @@ -1,6 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven -import java.io.File +import java.nio.file.Path interface MavenModule { /** @@ -16,7 +16,7 @@ interface MavenModule { fun dependsOn(groupId: String, artifactId: String, version: String): MavenModule - val pomFile: File + val pomPath: Path - val metaDataFile: File + val metaDataPath: Path } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt index a128e07f5..95ca83c86 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt @@ -8,7 +8,5 @@ import java.net.URI interface MavenRepository { val uri: URI - fun module(groupId: String, artifactId: String): MavenModule - fun module(groupId: String, artifactId: String, version: String): MavenModule }