Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/changes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class JavaPluginsTest : BasePluginTest() {
.appendText(
"""
sourceSets {
custom
create('custom')
}
dependencies {
implementation sourceSets.custom.output
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<List<File>> {
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<File>,
Expand Down Expand Up @@ -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<File>()
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<File>) {
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"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<File>() }
}

@get:InputFiles
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down