diff --git a/affected-paths/core/src/main/kotlin/com/squareup/affected/paths/core/CoreOptions.kt b/affected-paths/core/src/main/kotlin/com/squareup/affected/paths/core/CoreOptions.kt index 00fc629..3ccbe89 100644 --- a/affected-paths/core/src/main/kotlin/com/squareup/affected/paths/core/CoreOptions.kt +++ b/affected-paths/core/src/main/kotlin/com/squareup/affected/paths/core/CoreOptions.kt @@ -95,6 +95,7 @@ public data class CoreOptions @JvmOverloads constructor( internal val gradleArgs: List = buildList { addAll(customGradleFlags) + add("-PuseIncludeBuild=$useIncludeBuild") if (autoInjectPlugin) { add("-I") add( diff --git a/affected-paths/core/src/test/kotlin/com/squareup/affected/paths/core/git/AffectedPathsTest.kt b/affected-paths/core/src/test/kotlin/com/squareup/affected/paths/core/git/AffectedPathsTest.kt index 0ffcfca..1648b0d 100644 --- a/affected-paths/core/src/test/kotlin/com/squareup/affected/paths/core/git/AffectedPathsTest.kt +++ b/affected-paths/core/src/test/kotlin/com/squareup/affected/paths/core/git/AffectedPathsTest.kt @@ -292,7 +292,10 @@ class AffectedPathsTest { val result = analyzer.analyze() assertContentEquals(listOf("build2/foobar", "app", "library"), result.projectMap.keys) - assertContentEquals(listOf("build2/foobar"), result.affectedResults.flatMap { it.affectedProjectPaths }.distinct()) + assertContentEquals( + listOf("app", "app:debug:debugUnitTest", "build2/foobar", "app:release:releaseUnitTest"), + result.affectedResults.flatMap { it.affectedProjectPaths }.distinct() + ) } @Test diff --git a/gradle.properties b/gradle.properties index 4237d4f..3ef2b55 100644 --- a/gradle.properties +++ b/gradle.properties @@ -29,4 +29,4 @@ SONATYPE_STAGING_PROFILE=com.squareup SONATYPE_HOST=S01 RELEASE_SIGNING_ENABLED=true -SONATYPE_AUTOMATIC_RELEASE=true +SONATYPE_AUTOMATIC_RELEASE=true \ No newline at end of file diff --git a/tooling/support/android/src/main/kotlin/com/squareup/tooling/support/android/VariantExtractor.kt b/tooling/support/android/src/main/kotlin/com/squareup/tooling/support/android/VariantExtractor.kt index 438269a..fa8851c 100644 --- a/tooling/support/android/src/main/kotlin/com/squareup/tooling/support/android/VariantExtractor.kt +++ b/tooling/support/android/src/main/kotlin/com/squareup/tooling/support/android/VariantExtractor.kt @@ -19,8 +19,7 @@ package com.squareup.tooling.support.android import com.android.build.gradle.api.BaseVariant import com.squareup.tooling.models.SquareDependency -import com.squareup.tooling.support.core.extractors.extractDependencies -import com.squareup.tooling.support.core.extractors.extractSquareDependency +import com.squareup.tooling.support.core.extractors.extractSquareDependencies import org.gradle.api.Project import java.io.File @@ -77,8 +76,7 @@ internal fun BaseVariant.extractSquareVariantConfigurationParams( addAll(compileConfiguration.extendsFrom.map { it.name }) }.toTypedArray() - val deps = project.configurations.extractDependencies(*configNames) - .map { it.extractSquareDependency(project) } + val deps = project.configurations.extractSquareDependencies(project, *configNames) return srcs to deps.toSet() } diff --git a/tooling/support/android/src/test/kotlin/com/squareup/tooling/support/android/VariantExtractorsTest.kt b/tooling/support/android/src/test/kotlin/com/squareup/tooling/support/android/VariantExtractorsTest.kt index 5cd55a0..28f1e2a 100644 --- a/tooling/support/android/src/test/kotlin/com/squareup/tooling/support/android/VariantExtractorsTest.kt +++ b/tooling/support/android/src/test/kotlin/com/squareup/tooling/support/android/VariantExtractorsTest.kt @@ -53,7 +53,11 @@ class VariantExtractorsTest { "All source paths must be relative to the project dir" ) assertEquals(24, srcs.size, "Sources were missing") - assertTrue("No dependencies should be listed") { deps.isEmpty() } + // Filter out the kotlin-stdlib-jdk8 dependency + val filteredDeps = deps.filterNot { + it.target.contains("kotlin-stdlib") + } + assertTrue("No dependencies should be listed") { filteredDeps.isEmpty() } } @Test diff --git a/tooling/support/core/src/main/kotlin/com/squareup/tooling/support/core/extractors/DependencyExtractors.kt b/tooling/support/core/src/main/kotlin/com/squareup/tooling/support/core/extractors/DependencyExtractors.kt index c703ea2..302a85b 100644 --- a/tooling/support/core/src/main/kotlin/com/squareup/tooling/support/core/extractors/DependencyExtractors.kt +++ b/tooling/support/core/src/main/kotlin/com/squareup/tooling/support/core/extractors/DependencyExtractors.kt @@ -20,21 +20,77 @@ package com.squareup.tooling.support.core.extractors import com.squareup.tooling.models.SquareDependency import com.squareup.tooling.support.core.models.SquareDependency import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.artifacts.result.ResolvedDependencyResult +import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier import org.gradle.api.internal.artifacts.dependencies.AbstractExternalModuleDependency import org.gradle.api.internal.artifacts.dependencies.AbstractModuleDependency -// Extracts Gradle Dependency objects from the given configurations -public fun ConfigurationContainer.extractDependencies( +public fun ConfigurationContainer.extractSquareDependencies( + project: Project, vararg configurationNames: String -): Sequence { - return configurationNames.asSequence().flatMap { configurationName -> - getByName(configurationName).allDependencies.asSequence() - } +): Sequence { + val useIncludeBuild = project.findProperty("useIncludeBuild") == "true" + return configurationNames.asSequence() + .map { configurationName -> getByName(configurationName) } + .flatMap { configuration -> + sequence { + yieldAll(configuration.extractDependencies() + .map { it.extractSquareDependency(project) }) + if (useIncludeBuild) { + yieldAll(configuration.extractResolvedProjectDependencies()) + } + } + } +} + +/** + * Extracts Gradle Dependency objects from the given configurations + */ +public fun Configuration.extractDependencies(): Sequence { + return allDependencies.asSequence() +} + +/** + * Extracts project dependencies from the resolved artifacts of the configuration. + * + * In cases where an included build is used (e.g., through `includeBuild`), project dependencies + * may not appear in the standard `Configuration.allDependencies` list. Instead, they are + * resolved during the configuration resolution process. + * + * It only works if the configuration can be resolved (`isCanBeResolved` is true), which excludes + * configurations like `compileOnly`. + */ +public fun Configuration.extractResolvedProjectDependencies(): Sequence { + if (!isCanBeResolved) return emptySequence() + + val resolutionResult = incoming.resolutionResult + val allDependencies = resolutionResult.allDependencies + val directDependencies = resolutionResult.root.dependencies.toSet() + + return allDependencies.asSequence() + .mapNotNull { it as? ResolvedDependencyResult } + .mapNotNull { resolvedDependencyResult -> + val identifier = resolvedDependencyResult.selected.id as? DefaultProjectComponentIdentifier + identifier?.let { + if (it.identityPath.path != ":") { + val path = gradlePathToFilePath(it.identityPath.path) + val isTransitive = resolvedDependencyResult !in directDependencies + SquareDependency( + target = path, + tags = if (isTransitive) setOf("transitive") else emptySet() + ) + } else { + null + } + } + } } + // Converts the given Gradle Dependency into a SquareDependency use to construct the model project public fun Dependency.extractSquareDependency(project: Project): SquareDependency { return when (this) { @@ -71,7 +127,7 @@ public fun Dependency.extractSquareDependency(project: Project): SquareDependenc // Meant to de-duplicate project name from target string. private fun Dependency.keyRelativeTo(relative: String = ""): String { if (this is ProjectDependency) { - return dependencyProject.path.replace(':', '/') + return gradlePathToFilePath(dependencyProject.path) } val s = group?.split(".", ":", "/") ?: emptyList() val result = when (s.firstOrNull()) { @@ -80,3 +136,7 @@ private fun Dependency.keyRelativeTo(relative: String = ""): String { } return result.plus(name).joinToString("") { "/$it" } } + +private fun gradlePathToFilePath(path: String): String { + return path.replace(':', '/') +} diff --git a/tooling/support/core/src/test/kotlin/com/squareup/tooling/support/core/DependencyExtractorsTests.kt b/tooling/support/core/src/test/kotlin/com/squareup/tooling/support/core/DependencyExtractorsTests.kt index 841f283..4918cad 100644 --- a/tooling/support/core/src/test/kotlin/com/squareup/tooling/support/core/DependencyExtractorsTests.kt +++ b/tooling/support/core/src/test/kotlin/com/squareup/tooling/support/core/DependencyExtractorsTests.kt @@ -18,7 +18,9 @@ package com.squareup.tooling.support.core import com.squareup.tooling.support.core.extractors.extractDependencies +import com.squareup.tooling.support.core.extractors.extractResolvedProjectDependencies import com.squareup.tooling.support.core.extractors.extractSquareDependency +import com.squareup.tooling.support.core.models.SquareDependency import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.Test import kotlin.test.assertEquals @@ -34,7 +36,7 @@ class DependencyExtractorsTests { project.dependencies.add("testConfig", "com.squareup:foo") project.dependencies.add("testConfig", "com.squareup:bar") - val result = project.configurations.extractDependencies("testConfig").toList() + val result = project.configurations.getByName("testConfig").extractDependencies().toList() // Test assertEquals(2, result.size) @@ -106,4 +108,35 @@ class DependencyExtractorsTests { return@assertTrue result.target == "/squareTest" } } + + @Test + fun `test extractResolvedProjectDependencies() with empty project dependencies`() { + // Setup + val project = ProjectBuilder.builder().build() + project.configurations.create("testConfig") + + // Test + val configuration = project.configurations.getByName("testConfig") + + val result = configuration.extractResolvedProjectDependencies() + assertTrue(result.none(), "The result should be an empty sequence") + } + + @Test + fun `test extractResolvedProjectDependencies() with resolved project dependencies`() { + // Setup + val project = ProjectBuilder.builder().build() + project.configurations.create("testConfig") + val projectDependency = ProjectBuilder.builder().withName("squareTest").withParent(project).build() + projectDependency.configurations.create("default") + project.dependencies.add("testConfig", projectDependency) + + // Test + val configuration = project.configurations.getByName("testConfig") + + val result = configuration.extractResolvedProjectDependencies() + assertEquals(1, result.count()) + assertTrue(result.contains(SquareDependency(target = "/squareTest"))) + } + } diff --git a/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/KotlinExtractorSupport.kt b/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/KotlinExtractorSupport.kt index 566cda6..e33121e 100644 --- a/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/KotlinExtractorSupport.kt +++ b/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/KotlinExtractorSupport.kt @@ -19,8 +19,7 @@ package com.squareup.tooling.support.jvm import com.squareup.tooling.models.SquareDependency import com.squareup.tooling.models.SquareTestConfiguration -import com.squareup.tooling.support.core.extractors.extractDependencies -import com.squareup.tooling.support.core.extractors.extractSquareDependency +import com.squareup.tooling.support.core.extractors.extractSquareDependencies import com.squareup.tooling.support.core.models.SquareTestConfiguration import org.gradle.api.Project import org.gradle.api.tasks.SourceSet @@ -33,10 +32,11 @@ internal fun KotlinSourceSet.extractSquareTestConfiguration( ): SquareTestConfiguration { return SquareTestConfiguration( srcs = kotlin.sourceDirectories.map { it.toRelativeString(project.projectDir) }.toSet(), - deps = project.configurations.extractDependencies( + deps = project.configurations.extractSquareDependencies( + project, implementationMetadataConfigurationName, compileOnlyMetadataConfigurationName - ).map { it.extractSquareDependency(project) }.toSet() + ).toSet() ) } @@ -78,8 +78,7 @@ internal fun KotlinSourceSet.extractSquareVariantConfigurationParams( ) }.toTypedArray() - val result = project.configurations.extractDependencies(*configNames) - .map { it.extractSquareDependency(project) }.toList() + val result = project.configurations.extractSquareDependencies(project,*configNames).toList() deps.addAll(result) return srcs to deps diff --git a/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/TestVariantExtractor.kt b/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/TestVariantExtractor.kt index b53f78c..0cd7924 100644 --- a/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/TestVariantExtractor.kt +++ b/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/TestVariantExtractor.kt @@ -18,8 +18,7 @@ package com.squareup.tooling.support.jvm import com.squareup.tooling.models.SquareTestConfiguration -import com.squareup.tooling.support.core.extractors.extractDependencies -import com.squareup.tooling.support.core.extractors.extractSquareDependency +import com.squareup.tooling.support.core.extractors.extractSquareDependencies import com.squareup.tooling.support.core.models.SquareTestConfiguration import org.gradle.api.Project import org.gradle.api.tasks.SourceSet @@ -28,7 +27,9 @@ import org.gradle.api.tasks.SourceSet internal fun SourceSet.extractSquareTestConfiguration(project: Project): SquareTestConfiguration { return SquareTestConfiguration( srcs = allSource.sourceDirectories.map { it.toRelativeString(project.projectDir) }.toSet(), - deps = project.configurations.extractDependencies(compileClasspathConfigurationName) - .map { it.extractSquareDependency(project) }.toSet() + deps = project.configurations.extractSquareDependencies( + project, + compileClasspathConfigurationName + ).toSet() ) } diff --git a/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/VariantExtractor.kt b/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/VariantExtractor.kt index ef61e0a..69a3565 100644 --- a/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/VariantExtractor.kt +++ b/tooling/support/jvm/src/main/kotlin/com/squareup/tooling/support/jvm/VariantExtractor.kt @@ -18,8 +18,7 @@ package com.squareup.tooling.support.jvm import com.squareup.tooling.models.SquareDependency -import com.squareup.tooling.support.core.extractors.extractDependencies -import com.squareup.tooling.support.core.extractors.extractSquareDependency +import com.squareup.tooling.support.core.extractors.extractSquareDependencies import org.gradle.api.Project import org.gradle.api.tasks.SourceSet @@ -67,10 +66,7 @@ internal fun SourceSet.extractSquareVariantConfigurationParams( ) }.toTypedArray() - deps.addAll( - project.configurations.extractDependencies(*configNames) - .map { it.extractSquareDependency(project) } - ) + deps.addAll(project.configurations.extractSquareDependencies(project, *configNames)) return srcs to deps } diff --git a/tooling/support/jvm/src/test/kotlin/com/squareup/tooling/support/jvm/VariantExtractorsTest.kt b/tooling/support/jvm/src/test/kotlin/com/squareup/tooling/support/jvm/VariantExtractorsTest.kt index b88bd11..0574b89 100644 --- a/tooling/support/jvm/src/test/kotlin/com/squareup/tooling/support/jvm/VariantExtractorsTest.kt +++ b/tooling/support/jvm/src/test/kotlin/com/squareup/tooling/support/jvm/VariantExtractorsTest.kt @@ -106,7 +106,11 @@ class VariantExtractorsTest { val (srcs, deps) = kotlinSourceSet.extractSquareVariantConfigurationParams(appProject, "main") assertTrue(srcs.containsAll(listOf("src/main/java", "src/main/kotlin"))) - assertTrue("No dependencies should be listed") { deps.isEmpty() } + // Filter out the kotlin-stdlib-jdk8 dependency + val filteredDeps = deps.filterNot { + it.target.contains("kotlin-stdlib") + } + assertTrue("No dependencies should be listed") { filteredDeps.isEmpty() } } @Test diff --git a/tooling/support/src/test/kotlin/com/squareup/tooling/support/builder/SquareProjectModelBuilderTest.kt b/tooling/support/src/test/kotlin/com/squareup/tooling/support/builder/SquareProjectModelBuilderTest.kt index fc76962..754a3f5 100644 --- a/tooling/support/src/test/kotlin/com/squareup/tooling/support/builder/SquareProjectModelBuilderTest.kt +++ b/tooling/support/src/test/kotlin/com/squareup/tooling/support/builder/SquareProjectModelBuilderTest.kt @@ -107,7 +107,7 @@ class SquareProjectModelBuilderTest { ANDROID_SRC_DIRECTORY_PATHS.map { "src/main/$it" } ) } - assertTrue(debugVariant.deps.isEmpty()) + assertTrue(debugVariant.deps.filterNot { it.target.contains("kotlin-stdlib") }.isEmpty()) val releaseVariant = requireNotNull(result.variants["release"]) assertTrue { @@ -116,7 +116,7 @@ class SquareProjectModelBuilderTest { ANDROID_SRC_DIRECTORY_PATHS.map { "src/main/$it" } ) } - assertTrue(releaseVariant.deps.isEmpty()) + assertTrue(releaseVariant.deps.filterNot { it.target.contains("kotlin-stdlib") }.isEmpty()) // Check test variant properties val debugTestVariants = debugVariant.tests @@ -186,7 +186,7 @@ class SquareProjectModelBuilderTest { ANDROID_SRC_DIRECTORY_PATHS.map { "src/main/$it" } ) } - assertTrue(debugVariant.deps.isEmpty()) + assertTrue(debugVariant.deps.filterNot { it.target.contains("kotlin-stdlib") }.isEmpty()) val releaseVariant = requireNotNull(result.variants["release"]) assertTrue { @@ -195,7 +195,7 @@ class SquareProjectModelBuilderTest { ANDROID_SRC_DIRECTORY_PATHS.map { "src/main/$it" } ) } - assertTrue(releaseVariant.deps.isEmpty()) + assertTrue(releaseVariant.deps.filterNot { it.target.contains("kotlin-stdlib") }.isEmpty()) // Check test variant properties val debugTestVariants = debugVariant.tests