main.managers.utils.PackageManagerDependencyHandler.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of analyzer Show documentation
Show all versions of analyzer Show documentation
Part of the OSS Review Toolkit (ORT), a suite to automate software compliance checks.
/*
* Copyright (C) 2022 The ORT Project Authors (see )
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/
package org.ossreviewtoolkit.analyzer.managers.utils
import kotlin.contracts.contract
import org.apache.logging.log4j.kotlin.logger
import org.ossreviewtoolkit.model.AnalyzerResult
import org.ossreviewtoolkit.model.DependencyGraphNavigator
import org.ossreviewtoolkit.model.DependencyNode
import org.ossreviewtoolkit.model.Identifier
import org.ossreviewtoolkit.model.Issue
import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.model.PackageLinkage
import org.ossreviewtoolkit.model.PackageReference
import org.ossreviewtoolkit.model.Project
import org.ossreviewtoolkit.model.utils.DependencyHandler
private const val TYPE = "PackageManagerDependency"
class PackageManagerDependencyHandler(
private val analyzerResult: AnalyzerResult
) : DependencyHandler {
companion object {
/**
* Create a [PackageReference] that points to the result of a [scope] in the [definitionFile] from another
* package manager with the provided [linkage]. The analyzer will replace this reference with the dependency
* tree of the provided scope after all package managers have finished.
*/
fun createPackageManagerDependency(
packageManager: String,
definitionFile: String,
scope: String,
linkage: PackageLinkage
): PackageReference =
PackageReference(
id = Identifier(
type = TYPE,
namespace = packageManager,
name = definitionFile.encodeColon(),
version = "$linkage@$scope"
)
)
private fun getPackageManagerDependency(node: DependencyNode): PackageManagerDependency? =
node.id.type.takeIf { it == TYPE }?.let {
PackageManagerDependency(
packageManager = node.id.namespace,
definitionFile = node.id.name.decodeColon(),
scope = node.id.version.substringAfter('@'),
linkage = PackageLinkage.valueOf(node.id.version.substringBefore('@'))
)
}
}
private val navigator = DependencyGraphNavigator(analyzerResult.dependencyGraphs)
override fun createPackage(dependency: ResolvableDependencyNode, issues: MutableCollection): Package? =
analyzerResult.packages.find { it.id == dependency.id }
override fun dependenciesFor(dependency: ResolvableDependencyNode): List =
buildList {
dependency.visitDependencies { dependencies ->
dependencies.forEach { node ->
addAll(resolvePackageManagerDependency(node))
}
}
}
override fun identifierFor(dependency: ResolvableDependencyNode): Identifier = dependency.id
override fun issuesForDependency(dependency: ResolvableDependencyNode): List = dependency.issues
override fun linkageFor(dependency: ResolvableDependencyNode): PackageLinkage = dependency.linkage
fun resolvePackageManagerDependency(dependency: DependencyNode): List =
getPackageManagerDependency(dependency)?.let { packageManagerDependency ->
packageManagerDependency.findProjects(analyzerResult).map { project ->
val dependencies = navigator.directDependencies(project, packageManagerDependency.scope)
ProjectScopeDependencyNode(
id = project.id,
linkage = packageManagerDependency.linkage,
issues = emptyList(),
dependencies = dependencies.map { it.getStableReference() }
)
}
} ?: listOf(DependencyNodeDelegate(dependency.getStableReference()))
}
private data class PackageManagerDependency(
val packageManager: String,
val definitionFile: String,
val scope: String,
val linkage: PackageLinkage
) {
fun findProjects(analyzerResult: AnalyzerResult): List =
analyzerResult.projects.filter { it.definitionFilePath == definitionFile }.also { projects ->
if (projects.isEmpty()) {
logger.warn { "Could not find any project for definition file '$definitionFile'." }
}
projects.forEach { verify(it) }
}
fun verify(project: Project?) {
contract {
returns() implies (project != null)
}
requireNotNull(project) {
"Could not find a project for the definition file '$definitionFile'."
}
require(project.id.type == packageManager) {
"The project '${project.id.toCoordinates()}' from definition file '$definitionFile' uses the wrong " +
"package manager '${project.id.type}', expected is '$packageManager'."
}
requireNotNull(project.scopeNames) {
"The project '${project.id.toCoordinates()}' from definition file '$definitionFile' does not use a " +
"dependency graph."
}
if (scope !in project.scopeNames.orEmpty()) {
logger.warn {
"The project '${project.id.toCoordinates()}' from definition file '$definitionFile' does not contain " +
"the requested scope '$scope'."
}
}
}
}
sealed class ResolvableDependencyNode : DependencyNode
class ProjectScopeDependencyNode(
override val id: Identifier,
override val linkage: PackageLinkage,
override val issues: List,
private val dependencies: Sequence
) : ResolvableDependencyNode() {
override fun visitDependencies(block: (Sequence) -> T): T = block(dependencies)
}
class DependencyNodeDelegate(private val node: DependencyNode) : ResolvableDependencyNode() {
override val id: Identifier = node.id
override val linkage = node.linkage
override val issues = node.issues
override fun visitDependencies(block: (Sequence) -> T): T = node.visitDependencies(block)
}
private fun String.encodeColon() = replace(':', '\u0000')
private fun String.decodeColon() = replace('\u0000', ':')
© 2015 - 2025 Weber Informatics LLC | Privacy Policy