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
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class MultiProjectDependencyExtractorTest extends BaseExtractorTest {
}
}

def "extracts transitive project dependencies in multi-project build"() {
def "extracts transitive project dependencies in multi-project build with #description"() {
given:
settingsFile << "include 'a', 'b', 'c'"
buildFile << """
Expand All @@ -89,53 +89,33 @@ class MultiProjectDependencyExtractorTest extends BaseExtractorTest {
apply plugin: 'java'
dependencies {
implementation project(':b')
implementation 'org.test:bar:1.0'
}
}
"""

when:
run()
run(task)

then:
manifestNames == ["project :a", "project :b", "project :c"]
manifestNames == ["project :a", "project :c"]

def manifestA = gitHubManifest("project :a")
manifestA.sourceFile == "a/build.gradle"
manifestA.assertResolved([
"org.test:foo:1.0": [package_url: purlFor(foo)]
])

def manifestB = gitHubManifest("project :b")
manifestB.sourceFile == "b/build.gradle"
manifestB.assertResolved([
"project :a" : [
package_url : "pkg:maven/org.test/a@1.0",
dependencies: ["org.test:foo:1.0"]
],
"org.test:foo:1.0": [
package_url : purlFor(foo),
relationship: "indirect"
]
])

def manifestC = gitHubManifest("project :c")
manifestC.sourceFile == "c/build.gradle"
manifestC.assertResolved([
"project :b" : [
package_url : "pkg:maven/org.test/b@1.0",
dependencies: ["project :a"]
],
"project :a" : [
package_url : "pkg:maven/org.test/a@1.0",
relationship: "indirect",
dependencies: ["org.test:foo:1.0"]
],
"org.test:foo:1.0": [
package_url : purlFor(foo),
relationship: "indirect"
]

"org.test:bar:1.0": [package_url: purlFor(bar)]
])

where:
task | description
"GitHubDependencyGraphPlugin_generateDependencyGraph" | "All dependencies resolved"
":c:dependencies" | "One project resolved"
}

def "extracts dependencies from buildSrc project"() {
Expand Down Expand Up @@ -200,6 +180,7 @@ class MultiProjectDependencyExtractorTest extends BaseExtractorTest {
apply plugin: 'java'
dependencies {
implementation 'org.test.included:included-child'
implementation 'org.test:bar:1.0'
}
"""

Expand All @@ -212,14 +193,7 @@ class MultiProjectDependencyExtractorTest extends BaseExtractorTest {
def projectManifest = gitHubManifest("project :")
projectManifest.sourceFile == "build.gradle"
projectManifest.assertResolved([
"project :included-child": [
package_url : "pkg:maven/org.test.included/included-child@1.0",
dependencies: ["org.test:foo:1.0"]
],
"org.test:foo:1.0" : [
package_url : purlFor(foo),
relationship: "indirect"
]
"org.test:bar:1.0": [package_url: purlFor(bar)]
])

def includedManifest = gitHubManifest("project :included-child")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,8 @@ class PluginDependencyExtractorTest extends BaseExtractorTest {
run()

then:
manifestNames == ["build :", "project :", "project :buildSrc", "project :buildSrc:a"]
manifestNames == ["build :", "project :buildSrc", "project :buildSrc:a"]
manifestHasSettingsPlugin("build :")
manifestIsEmpty("project :")
manifestHasPlugin1("project :buildSrc")
manifestHasPlugin2("project :buildSrc:a")
}
Expand All @@ -72,9 +71,8 @@ class PluginDependencyExtractorTest extends BaseExtractorTest {
run()

then:
manifestNames == ["build :", "project :", "project :included-child", "project :included-child:a"]
manifestNames == ["build :", "project :included-child", "project :included-child:a"]
manifestHasSettingsPlugin("build :")
manifestIsEmpty("project :")
manifestHasPlugin1("project :included-child")
manifestHasPlugin2("project :included-child:a")
}
Expand All @@ -94,9 +92,8 @@ class PluginDependencyExtractorTest extends BaseExtractorTest {
run()

then:
manifestNames == ["build :", "project :", "project :included-plugin", "project :included-plugin:a"]
manifestNames == ["build :", "project :included-plugin", "project :included-plugin:a"]
manifestHasSettingsPlugin("build :")
manifestIsEmpty("project :")
manifestHasPlugin1("project :included-plugin")
manifestHasPlugin2("project :included-plugin:a")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ class SingleProjectDependencyExtractorTest extends BaseExtractorTest {
run()

then:
manifestNames == ["build :", "project :"]
manifestNames == ["build :"]
def settingsManifest = gitHubManifest("build :")
settingsManifest.sourceFile == "build.gradle"

Expand Down Expand Up @@ -412,7 +412,7 @@ class SingleProjectDependencyExtractorTest extends BaseExtractorTest {
run()

then:
manifestNames == ["build :", "project :"]
manifestNames == ["build :"]
def buildManifest = gitHubManifest("build :")
buildManifest.sourceFile == "build.gradle"
buildManifest.assertResolved([
Expand All @@ -425,9 +425,5 @@ class SingleProjectDependencyExtractorTest extends BaseExtractorTest {
relationship: "indirect"
]
])

def manifest = gitHubManifest("project :")
manifest.sourceFile == "build.gradle"
manifest.assertResolved([:])
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.gradle.github.dependencygraph.internal

import org.gradle.github.dependencygraph.internal.json.GitHubManifestFile
import org.gradle.github.dependencygraph.internal.model.ResolvedConfiguration
import org.gradle.github.dependencygraph.internal.model.ResolutionRoot
import java.nio.file.Path
import java.nio.file.Paths
import java.util.concurrent.ConcurrentHashMap
Expand All @@ -22,7 +22,7 @@ class BuildLayout(val gitWorkspaceDirectory: Path) {
}
}

fun getBuildFile(resolvedConfiguration: ResolvedConfiguration): GitHubManifestFile? {
return buildFileForProject(resolvedConfiguration.identityPath)
fun getBuildFile(rootComponent: ResolutionRoot): GitHubManifestFile? {
return buildFileForProject(rootComponent.path)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier
import org.gradle.api.internal.artifacts.configurations.ResolveConfigurationDependenciesBuildOperationType
import org.gradle.github.GitHubDependencyGraphPlugin
import org.gradle.github.dependencygraph.internal.model.ComponentCoordinates
import org.gradle.github.dependencygraph.internal.model.ResolutionRoot
import org.gradle.github.dependencygraph.internal.model.ResolvedComponent
import org.gradle.github.dependencygraph.internal.model.ResolvedConfiguration
import org.gradle.internal.exceptions.DefaultMultiCauseException
Expand Down Expand Up @@ -116,44 +117,63 @@ abstract class DependencyExtractor :
// It is possible to do better. By tracking the current build operation context, we can assign more precisely.
// See the Gradle Enterprise Build Scan Plugin: `ConfigurationResolutionCapturer_5_0`
val rootPath = projectIdentityPath ?: details.buildPath
val rootId = if (projectIdentityPath == null) "build $rootPath" else "project $rootPath"
val resolvedConfiguration = ResolvedConfiguration(rootId, rootPath)
val rootId = if (projectIdentityPath == null) "build $rootPath" else componentId(rootComponent)
val resolutionRoot = ResolutionRoot(rootId, rootPath)
val resolvedConfiguration = ResolvedConfiguration(resolutionRoot)

for (directDependency in getResolvedDependencies(rootComponent)) {
val directDep = createComponentNode(componentId(directDependency), true, directDependency, repositoryLookup)
val directDep = createComponentNode(
componentId(directDependency),
resolutionRoot,
true,
directDependency,
repositoryLookup
)
resolvedConfiguration.addDependency(directDep)

walkComponentDependencies(directDependency, repositoryLookup, resolvedConfiguration)
walkComponentDependencies(directDependency, resolutionRoot, repositoryLookup, resolvedConfiguration)
}

resolvedConfigurations.add(resolvedConfiguration)
}

private fun walkComponentDependencies(
component: ResolvedComponentResult,
resolveContext: ResolutionRoot,
repositoryLookup: RepositoryUrlLookup,
resolvedConfiguration: ResolvedConfiguration
) {
val rootComponent = getResolutionRoot(component, resolveContext)
val direct = rootComponent != resolveContext

val dependencyComponents = getResolvedDependencies(component)
for (dependencyComponent in dependencyComponents) {
val dependencyId = componentId(dependencyComponent)
if (!resolvedConfiguration.hasDependency(dependencyId)) {
val dependencyNode = createComponentNode(dependencyId, false, dependencyComponent, repositoryLookup)
val dependencyNode = createComponentNode(dependencyId, rootComponent, direct, dependencyComponent, repositoryLookup)
resolvedConfiguration.addDependency(dependencyNode)

walkComponentDependencies(dependencyComponent, repositoryLookup, resolvedConfiguration)
walkComponentDependencies(dependencyComponent, rootComponent, repositoryLookup, resolvedConfiguration)
}
}
}

private fun getResolutionRoot(component: ResolvedComponentResult, resolveContext: ResolutionRoot): ResolutionRoot {
val componentId = component.id
if (componentId is DefaultProjectComponentIdentifier) {
return ResolutionRoot(componentId(component), componentId.identityPath.path)
}
return resolveContext
}

private fun getResolvedDependencies(component: ResolvedComponentResult): List<ResolvedComponentResult> {
return component.dependencies.filterIsInstance<ResolvedDependencyResult>().map { it.selected }.filter { it != component }
}

private fun createComponentNode(componentId: String, direct: Boolean, component: ResolvedComponentResult, repositoryLookup: RepositoryUrlLookup): ResolvedComponent {
private fun createComponentNode(componentId: String, rootComponent: ResolutionRoot, direct: Boolean, component: ResolvedComponentResult, repositoryLookup: RepositoryUrlLookup): ResolvedComponent {
val componentDependencies = component.dependencies.filterIsInstance<ResolvedDependencyResult>().map { componentId(it.selected) }
val repositoryUrl = repositoryLookup.doLookup(component)
return ResolvedComponent(componentId, direct, coordinates(component), repositoryUrl, componentDependencies)
return ResolvedComponent(componentId, rootComponent, direct, coordinates(component), repositoryUrl, componentDependencies)
}

private fun componentId(component: ResolvedComponentResult): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.github.packageurl.PackageURLBuilder
import org.gradle.github.dependencygraph.internal.model.ResolvedComponent
import org.gradle.github.dependencygraph.internal.model.ResolvedConfiguration
import org.gradle.github.dependencygraph.internal.json.*
import org.gradle.github.dependencygraph.internal.model.ResolutionRoot

private const val DEFAULT_MAVEN_REPOSITORY_URL = "https://repo.maven.apache.org/maven2"

Expand All @@ -25,25 +26,25 @@ class GitHubRepositorySnapshotBuilder(

fun build(resolvedConfigurations: MutableList<ResolvedConfiguration>, buildLayout: BuildLayout): GitHubRepositorySnapshot {
val manifestDependencies = mutableMapOf<String, DependencyCollector>()
val manifestFiles = mutableMapOf<String, GitHubManifestFile?>()

for (resolutionRoot in resolvedConfigurations) {
val manifestName = manifestName(resolutionRoot)
val dependencyCollector = manifestDependencies.getOrPut(manifestName) { DependencyCollector() }
for (component in resolutionRoot.allDependencies) {
dependencyCollector.addResolved(component)
}
for (dependency in resolutionRoot.allDependencies) {
// Ignore project dependencies (transitive deps of projects will be reported with project)
if (isProject(dependency)) continue

// If not assigned to a project, assume the root project for the assigned build.
manifestFiles.putIfAbsent(manifestName, buildLayout.getBuildFile(resolutionRoot))
val rootComponent = dependency.rootComponent
val dependencyCollector = manifestDependencies.getOrPut(rootComponent.id) { DependencyCollector(rootComponent) }
dependencyCollector.addResolved(dependency)
}
}

val manifests = manifestDependencies.mapValues { (name, collector) ->
val manifestFile = buildLayout.getBuildFile(collector.rootComponent)

GitHubManifest(
name,
collector.getDependencies(),
manifestFiles[name]
manifestFile
)
}
return GitHubRepositorySnapshot(
Expand All @@ -55,11 +56,12 @@ class GitHubRepositorySnapshotBuilder(
)
}

private fun manifestName(root: ResolvedConfiguration): String {
return root.id
// TODO:DAZ Model this better
private fun isProject(dependency: ResolvedComponent): Boolean {
return dependency.id.startsWith("project ")
}

private class DependencyCollector() {
private class DependencyCollector(val rootComponent: ResolutionRoot) {
private val dependencyBuilders: MutableMap<String, GitHubDependencyBuilder> = mutableMapOf()

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.gradle.github.dependencygraph.internal.model

/**
* A root component in dependency resolution, this represents a component that "owns" or declares
* dependencies within a given resolution.
* In most cases, this will be the project component that declares the dependency, but for certain resolutions
* this component will represent an overall build.
*/
data class ResolutionRoot(val id: String, val path: String)
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
package org.gradle.github.dependencygraph.internal.model

data class ResolvedComponent(val id: String, val direct: Boolean, val coordinates: ComponentCoordinates, val repositoryUrl: String?, val dependencies: List<String>)
data class ResolvedComponent(
val id: String,
val rootComponent: ResolutionRoot,
val direct: Boolean,
val coordinates: ComponentCoordinates,
val repositoryUrl: String?,
val dependencies: List<String>
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package org.gradle.github.dependencygraph.internal.model

data class ResolvedConfiguration(val id: String,
val identityPath: String,
val allDependencies: MutableList<ResolvedComponent> = mutableListOf()) {
data class ResolvedConfiguration(
val rootComponent: ResolutionRoot,
val allDependencies: MutableList<ResolvedComponent> = mutableListOf()
) {
fun addDependency(component: ResolvedComponent) {
allDependencies.add(component)
}
Expand Down