Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Include dependency substitutions in dependency extractors #31

Closed
wants to merge 3 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public data class CoreOptions @JvmOverloads constructor(

internal val gradleArgs: List<String> = buildList {
addAll(customGradleFlags)
add("-PuseIncludeBuild=$useIncludeBuild")
if (autoInjectPlugin) {
add("-I")
add(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ SONATYPE_STAGING_PROFILE=com.squareup

SONATYPE_HOST=S01
RELEASE_SIGNING_ENABLED=true
SONATYPE_AUTOMATIC_RELEASE=true
SONATYPE_AUTOMATIC_RELEASE=true
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
rainecp marked this conversation as resolved.
Show resolved Hide resolved
}
assertTrue("No dependencies should be listed") { filteredDeps.isEmpty() }
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Dependency> {
return configurationNames.asSequence().flatMap { configurationName ->
getByName(configurationName).allDependencies.asSequence()
}
): Sequence<SquareDependency> {
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<Dependency> {
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<SquareDependency> {
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) {
Expand Down Expand Up @@ -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()) {
Expand All @@ -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(':', '/')
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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")))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
)
}

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down