diff --git a/docs/changes/README.md b/docs/changes/README.md index 19eec4a8b..5fced3014 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.2...HEAD) - 2026-xx-xx +### Added + +- Support Isolated Projects. ([#1139](https://github.com/GradleUp/shadow/pull/1139)) + ### Changed - Allow opting out of adding `shadowJar` into `assemble` lifecycle. ([#1939](https://github.com/GradleUp/shadow/pull/1939)) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt index b69e5b854..bfc12d07e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -415,9 +415,6 @@ abstract class BasePluginTest { const val runShadowTask = "tasks.named('$SHADOW_RUN_TASK_NAME', JavaExec)" const val jarTask = "tasks.named('jar', Jar)" - // TODO: enable this flag for all tests once we have fixed all issues with isolated projects. - // See https://github.com/GradleUp/shadow/pull/1139. - const val ipArgument = "-Dorg.gradle.unsafe.isolated-projects=true" const val infoArgument = "--info" fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 2273c29f0..3e1b32ab3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -200,7 +200,7 @@ class JavaPluginsTest : BasePluginTest() { .appendText( """ sourceSets { - custom + create('custom') } dependencies { implementation sourceSets.custom.output @@ -871,7 +871,6 @@ class JavaPluginsTest : BasePluginTest() { val result = runWithSuccess( serverShadowJarPath, - ipArgument, infoArgument, "-P${ENABLE_DEVELOCITY_INTEGRATION_PROPERTY}=true", "-Dscan.dump", // Using scan.dump avoids actually publishing a Build Scan, writing it to a diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index e9b09ba44..d7b7bd88b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -2,16 +2,51 @@ package com.github.jengelman.gradle.plugins.shadow.internal import java.io.File import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ExternalModuleDependency -import org.gradle.api.artifacts.FileCollectionDependency -import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.artifacts.component.ModuleComponentIdentifier +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.LibraryElements +import org.gradle.api.attributes.Usage import org.gradle.api.file.FileCollection +import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME +import org.gradle.api.provider.Provider import org.gradle.api.tasks.InputFiles import org.vafer.jdependency.Clazzpath import org.vafer.jdependency.ClazzpathUnit +internal fun Project.getApiJars(): Provider> { + val apiConfiguration = + configurations.findByName(API_CONFIGURATION_NAME) ?: return provider { emptyList() } + + val configName = "shadowMinimizeApi" + val shadowApiConfig = + if (configurations.names.contains(configName)) { + configurations.named(configName) + } else { + configurations.register(configName) { + it.isCanBeResolved = true + it.isCanBeConsumed = false + it.attributes { attrs -> + attrs.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_API)) + attrs.attribute( + Category.CATEGORY_ATTRIBUTE, + objects.named(Category::class.java, Category.LIBRARY), + ) + attrs.attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + objects.named(LibraryElements::class.java, LibraryElements.JAR), + ) + } + it.extendsFrom(apiConfiguration) + } + } + + return shadowApiConfig.flatMap { shadowApi -> + shadowApi.incoming.artifacts.resolvedArtifacts.map { artifacts -> + artifacts.filter { it.id.componentIdentifier !is ModuleComponentIdentifier }.map { it.file } + } + } +} + /** Tracks unused classes in the project classpath. */ internal class UnusedTracker( sourceSetsClassesDirs: Iterable, @@ -41,44 +76,4 @@ internal class UnusedTracker( cp.addClazzpathUnit(jarOrDir) } } - - companion object { - fun getApiJarsFromProject(project: Project): FileCollection { - val apiDependencies = - project.configurations.findByName("api")?.dependencies ?: return project.files() - val runtimeConfiguration = project.runtimeConfiguration - val apiJars = mutableListOf() - apiDependencies.forEach { dep -> - when (dep) { - is ProjectDependency -> { - apiJars.addAll(getApiJarsFromProject(project.project(dep.path))) - addJar(runtimeConfiguration, dep, apiJars) - } - is FileCollectionDependency -> { - apiJars.addAll(dep.files) - } - // Skip BOM dependencies and other non-JAR dependencies. - is ExternalModuleDependency -> Unit - else -> { - addJar(runtimeConfiguration, dep, apiJars) - val jarFile = - runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } ?: return@forEach - apiJars.add(jarFile) - } - } - } - return project.files(apiJars) - } - - private fun addJar(config: Configuration, dep: Dependency, result: MutableList) { - config.find { isProjectDependencyFile(it, dep) }?.let { result.add(it) } - } - - private fun isProjectDependencyFile(file: File, dep: Dependency): Boolean { - val fileName = file.name - val dependencyName = dep.name - return fileName == "$dependencyName.jar" || - (fileName.startsWith("$dependencyName-") && fileName.endsWith(".jar")) - } - } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index a3e65c159..ff4206809 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -8,6 +8,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFil import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.fileCollection +import com.github.jengelman.gradle.plugins.shadow.internal.getApiJars import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.property @@ -108,7 +109,7 @@ public abstract class ShadowJar : Jar() { @get:Classpath public open val apiJars: ConfigurableFileCollection = objectFactory.fileCollection { - minimizeJar.map { if (it) UnusedTracker.getApiJarsFromProject(project) else emptySet() } + minimizeJar.map { if (it) project.getApiJars() else emptySet() } } @get:InputFiles diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt index 3b5707575..2175a5a3d 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt @@ -28,6 +28,8 @@ val commonGradleArgs = "--stacktrace", // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel "-Dorg.gradle.configuration-cache.parallel=true", + // https://docs.gradle.org/current/userguide/isolated_projects.html#how_do_i_use_it + "-Dorg.gradle.unsafe.isolated-projects=true", ) fun gradleRunner(