Skip to content
Open
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 @@ -19,7 +19,7 @@

package org.ossreviewtoolkit.plugins.packagemanagers.gradleinspector

import OrtDependency
import OrtComponent
Comment thread
sschuberth marked this conversation as resolved.

import java.lang.invoke.MethodHandles

Expand Down Expand Up @@ -58,16 +58,18 @@ import org.ossreviewtoolkit.utils.spdxexpression.SpdxOperator
internal class GradleDependencyHandler(
/** The type of projects to handle. */
private val projectType: String
) : DependencyHandler<OrtDependency> {
override fun identifierFor(dependency: OrtDependency): Identifier =
with(dependency) { Identifier(getIdentifierType(projectType), groupId, artifactId, version) }
) : DependencyHandler<OrtComponent> {
override fun identifierFor(dependency: OrtComponent): Identifier =
with(dependency.componentId) {
Identifier(dependency.getIdentifierType(projectType), groupId, artifactId, version)
}

override fun dependenciesFor(dependency: OrtDependency): List<OrtDependency> = dependency.dependencies
override fun dependenciesFor(dependency: OrtComponent): List<OrtComponent> = dependency.dependencies

override fun linkageFor(dependency: OrtDependency): PackageLinkage =
override fun linkageFor(dependency: OrtComponent): PackageLinkage =
if (dependency.isProjectDependency) PackageLinkage.PROJECT_DYNAMIC else PackageLinkage.DYNAMIC

override fun createPackage(dependency: OrtDependency, issues: MutableCollection<Issue>): Package? {
override fun createPackage(dependency: OrtComponent, issues: MutableCollection<Issue>): Package? {
// Only look for a package if there was no error resolving the dependency and it is no project dependency.
if (dependency.error != null || dependency.isProjectDependency) return null

Expand Down Expand Up @@ -136,7 +138,7 @@ internal class GradleDependencyHandler(
)
}

override fun areDependenciesEqual(dependenciesA: List<OrtDependency>, dependenciesB: List<OrtDependency>): Boolean {
override fun areDependenciesEqual(dependenciesA: List<OrtComponent>, dependenciesB: List<OrtComponent>): Boolean {
if (dependenciesA === dependenciesB) return true
if (dependenciesA.isEmpty() && dependenciesB.isEmpty()) return true

Expand Down Expand Up @@ -167,7 +169,7 @@ private val USER_HOST_REGEX = Regex("scm:(?<user>[^:@]+)@(?<host>[^:]+)[:/](?<pa

private val logger = loggerOf(MethodHandles.lookup().lookupClass())

private fun OrtDependency.toVcsInfo(): VcsInfo =
private fun OrtComponent.toVcsInfo(): VcsInfo =
mavenModel?.vcs?.run {
@Suppress("UnsafeCallOnNullableType")
SCM_REGEX.matchEntire(connection)?.let { match ->
Expand All @@ -178,7 +180,7 @@ private fun OrtDependency.toVcsInfo(): VcsInfo =
} ?: handleInvalidScmInfo(connection, tag)
}.orEmpty()

private fun OrtDependency.handleValidScmInfo(type: String, url: String, tag: String): VcsInfo =
private fun OrtComponent.handleValidScmInfo(type: String, url: String, tag: String): VcsInfo =
when {
// Maven does not officially support git-repo as an SCM, see http://maven.apache.org/scm/scms-overview.html, so
// come up with the convention to use the "manifest" query parameter for the path to the manifest inside the
Expand Down Expand Up @@ -208,8 +210,10 @@ private fun OrtDependency.handleValidScmInfo(type: String, url: String, tag: Str

// Try to detect the Maven SCM provider from the URL only, e.g. by looking at the host or special URL paths.
VcsHost.parseUrl(fixedUrl).copy(revision = tag).also {
logger.info {
"Fixed up invalid SCM connection without a provider in '$groupId:$artifactId:$version' to $it."
with(componentId) {
logger.info {
"Fixed up invalid SCM connection without a provider in '$groupId:$artifactId:$version' to $it."
}
}
}
}
Expand All @@ -229,7 +233,7 @@ private fun OrtDependency.handleValidScmInfo(type: String, url: String, tag: Str
}
}

private fun OrtDependency.handleInvalidScmInfo(connection: String, tag: String): VcsInfo =
private fun OrtComponent.handleInvalidScmInfo(connection: String, tag: String): VcsInfo =
@Suppress("UnsafeCallOnNullableType")
USER_HOST_REGEX.matchEntire(connection)?.let { match ->
// Some projects omit the provider and use the SCP-like Git URL syntax, for example
Expand All @@ -244,7 +248,7 @@ private fun OrtDependency.handleInvalidScmInfo(connection: String, tag: String):
VcsInfo.EMPTY
}
} ?: run {
val dep = "$groupId:$artifactId:$version"
val dep = with(componentId) { "$groupId:$artifactId:$version" }

if (connection.startsWith("git://") || connection.endsWith(".git")) {
// It is a common mistake to omit the "scm:[provider]:" prefix. Add fall-backs for nevertheless clear
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@

package org.ossreviewtoolkit.plugins.packagemanagers.gradlemodel

import OrtDependency
import OrtComponent

/**
* The type of this Gradle dependency. In case of a project, it is [projectType]. Otherwise it is "Maven" unless there
* is no POM, then it is "Unknown".
*/
fun OrtDependency.getIdentifierType(projectType: String) =
fun OrtComponent.getIdentifierType(projectType: String) =
when {
isProjectDependency -> projectType
pomFile != null -> "Maven"
Expand All @@ -35,5 +35,5 @@ fun OrtDependency.getIdentifierType(projectType: String) =
/**
* A flag to indicate whether this Gradle dependency refers to a project, or to a package.
*/
val OrtDependency.isProjectDependency: Boolean
val OrtComponent.isProjectDependency: Boolean
get() = localPath != null
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,21 @@ interface OrtDependencyTreeModel {

interface OrtConfiguration {
val name: String
val dependencies: List<OrtDependency>
val dependencies: List<OrtComponent>
}

interface OrtDependency {
interface OrtComponentIdentifier {
val groupId: String
val artifactId: String
val version: String
}

interface OrtComponent {
val componentId: OrtComponentIdentifier
val classifier: String
val extension: String
val variants: Map<String, Map<String, String>>
val dependencies: List<OrtDependency>
val dependencies: List<OrtComponent>
val error: String?
val warning: String?
val pomFile: String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

package org.ossreviewtoolkit.plugins.packagemanagers.gradleplugin

import OrtDependency
import OrtComponent
import OrtDependencyTreeModel

import org.apache.maven.model.building.FileModelSource
Expand Down Expand Up @@ -54,10 +54,10 @@ internal class OrtModelBuilder : ToolingModelBuilder {
private val logger = Logging.getLogger(OrtModelBuilder::class.java)
private val errors = mutableListOf<String>()
private val warnings = mutableListOf<String>()
private val globalDependencySubtrees = mutableMapOf<String, List<OrtDependency>>()
private val globalDependencySubtrees = mutableMapOf<String, List<OrtComponent>>()

// Only create one "OrtDependency" for each "ResolvedComponentResult".
private val ortDependencyCache = mutableMapOf<ResolvedComponentResult, OrtDependency>()
// Only create one "OrtComponent" for each "ResolvedComponentResult".
private val ortComponentCache = mutableMapOf<ResolvedComponentResult, OrtComponent>()

override fun canBuild(modelName: String): Boolean = modelName == OrtDependencyTreeModel::class.java.name

Expand Down Expand Up @@ -88,7 +88,7 @@ internal class OrtModelBuilder : ToolingModelBuilder {

// Omit configurations without dependencies.
root.dependencies.takeUnless { it.isEmpty() }?.let { dep ->
OrtConfigurationImpl(name = config.name, dependencies = dep.toOrtDependencies(poms, emptySet()))
OrtConfigurationImpl(name = config.name, dependencies = dep.toOrtComponents(poms, emptySet()))
}
}

Expand All @@ -103,63 +103,22 @@ internal class OrtModelBuilder : ToolingModelBuilder {
)
}

/**
* Resolve the POM files for all dependences in the given [Gradle configuration][config] incl. their parent POMs.
*/
private fun Project.resolvePoms(config: Configuration): Map<String, ModelBuildingResult> {
val allComponentIds = config.incoming.resolutionResult.allDependencies
.filterIsInstance<ResolvedDependencyResult>()
.map { it.selected.id }
.distinct()

// Get the POM files for all resolved dependencies.
val pomFiles = resolvePoms(allComponentIds)

val fileModelBuilder = FileModelBuilder { groupId, artifactId, version ->
val moduleId = DefaultModuleIdentifier.newId(groupId, artifactId)
val componentId = DefaultModuleComponentIdentifier.newId(moduleId, version)

val pomFile = resolvePoms(listOf(componentId)).single().file

FileModelSource(pomFile)
}

return pomFiles.associate {
// Trigger resolution of parent POMs by building the POM model.
it.id.componentIdentifier.toString() to fileModelBuilder.buildModel(it.file)
}
}

/**
* Resolve the POM files for the given [componentIds] and return them.
*/
private fun Project.resolvePoms(componentIds: List<ComponentIdentifier>): List<ResolvedArtifactResult> {
val resolutionResult = dependencies.createArtifactResolutionQuery()
.forComponents(componentIds)
.withArtifacts(MavenModule::class.java, MavenPomArtifact::class.java)
.execute()

return resolutionResult.resolvedComponents.flatMap {
it.getArtifacts(MavenPomArtifact::class.java)
}.filterIsInstance<ResolvedArtifactResult>()
}

private fun Collection<DependencyResult>.toOrtDependencies(
private fun Collection<DependencyResult>.toOrtComponents(
poms: Map<String, ModelBuildingResult>,
visited: Set<ComponentIdentifier>
): List<OrtDependency> =
): List<OrtComponent> =
if (GradleVersion.current() < GradleVersion.version("5.1")) {
this
} else {
filterNot { it.isConstraint }
}.mapNotNull {
it.toOrtDependency(poms, visited)
it.toOrtComponent(poms, visited)
}

private fun DependencyResult.toOrtDependency(
private fun DependencyResult.toOrtComponent(
poms: Map<String, ModelBuildingResult>,
visited: Set<ComponentIdentifier>
): OrtDependency? {
): OrtComponent? {
if (this is UnresolvedDependencyResult) {
if (attempted is ProjectComponentSelector) {
// Ignore unresolved project dependencies. For example for complex Android projects, Gradle's
Expand Down Expand Up @@ -199,8 +158,8 @@ internal class OrtModelBuilder : ToolingModelBuilder {
// Cut the graph on cyclic dependencies.
if (id in visited) return null

if (selected in ortDependencyCache) {
return ortDependencyCache[selected]
if (selected in ortComponentCache) {
return ortComponentCache[selected]
}

if (id is ModuleComponentIdentifier) {
Expand All @@ -215,13 +174,11 @@ internal class OrtModelBuilder : ToolingModelBuilder {

// Check if we have scanned the dependencies of this subtree before, and if so, reuse them.
val dependencies = globalDependencySubtrees.getOrPut(id.displayName) {
selected.dependencies.toOrtDependencies(poms, visited + id)
selected.dependencies.toOrtComponents(poms, visited + id)
}

return OrtDependencyImpl(
groupId = id.group,
artifactId = id.module,
version = id.version,
return OrtComponentImpl(
componentId = OrtComponentIdentifierImpl(id.group, id.module, id.version),
classifier = "",
extension = modelBuildingResult?.effectiveModel?.packaging.orEmpty(),
variants = selected.variants.associate {
Expand All @@ -244,18 +201,20 @@ internal class OrtModelBuilder : ToolingModelBuilder {
},
localPath = null
).also {
ortDependencyCache[selected] = it
ortComponentCache[selected] = it
}
}

if (id is ProjectComponentIdentifier) {
val moduleId = selected.moduleVersion ?: return null
val dependencies = selected.dependencies.toOrtDependencies(poms, visited + id)

return OrtDependencyImpl(
groupId = moduleId.group,
artifactId = moduleId.name,
version = moduleId.version.takeUnless { it == "unspecified" }.orEmpty(),
val dependencies = selected.dependencies.toOrtComponents(poms, visited + id)

return OrtComponentImpl(
componentId = OrtComponentIdentifierImpl(
groupId = moduleId.group,
artifactId = moduleId.name,
version = moduleId.version.takeUnless { it == "unspecified" }.orEmpty()
),
classifier = "",
extension = "",
variants = selected.variants.associate {
Expand All @@ -270,7 +229,7 @@ internal class OrtModelBuilder : ToolingModelBuilder {
mavenModel = null,
localPath = id.projectPath
).also {
ortDependencyCache[selected] = it
ortComponentCache[selected] = it
}
}

Expand Down Expand Up @@ -316,6 +275,44 @@ internal class OrtModelBuilder : ToolingModelBuilder {
}
}

/**
* Resolve the POM files for all dependences in the given [Gradle configuration][config] incl. their parent POMs.
*/
private fun Project.resolvePoms(config: Configuration): Map<String, ModelBuildingResult> {
val allComponentIds = config.incoming.resolutionResult.allComponents.map { it.id }
Comment thread
sschuberth marked this conversation as resolved.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit message nit: You don't refer to [1] currently, but should do so after "getAllComponents()". And maybe swap [2] and [1] to refer to them from the text in order.


// Get the POM files for all resolved dependencies.
val pomFiles = resolvePoms(allComponentIds)

val fileModelBuilder = FileModelBuilder { groupId, artifactId, version ->
val moduleId = DefaultModuleIdentifier.newId(groupId, artifactId)
val componentId = DefaultModuleComponentIdentifier.newId(moduleId, version)

val pomFile = resolvePoms(listOf(componentId)).single().file

FileModelSource(pomFile)
}

return pomFiles.associate {
// Trigger resolution of parent POMs by building the POM model.
it.id.componentIdentifier.toString() to fileModelBuilder.buildModel(it.file)
}
}

/**
* Resolve the POM files for the given [componentIds] and return them.
*/
private fun Project.resolvePoms(componentIds: List<ComponentIdentifier>): List<ResolvedArtifactResult> {
val resolutionResult = dependencies.createArtifactResolutionQuery()
.forComponents(componentIds.distinct())
.withArtifacts(MavenModule::class.java, MavenPomArtifact::class.java)
.execute()

return resolutionResult.resolvedComponents.flatMap {
it.getArtifacts(MavenPomArtifact::class.java)
}.filterIsInstance<ResolvedArtifactResult>()
}

/**
* Add a string with information about the causes of the given [exception] to this [StringBuilder]. This is used to
* log the reason why a dependency could not be resolved. To get meaningful information, all causes need to be obtained
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@

package org.ossreviewtoolkit.plugins.packagemanagers.gradleplugin

import OrtComponent
import OrtComponentIdentifier
import OrtConfiguration
import OrtDependency
import OrtDependencyTreeModel
import OrtMavenModel
import OrtRepository
Expand All @@ -42,24 +43,29 @@ internal class OrtDependencyTreeModelImpl(
@Suppress("SerialVersionUIDInSerializableClass")
internal class OrtConfigurationImpl(
override val name: String,
override val dependencies: List<OrtDependency>
override val dependencies: List<OrtComponent>
) : OrtConfiguration, Serializable

@Suppress("LongParameterList", "SerialVersionUIDInSerializableClass")
internal class OrtDependencyImpl(
internal class OrtComponentIdentifierImpl(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does "LongParameterList" really need to be suppressed here? It's now only 3 parameters.

override val groupId: String,
override val artifactId: String,
override val version: String,
override val version: String
) : OrtComponentIdentifier, Serializable

@Suppress("LongParameterList", "SerialVersionUIDInSerializableClass")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this became shorter now, but I guess "LongParameterList" is still needed here?

internal class OrtComponentImpl(
override val componentId: OrtComponentIdentifier,
override val classifier: String,
override val extension: String,
override val variants: Map<String, Map<String, String>>,
override val dependencies: List<OrtDependency>,
override val dependencies: List<OrtComponent>,
override val error: String?,
override val warning: String?,
override val pomFile: String?,
override val mavenModel: OrtMavenModel?,
override val localPath: String?
) : OrtDependency, Serializable
) : OrtComponent, Serializable

@Suppress("SerialVersionUIDInSerializableClass")
internal class OrtMavenModelImpl(
Expand Down
Loading
Loading