diff --git a/build.gradle.kts b/build.gradle.kts index 8cdb3d17e..42fbbde71 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -86,6 +86,7 @@ dependencies { exclude(group = "org.hamcrest") } funcTestImplementation(sourceSets.main.get().output) + funcTestImplementation(intiTest.output) lintChecks(libs.androidx.gradlePluginLints) lintChecks(libs.assertk.lint) diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy deleted file mode 100644 index b6fad7012..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -class AppendableJar { - - Map contents = [:] - File file - - AppendableJar(File file) { - this.file = file - } - - AppendableJar insertFile(String path, String content) { - contents[path] = content - return this - } - - File write() { - JarBuilder builder = new JarBuilder(file.newOutputStream()) - contents.each { path, contents -> - builder.withFile(path, contents) - } - builder.build() - return file - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java deleted file mode 100644 index b4cb8404d..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util; - -import org.gradle.api.UncheckedIOException; -import org.gradle.internal.UncheckedException; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public class HashUtil { - public static HashValue createHash(File file, String algorithm) { - try { - return createHash(new FileInputStream(file), algorithm); - } catch (UncheckedIOException e) { - // Catch any unchecked io exceptions and add the file path for troubleshooting - throw new UncheckedIOException(String.format("Failed to create %s hash for file %s.", algorithm, file.getAbsolutePath()), e.getCause()); - } catch (FileNotFoundException e) { - throw new UncheckedIOException(e); - } - } - - private static HashValue createHash(InputStream instr, String algorithm) { - MessageDigest messageDigest; - try { - messageDigest = createMessageDigest(algorithm); - byte[] buffer = new byte[4096]; - try { - while (true) { - int nread = instr.read(buffer); - if (nread < 0) { - break; - } - messageDigest.update(buffer, 0, nread); - } - } finally { - instr.close(); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - return new HashValue(messageDigest.digest()); - } - - private static MessageDigest createMessageDigest(String algorithm) { - try { - return MessageDigest.getInstance(algorithm); - } catch (NoSuchAlgorithmException e) { - throw UncheckedException.throwAsUncheckedException(e); - } - } - - public static HashValue sha1(byte[] bytes) { - return createHash(new ByteArrayInputStream(bytes), "SHA1"); - } - - public static HashValue sha1(InputStream inputStream) { - return createHash(inputStream, "SHA1"); - } - - public static HashValue md5(File file) { - return createHash(file, "MD5"); - } - - public static HashValue sha1(File file) { - return createHash(file, "SHA1"); - } - - public static HashValue sha256(byte[] bytes) { - return createHash(new ByteArrayInputStream(bytes), "SHA-256"); - } - - public static HashValue sha256(InputStream inputStream) { - return createHash(inputStream, "SHA-256"); - } - - public static HashValue sha256(File file) { - return createHash(file, "SHA-256"); - } - - public static HashValue sha512(InputStream inputStream) { - return createHash(inputStream, "SHA-512"); - } - - public static HashValue sha512(File file) { - return createHash(file, "SHA-512"); - } -} - diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java deleted file mode 100644 index 967afe7ea..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util; - -import java.math.BigInteger; - -public class HashValue { - private final BigInteger digest; - - public HashValue(byte[] digest) { - this.digest = new BigInteger(1, digest); - } - - public HashValue(String hexString) { - this.digest = new BigInteger(hexString, 16); - } - - public static HashValue parse(String inputString) { - if (inputString == null || inputString.isEmpty()) { - return null; - } - return new HashValue(parseInput(inputString)); - } - - private static String parseInput(String inputString) { - if (inputString == null) { - return null; - } - String cleaned = inputString.trim().toLowerCase(); - int spaceIndex = cleaned.indexOf(' '); - if (spaceIndex != -1) { - String firstPart = cleaned.substring(0, spaceIndex); - if (firstPart.startsWith("md") || firstPart.startsWith("sha")) { - cleaned = cleaned.substring(cleaned.lastIndexOf(' ') + 1); - } else if (firstPart.endsWith(":")) { - cleaned = cleaned.substring(spaceIndex + 1).replace(" ", ""); - } else { - cleaned = cleaned.substring(0, spaceIndex); - } - } - return cleaned; - } - - public String asCompactString() { - return digest.toString(36); - } - - public String asHexString() { - return digest.toString(16); - } - - public byte[] asByteArray() { - return digest.toByteArray(); - } - - public BigInteger asBigInteger() { - return digest; - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof HashValue)) { - return false; - } - - HashValue otherHashValue = (HashValue) other; - return digest.equals(otherHashValue.digest); - } - - @Override - public int hashCode() { - return digest.hashCode(); - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy deleted file mode 100644 index b24eb994c..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy +++ /dev/null @@ -1,51 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import org.codehaus.plexus.util.IOUtil - -import java.util.jar.JarEntry -import java.util.jar.JarOutputStream - -class JarBuilder { - - List entries = [] - JarOutputStream jos - - JarBuilder(OutputStream os) { - jos = new JarOutputStream(os) - } - - private void addDirectory(String name) { - if (!entries.contains(name)) { - if (name.lastIndexOf('/') > 0) { - String parent = name.substring(0, name.lastIndexOf('/')) - if (!entries.contains(parent)) { - addDirectory(parent) - } - } - - // directory entries must end in "/" - JarEntry entry = new JarEntry(name + "/") - jos.putNextEntry(entry) - - entries.add(name) - } - } - - JarBuilder withFile(String path, String data) { - def idx = path.lastIndexOf('/') - if (idx != -1) { - addDirectory(path.substring(0, idx)) - } - if (!entries.contains(path)) { - JarEntry entry = new JarEntry(path) - jos.putNextEntry(entry) - entries << path - IOUtil.copy(data.bytes, jos) - } - return this - } - - void build() { - jos.close() - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy deleted file mode 100644 index 8b99bf018..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy +++ /dev/null @@ -1,71 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo - -import com.github.jengelman.gradle.plugins.shadow.util.HashUtil - -abstract class AbstractModule { - /** - * @param cl A closure that is passed a writer to use to generate the content. - */ - protected void publish(File file, Closure cl) { - def hashBefore = file.exists() ? getHash(file, "sha1") : null - def tmpFile = file.parentFile.resolve("${file.name}.tmp") - - tmpFile.withWriter("utf-8") { - cl.call(it) - } - - def hashAfter = getHash(tmpFile, "sha1") - if (hashAfter == hashBefore) { - // Already published - return - } - - assert !file.exists() || file.delete() - assert tmpFile.renameTo(file) - onPublish(file) - } - - protected void publishWithStream(File file, Closure cl) { - def hashBefore = file.exists() ? getHash(file, "sha1") : null - def tmpFile = file.parentFile.resolve("${file.name}.tmp") - - tmpFile.withOutputStream { - cl.call(it) - } - - def hashAfter = getHash(tmpFile, "sha1") - if (hashAfter == hashBefore) { - // Already published - return - } - - assert !file.exists() || file.delete() - assert tmpFile.renameTo(file) - onPublish(file) - } - - protected abstract onPublish(File file) - - static File sha1File(File file) { - hashFile(file, "sha1", 40) - } - - static File md5File(File file) { - hashFile(file, "md5", 32) - } - - private static File hashFile(File file, String algorithm, int len) { - def hashFile = getHashFile(file, algorithm) - def hash = getHash(file, algorithm) - hashFile.text = String.format("%0${len}x", hash) - return hashFile - } - - private static File getHashFile(File file, String algorithm) { - file.parentFile.resolve("${file.name}.${algorithm}") - } - - private static BigInteger getHash(File file, String algorithm) { - HashUtil.createHash(file, algorithm.toUpperCase()).asBigInteger() - } -} 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 index aa3ec713b..90dddd2f6 100644 --- 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 @@ -56,8 +56,8 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule } @Override - MavenModule dependsOn(String group, String artifactId, String version) { - this.dependencies << [groupId: group, artifactId: artifactId, version: version, type: type] + MavenModule dependsOn(String groupId, String artifactId, String version) { + this.dependencies << [groupId: groupId, artifactId: artifactId, version: version, type: type] return this } @@ -111,12 +111,12 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule updateRootMavenMetaData(rootMavenMetaData) if (publishesMetaDataFile()) { - publish(metaDataFile) { Writer writer -> + publishWithWriter(metaDataFile) { Writer writer -> writer << getMetaDataFileContent() } } - publish(pomFile) { Writer writer -> + publishWithWriter(pomFile) { Writer writer -> def pomPackaging = packaging ?: type writer << """ @@ -160,7 +160,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule private void updateRootMavenMetaData(File rootMavenMetaData) { def allVersions = rootMavenMetaData.exists() ? new XmlParser().parseText(rootMavenMetaData.text).versioning.versions.version*.value().flatten() : [] allVersions << version - publish(rootMavenMetaData) { Writer writer -> + publishWithWriter(rootMavenMetaData) { Writer writer -> def builder = new MarkupBuilder(writer) builder.metadata { groupId(groupId) @@ -203,7 +203,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule if (type == 'pom') { return artifactFile } - publish(artifactFile) { Writer writer -> + publishWithWriter(artifactFile) { Writer writer -> writer << "${artifactFile.name} : $artifactContent" } return artifactFile 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 index fad253beb..80d482224 100644 --- 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 @@ -1,5 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven +import org.jetbrains.annotations.NotNull + class MavenFileModule extends AbstractMavenModule { private boolean uniqueSnapshots = true @@ -32,7 +34,7 @@ class MavenFileModule extends AbstractMavenModule { } @Override - protected onPublish(File file) { + protected void onPublish(@NotNull File file) { sha1File(file) md5File(file) } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy deleted file mode 100644 index dd74383e3..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -interface MavenModule { - /** - * Publishes the pom.xml plus main artifact, plus any additional artifacts for this module. Publishes only those artifacts whose content has - * changed since the last call to {@code #publish()}. - */ - MavenModule publish() - - /** - * Publishes the pom.xml only - */ - MavenModule publishPom() - - MavenModule dependsOn(String group, String artifactId, String version) - - File getPomFile() - - File getMetaDataFile() -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy deleted file mode 100644 index 614b083bb..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -/** - * A fixture for dealing with Maven repositories. - */ -interface MavenRepository { - URI getUri() - - MavenModule module(String groupId, String artifactId) - - MavenModule module(String groupId, String artifactId, String version) -} 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 new file mode 100644 index 000000000..798a06bf7 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt @@ -0,0 +1,20 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import java.io.File + +class AppendableJar(private val file: File) { + private val contents = mutableMapOf() + + fun insertFile(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) + } + builder.build() + return file + } +} 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 new file mode 100644 index 000000000..492a8a526 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt @@ -0,0 +1,54 @@ +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 new file mode 100644 index 000000000..821a0953b --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt @@ -0,0 +1,7 @@ +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 new file mode 100644 index 000000000..d4c81cabb --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt @@ -0,0 +1,42 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import java.io.OutputStream +import java.util.jar.JarEntry +import java.util.jar.JarOutputStream + +class JarBuilder(os: OutputStream) { + private val entries = mutableListOf() + private val jos = JarOutputStream(os) + + private fun addDirectory(name: String) { + if (!entries.contains(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) + } + } + + fun withFile(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) } + } + return this + } + + fun build() { + jos.close() + } +} 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 new file mode 100644 index 000000000..51d740e5d --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt @@ -0,0 +1,63 @@ +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.math.BigInteger + +abstract class AbstractModule { + + protected abstract fun onPublish(file: File) + + 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) } + } + + 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") + if (hashAfter == hashBefore) { + // Already published + return + } + + check(!file.exists() || file.delete()) + check(tempFile.renameTo(file)) + onPublish(file) + } + + companion object { + @JvmStatic + fun sha1File(file: File): File { + return hashFile(file, "sha1", 40) + } + + @JvmStatic + fun md5File(file: File): File { + return hashFile(file, "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 getHashFile(file: File, algorithm: String): File { + return File(file.parentFile, "${file.name}.$algorithm") + } + + private fun getHash(file: File, algorithm: String): BigInteger { + return HashUtil.createHash(file, algorithm.uppercase()).digest + } + } +} 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 new file mode 100644 index 000000000..b77d25886 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt @@ -0,0 +1,22 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo.maven + +import java.io.File + +interface MavenModule { + /** + * Publishes the `pom.xml` plus main artifact, plus any additional artifacts for this module. + * Publishes only those artifacts whose content has changed since the last call to `publish()`. + */ + fun publish(): MavenModule + + /** + * Publishes the `pom.xml` only + */ + fun publishPom(): MavenModule + + fun dependsOn(groupId: String, artifactId: String, version: String): MavenModule + + val pomFile: File + + val metaDataFile: File +} 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 new file mode 100644 index 000000000..a128e07f5 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt @@ -0,0 +1,14 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo.maven + +import java.net.URI + +/** + * A fixture for dealing with Maven repositories. + */ +interface MavenRepository { + val uri: URI + + fun module(groupId: String, artifactId: String): MavenModule + + fun module(groupId: String, artifactId: String, version: String): MavenModule +}