diff --git a/model/src/main/kotlin/CompatibilityDependencyNavigator.kt b/model/src/main/kotlin/CompatibilityDependencyNavigator.kt index 3ee5ede1d66ba..c926b119bd9ec 100644 --- a/model/src/main/kotlin/CompatibilityDependencyNavigator.kt +++ b/model/src/main/kotlin/CompatibilityDependencyNavigator.kt @@ -68,12 +68,12 @@ class CompatibilityDependencyNavigator internal constructor( override fun directDependencies(project: Project, scopeName: String): Sequence = project.invokeNavigator { directDependencies(it, scopeName) } - override fun dependenciesForScope( + override fun scopeDependencies( project: Project, scopeName: String, maxDepth: Int, matcher: DependencyMatcher - ): Set = project.invokeNavigator { dependenciesForScope(it, scopeName, maxDepth, matcher) } + ): Set = project.invokeNavigator { scopeDependencies(it, scopeName, maxDepth, matcher) } override fun packageDependencies( project: Project, diff --git a/model/src/main/kotlin/DependencyGraphNavigator.kt b/model/src/main/kotlin/DependencyGraphNavigator.kt index c450927a276ae..eb64e6f5f851f 100644 --- a/model/src/main/kotlin/DependencyGraphNavigator.kt +++ b/model/src/main/kotlin/DependencyGraphNavigator.kt @@ -74,7 +74,7 @@ class DependencyGraphNavigator( return dependenciesSequence(graph, rootDependencies) } - override fun dependenciesForScope( + override fun scopeDependencies( project: Project, scopeName: String, maxDepth: Int, diff --git a/model/src/main/kotlin/DependencyNavigator.kt b/model/src/main/kotlin/DependencyNavigator.kt index a304ebec5deef..b6db1e30b8e37 100644 --- a/model/src/main/kotlin/DependencyNavigator.kt +++ b/model/src/main/kotlin/DependencyNavigator.kt @@ -48,17 +48,10 @@ interface DependencyNavigator { /** * A pre-defined [DependencyMatcher] that matches only dependencies with a linkage indicating subprojects. */ - private val MATCH_SUB_PROJECTS: DependencyMatcher = { node -> + val MATCH_SUB_PROJECTS: DependencyMatcher = { node -> node.linkage in PackageLinkage.PROJECT_LINKAGE } - /** - * Return a set with all the [Identifier]s contained in the given map of scope dependencies. This is useful if - * all dependencies are needed independent of the scope they belong to. - */ - private fun Map>.collectDependencies(): Set = - values.flatMapTo(mutableSetOf()) { it } - /** * Return a map with all [Issue]s found in the dependency graph spawned by [dependencies] grouped by their * [Identifier]s. @@ -94,11 +87,11 @@ interface DependencyNavigator { fun directDependencies(project: Project, scopeName: String): Sequence /** - * Return a set with information of the dependencies of a specific [scope][scopeName] of a [project]. With - * [maxDepth] the depth of the dependency tree to be traversed can be restricted; negative values mean that there - * is no restriction. Use the specified [matcher] to filter for specific dependencies. + * Return a set with the [Identifier]s of the dependencies of a [scope][scopeName] of a [project]. With [maxDepth] + * the depth of the dependency tree to be traversed can be restricted; negative values mean that there is no + * restriction. Use the specified [matcher] to filter for specific dependencies. */ - fun dependenciesForScope( + fun scopeDependencies( project: Project, scopeName: String, maxDepth: Int = -1, @@ -106,16 +99,15 @@ interface DependencyNavigator { ): Set /** - * Return a map with information of the dependencies of a [project] grouped by scopes. This is equivalent to - * calling [dependenciesForScope] for all the scopes of the given [project]. (This base implementation does - * exactly this.) + * Return a set with the [Identifier]s of the dependencies of a [project]. This is equivalent to calling + * [scopeDependencies] for all the scopes of the given [project]. */ - fun scopeDependencies( + fun projectDependencies( project: Project, maxDepth: Int = -1, matcher: DependencyMatcher = MATCH_ALL - ): Map> = - scopeNames(project).associateWith { dependenciesForScope(project, it, maxDepth, matcher) } + ): Set = + scopeNames(project).flatMapTo(mutableSetOf()) { scopeDependencies(project, it, maxDepth, matcher) } /** * Return a set with the [Identifier]s of packages that are dependencies of the package with the given [packageId] @@ -146,22 +138,6 @@ interface DependencyNavigator { return pathMap } - /** - * Return the set of [Identifier]s that refer to subprojects of the given [project]. - */ - fun collectSubProjects(project: Project): Set = - scopeDependencies(project, matcher = MATCH_SUB_PROJECTS).collectDependencies() - - /** - * Return a set with the [Identifier]s of all dependencies of the given [project] across all scopes. As usual, - * it is possible to restrict the dependencies to be fetched with [maxDepth] and [matcher]. - */ - fun projectDependencies( - project: Project, - maxDepth: Int = -1, - matcher: DependencyMatcher = MATCH_ALL - ): Set = scopeDependencies(project, maxDepth, matcher).collectDependencies() - /** * Return the depth of the dependency tree rooted at the given [project] associated with this [scopeName]. If the * scope cannot be resolved, return -1. @@ -177,10 +153,10 @@ interface DependencyNavigator { /** * Determine the map of the shortest paths for all the dependencies of a [project], given its map of - * [scopeDependencies]. + * [projectDependencies]. */ private fun getShortestPathForScope(project: Project, scope: String): Map> = - getShortestPathsForScope(directDependencies(project, scope), dependenciesForScope(project, scope)) + getShortestPathsForScope(directDependencies(project, scope), scopeDependencies(project, scope)) /** * Determine the map of the shortest paths for a specific scope given its direct dependency [nodes] and a set with diff --git a/model/src/main/kotlin/DependencyTreeNavigator.kt b/model/src/main/kotlin/DependencyTreeNavigator.kt index 01ad65043c944..350b971b1f5d8 100644 --- a/model/src/main/kotlin/DependencyTreeNavigator.kt +++ b/model/src/main/kotlin/DependencyTreeNavigator.kt @@ -29,22 +29,16 @@ object DependencyTreeNavigator : DependencyNavigator { override fun directDependencies(project: Project, scopeName: String): Sequence = project.findScope(scopeName)?.dependencies?.asSequence().orEmpty() - override fun dependenciesForScope( + override fun scopeDependencies( project: Project, scopeName: String, maxDepth: Int, matcher: DependencyMatcher ): Set = project.findScope(scopeName)?.collectDependencies(maxDepth) { matcher(it) }.orEmpty() - override fun scopeDependencies( - project: Project, - maxDepth: Int, - matcher: DependencyMatcher - ): Map> = + override fun projectDependencies(project: Project, maxDepth: Int, matcher: DependencyMatcher): Set = // Override the base implementation because a more efficient access to single scopes is possible. - project.scopes.associate { scope -> - scope.name to scope.collectDependencies(maxDepth, matcher.forReference()) - } + project.scopes.flatMapTo(mutableSetOf()) { it.collectDependencies(maxDepth, matcher.forReference()) } override fun packageDependencies( project: Project, diff --git a/model/src/main/kotlin/OrtResult.kt b/model/src/main/kotlin/OrtResult.kt index 3dbf167f0d85a..fafc52dad43e0 100644 --- a/model/src/main/kotlin/OrtResult.kt +++ b/model/src/main/kotlin/OrtResult.kt @@ -26,6 +26,7 @@ import java.lang.invoke.MethodHandles import org.apache.logging.log4j.kotlin.loggerOf +import org.ossreviewtoolkit.model.DependencyNavigator.Companion.MATCH_SUB_PROJECTS import org.ossreviewtoolkit.model.ResolvedPackageCurations.Companion.REPOSITORY_CONFIGURATION_PROVIDER_ID import org.ossreviewtoolkit.model.config.Excludes import org.ossreviewtoolkit.model.config.IssueResolution @@ -129,12 +130,14 @@ data class OrtResult( val includedDependencies = mutableSetOf() projects.forEach { project -> - dependencyNavigator.scopeDependencies(project).forEach { (scopeName, dependencies) -> - val isScopeExcluded = getExcludes().isScopeExcluded(scopeName) - allDependencies += dependencies + dependencyNavigator.scopeNames(project).forEach { scopeName -> + dependencyNavigator.scopeDependencies(project, scopeName).forEach { dependencies -> + val isScopeExcluded = getExcludes().isScopeExcluded(scopeName) + allDependencies += dependencies - if (!isProjectExcluded(project.id) && !isScopeExcluded) { - includedDependencies += dependencies + if (!isProjectExcluded(project.id) && !isScopeExcluded) { + includedDependencies += dependencies + } } } } @@ -434,7 +437,7 @@ data class OrtResult( if (!includeSubProjects) { val subProjectIds = projects.flatMapTo(mutableSetOf()) { - dependencyNavigator.collectSubProjects(it) + dependencyNavigator.projectDependencies(it, matcher = MATCH_SUB_PROJECTS) } projects.removeAll { it.id in subProjectIds } diff --git a/model/src/test/kotlin/AbstractDependencyNavigatorTest.kt b/model/src/test/kotlin/AbstractDependencyNavigatorTest.kt index d09cb03fae350..3ca635491fb87 100644 --- a/model/src/test/kotlin/AbstractDependencyNavigatorTest.kt +++ b/model/src/test/kotlin/AbstractDependencyNavigatorTest.kt @@ -27,6 +27,7 @@ import io.kotest.matchers.collections.containExactly import io.kotest.matchers.collections.containExactlyInAnyOrder import io.kotest.matchers.collections.haveSize import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder +import io.kotest.matchers.collections.shouldNotContain import io.kotest.matchers.maps.containExactly as containExactlyEntries import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.sequences.beEmpty as beEmptySequence @@ -39,6 +40,7 @@ import io.kotest.matchers.shouldNotBe import java.io.File import java.time.Instant +import org.ossreviewtoolkit.model.DependencyNavigator.Companion.MATCH_SUB_PROJECTS import org.ossreviewtoolkit.utils.test.readOrtResult /** @@ -85,75 +87,88 @@ abstract class AbstractDependencyNavigatorTest : WordSpec() { } } - "scopeDependencies" should { - "return a map with scopes and their dependencies for a project" { - val scopeDependencies = navigator.scopeDependencies(testProject) - - scopeDependencies.keys should containExactlyInAnyOrder("compile", "test") - - scopeDependencies["compile"] shouldNotBeNull { - this should haveSize(17) - this should containAll( - Identifier("Maven:com.typesafe.akka:akka-actor_2.12:2.5.6"), - Identifier("Maven:org.scala-lang:scala-reflect:2.12.2"), - Identifier("Maven:org.scala-lang:scala-library:2.12.15") - ) - } + "projectDependencies" should { + "return all dependencies from all scopes for a project" { + val scopeDependencies = navigator.projectDependencies(testProject) - scopeDependencies["test"] shouldNotBeNull { - this should haveSize(6) - this should containAll( - Identifier("Maven:org.scalacheck:scalacheck_2.12:1.13.5"), - Identifier("Maven:org.scalactic:scalactic_2.12:3.0.4") - ) - } + scopeDependencies should containAll( + Identifier("Maven:com.typesafe.akka:akka-actor_2.12:2.5.6"), + Identifier("Maven:org.scala-lang:scala-reflect:2.12.2"), + Identifier("Maven:org.scala-lang:scala-library:2.12.15"), + Identifier("Maven:org.scalacheck:scalacheck_2.12:1.13.5"), + Identifier("Maven:org.scalactic:scalactic_2.12:3.0.4") + ) } - "return a map with scopes and their direct dependencies by using maxDepth = 1" { - val scopeDependencies = navigator.scopeDependencies(testProject, maxDepth = 1) + "return direct dependencies from all scopes by using maxDepth = 1" { + val scopeDependencies = navigator.projectDependencies(testProject, maxDepth = 1) - scopeDependencies["compile"] shouldNotBeNull { - this should haveSize(7) - this shouldNot contain(Identifier("Maven:com.typesafe.akka:akka-actor_2.12:2.5.6")) - } + scopeDependencies should haveSize(9) + scopeDependencies shouldNotContain Identifier("Maven:com.typesafe.akka:akka-actor_2.12:2.5.6") } - "return a map with scopes and their dependencies up to a given maxDepth" { - val scopeDependencies = navigator.scopeDependencies(testProject, maxDepth = 2) + "return all dependencies from all scopes up to a given maxDepth" { + val scopeDependencies = navigator.projectDependencies(testProject, maxDepth = 2) - scopeDependencies["compile"] shouldNotBeNull { - this should haveSize(14) - this shouldNot contain( - Identifier("Maven:org.scala-lang.modules:scala-java8-compat_2.12:0.8.0") - ) - } + scopeDependencies should haveSize(20) + scopeDependencies shouldNotContain + Identifier("Maven:org.scala-lang.modules:scala-java8-compat_2.12:0.8.0") } - "return a map with scopes and their dependencies with filter criteria" { + "return dependencies from all scopes that match a filter criteria" { val matchedIds = mutableSetOf() - val scopeDependencies = navigator.scopeDependencies(testProject) { node -> + val scopeDependencies = navigator.projectDependencies(testProject) { node -> matchedIds += node.id node.id.namespace == "com.typesafe.akka" } - scopeDependencies["compile"] shouldNotBeNull { - this should containExactlyInAnyOrder( - Identifier("Maven:com.typesafe.akka:akka-actor_2.12:2.5.6"), - Identifier("Maven:com.typesafe.akka:akka-stream_2.12:2.5.6") - ) + matchedIds should haveSize(23) + scopeDependencies should containExactlyInAnyOrder( + Identifier("Maven:com.typesafe.akka:akka-actor_2.12:2.5.6"), + Identifier("Maven:com.typesafe.akka:akka-stream_2.12:2.5.6") + ) + } + + "return the combination of scope dependencies of a project" { + val projectDependencies = navigator.projectDependencies(testProject) + val expectedDependencies = navigator.scopeDependencies(testProject, "compile") + + navigator.scopeDependencies(testProject, "test") + + projectDependencies should containExactlyInAnyOrder(expectedDependencies) + } + + "support filtering the dependencies of a project" { + val dependencies = navigator.projectDependencies(testProject, maxDepth = 1) { + it.linkage != PackageLinkage.PROJECT_DYNAMIC } - matchedIds should haveSize(23) + dependencies should containExactlyInAnyOrder( + Identifier("Maven:ch.qos.logback:logback-classic:1.2.3"), + Identifier("Maven:com.typesafe:config:1.3.1"), + Identifier("Maven:com.typesafe.akka:akka-stream_2.12:2.5.6"), + Identifier("Maven:com.typesafe.scala-logging:scala-logging_2.12:3.7.2"), + Identifier("Maven:net.logstash.logback:logstash-logback-encoder:4.11"), + Identifier("Maven:org.scala-lang:scala-library:2.12.15"), + Identifier("Maven:org.slf4j:jcl-over-slf4j:1.7.25"), + Identifier("Maven:org.scalacheck:scalacheck_2.12:1.13.5"), + Identifier("Maven:org.scalatest:scalatest_2.12:3.0.4") + ) + } + + "return no dependencies for a maxDepth of 0" { + val dependencies = navigator.projectDependencies(testProject, 0) + + dependencies should beEmpty() } } - "dependenciesForScope" should { + "scopeDependencies" should { "return an empty set for an unknown scope" { - navigator.dependenciesForScope(testProject, "unknownScope") should beEmpty() + navigator.scopeDependencies(testProject, "unknownScope") should beEmpty() } "return the dependencies of a specific scope" { - val compileDependencies = navigator.dependenciesForScope(testProject, "compile") + val compileDependencies = navigator.scopeDependencies(testProject, "compile") compileDependencies should haveSize(17) compileDependencies should containAll( @@ -164,7 +179,7 @@ abstract class AbstractDependencyNavigatorTest : WordSpec() { } "return the dependencies of a specific scope up to a given maxDepth" { - val compileDependencies = navigator.dependenciesForScope(testProject, "compile", maxDepth = 2) + val compileDependencies = navigator.scopeDependencies(testProject, "compile", maxDepth = 2) compileDependencies should haveSize(14) compileDependencies shouldNot contain( @@ -173,7 +188,7 @@ abstract class AbstractDependencyNavigatorTest : WordSpec() { } "return the dependencies of a specific scope with filter criteria" { - val akkaDependencies = navigator.dependenciesForScope(testProject, "compile") { node -> + val akkaDependencies = navigator.scopeDependencies(testProject, "compile") { node -> "akka" in node.id.namespace } @@ -230,6 +245,15 @@ abstract class AbstractDependencyNavigatorTest : WordSpec() { Identifier("Maven:com.typesafe.akka:akka-actor_2.12:2.5.6") ) } + + "find all the sub projects of a project" { + val projectId = Identifier("SBT:com.pbassiner:multi1_2.12:0.1.0-SNAPSHOT") + testResult.getProject(projectId) shouldNotBeNull { + val subProjectIds = navigator.projectDependencies(this, matcher = MATCH_SUB_PROJECTS) + + subProjectIds should containExactly(PROJECT_ID) + } + } } "getShortestPaths" should { @@ -285,52 +309,6 @@ abstract class AbstractDependencyNavigatorTest : WordSpec() { } } - "projectDependencies" should { - "return the dependencies of a project" { - val scopeDependencies = navigator.scopeDependencies(testProject) - val projectDependencies = navigator.projectDependencies(testProject) - - scopeDependencies.keys should containExactlyInAnyOrder("compile", "test") - val expectedDependencies = scopeDependencies.getValue("compile") + scopeDependencies.getValue("test") - projectDependencies should containExactlyInAnyOrder(expectedDependencies) - } - - "support filtering the dependencies of a project" { - val dependencies = navigator.projectDependencies(testProject, 1) { - it.linkage != PackageLinkage.PROJECT_DYNAMIC - } - - dependencies should containExactlyInAnyOrder( - Identifier("Maven:ch.qos.logback:logback-classic:1.2.3"), - Identifier("Maven:com.typesafe:config:1.3.1"), - Identifier("Maven:com.typesafe.akka:akka-stream_2.12:2.5.6"), - Identifier("Maven:com.typesafe.scala-logging:scala-logging_2.12:3.7.2"), - Identifier("Maven:net.logstash.logback:logstash-logback-encoder:4.11"), - Identifier("Maven:org.scala-lang:scala-library:2.12.15"), - Identifier("Maven:org.slf4j:jcl-over-slf4j:1.7.25"), - Identifier("Maven:org.scalacheck:scalacheck_2.12:1.13.5"), - Identifier("Maven:org.scalatest:scalatest_2.12:3.0.4") - ) - } - - "return no dependencies for a maxDepth of 0" { - val dependencies = navigator.projectDependencies(testProject, 0) - - dependencies should beEmpty() - } - } - - "collectSubProjects" should { - "find all the sub projects of a project" { - val projectId = Identifier("SBT:com.pbassiner:multi1_2.12:0.1.0-SNAPSHOT") - testResult.getProject(projectId) shouldNotBeNull { - val subProjectIds = navigator.collectSubProjects(this) - - subProjectIds should containExactly(PROJECT_ID) - } - } - } - "dependencyTreeDepth" should { "calculate the dependency tree depth for a project" { navigator.dependencyTreeDepth(testProject, "compile") shouldBe 3 diff --git a/model/src/test/kotlin/CompatibilityDependencyNavigatorTest.kt b/model/src/test/kotlin/CompatibilityDependencyNavigatorTest.kt index eaac5f117afda..2278ad016c9fd 100644 --- a/model/src/test/kotlin/CompatibilityDependencyNavigatorTest.kt +++ b/model/src/test/kotlin/CompatibilityDependencyNavigatorTest.kt @@ -150,7 +150,7 @@ class CompatibilityDependencyNavigatorTest : WordSpec() { val treeNavigator = mockk() val graphNavigator = mockk() every { - treeNavigator.dependenciesForScope( + treeNavigator.scopeDependencies( treeProject, scopeName, maxDepth, @@ -160,7 +160,7 @@ class CompatibilityDependencyNavigatorTest : WordSpec() { val navigator = CompatibilityDependencyNavigator(graphNavigator, treeNavigator) - navigator.dependenciesForScope(treeProject, scopeName, maxDepth, matcher) shouldBe dependencies + navigator.scopeDependencies(treeProject, scopeName, maxDepth, matcher) shouldBe dependencies } "return the dependencies of a dependency graph project" { @@ -171,7 +171,7 @@ class CompatibilityDependencyNavigatorTest : WordSpec() { val treeNavigator = mockk() val graphNavigator = mockk() every { - graphNavigator.dependenciesForScope( + graphNavigator.scopeDependencies( graphProject, scopeName, maxDepth, @@ -181,7 +181,7 @@ class CompatibilityDependencyNavigatorTest : WordSpec() { val navigator = CompatibilityDependencyNavigator(graphNavigator, treeNavigator) - navigator.dependenciesForScope(graphProject, scopeName, maxDepth, matcher) shouldBe dependencies + navigator.scopeDependencies(graphProject, scopeName, maxDepth, matcher) shouldBe dependencies } } diff --git a/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelMapper.kt b/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelMapper.kt index bafe32c889a76..5c1360b38aef1 100644 --- a/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelMapper.kt +++ b/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelMapper.kt @@ -187,17 +187,19 @@ internal class EvaluatedModelMapper(private val input: ReporterInput) { } } - input.ortResult.dependencyNavigator.scopeDependencies(project).forEach { (scopeName, dependencies) -> + input.ortResult.dependencyNavigator.scopeNames(project).forEach { scopeName -> + val scopeDependencies = input.ortResult.dependencyNavigator.scopeDependencies(project, scopeName) val scopeExcludes = input.ortResult.getExcludes().findScopeExcludes(scopeName) + if (scopeExcludes.isNotEmpty()) { - dependencies.forEach { id -> + scopeDependencies.forEach { id -> val info = packageExcludeInfo.getOrPut(id) { PackageExcludeInfo(id, true) } if (info.isExcluded) { info.scopeExcludes += scopeExcludes } } } else if (pathExcludes.isEmpty()) { - dependencies.forEach { id -> + scopeDependencies.forEach { id -> val info = packageExcludeInfo.getOrPut(id) { PackageExcludeInfo(id, true) } info.isExcluded = false info.pathExcludes.clear() diff --git a/plugins/reporters/static-html/src/main/kotlin/TablesReportModelMapper.kt b/plugins/reporters/static-html/src/main/kotlin/TablesReportModelMapper.kt index bcb13dc0c0b7a..abab130df40db 100644 --- a/plugins/reporters/static-html/src/main/kotlin/TablesReportModelMapper.kt +++ b/plugins/reporters/static-html/src/main/kotlin/TablesReportModelMapper.kt @@ -72,8 +72,8 @@ private fun OrtResult.getScopesForDependencies(project: Project): Map>>() val excludes = getExcludes() - dependencyNavigator.scopeDependencies(project).forEach { (scopeName, dependencies) -> - dependencies.forEach { dependency -> + dependencyNavigator.scopeNames(project).forEach { scopeName -> + dependencyNavigator.scopeDependencies(project, scopeName).forEach { dependency -> result.getOrPut(dependency) { mutableMapOf() } .getOrPut(scopeName) { excludes.findScopeExcludes(scopeName) } }