@@ -119,30 +119,30 @@ class GoMod(
119119 stashDirectories(projectDir.resolve(" vendor" )).use { _ ->
120120 val moduleInfoForModuleName = getModuleInfos(projectDir)
121121 val graph = getModuleGraph(projectDir, moduleInfoForModuleName)
122- val projectId = moduleInfoForModuleName.getMainModuleId()
123- val packageIds = graph.nodes - projectId
124- val packages = packageIds.mapTo(mutableSetOf ()) { moduleInfoForModuleName.getValue(it.name).toPackage() }
125- val projectVcs = processProjectVcs(projectDir)
122+ val packages = graph.nodes.mapNotNullTo(mutableSetOf ()) {
123+ moduleInfoForModuleName.getValue(it.name).toPackage()
124+ }
126125
127- val dependenciesScopePackageIds = getTransitiveMainModuleDependencies(projectDir).let { moduleNames ->
126+ val projectVcs = processProjectVcs(projectDir)
127+ val mainScopeModules = getTransitiveMainModuleDependencies(projectDir).let { moduleNames ->
128128 graph.nodes.filterTo(mutableSetOf ()) { it.name in moduleNames }
129129 }
130130
131131 val scopes = setOf (
132132 Scope (
133133 name = " main" ,
134- dependencies = graph.subgraph(dependenciesScopePackageIds ).toPackageReferenceForest(projectId )
134+ dependencies = graph.subgraph(mainScopeModules ).toPackageReferenceForest(moduleInfoForModuleName )
135135 ),
136136 Scope (
137137 name = " vendor" ,
138- dependencies = graph.toPackageReferenceForest(projectId )
138+ dependencies = graph.toPackageReferenceForest(moduleInfoForModuleName )
139139 )
140140 )
141141
142142 return listOf (
143143 ProjectAnalyzerResult (
144144 project = Project (
145- id = projectId ,
145+ id = moduleInfoForModuleName.values.single { it.main }.toId() ,
146146 definitionFilePath = VersionControlSystem .getPathInfo(definitionFile).path,
147147 authors = emptySet(), // Go mod doesn't support author information.
148148 declaredLicenses = emptySet(), // Go mod doesn't support declared licenses.
@@ -160,16 +160,20 @@ class GoMod(
160160 /* *
161161 * Return the module graph output from `go mod graph` with non-vendor dependencies removed.
162162 */
163- private fun getModuleGraph (projectDir : File , moduleInfoForModuleName : Map <String , ModuleInfo >): Graph <Identifier > {
163+ private fun getModuleGraph (projectDir : File , moduleInfoForModuleName : Map <String , ModuleInfo >): Graph <GoModule > {
164164 fun moduleInfo (moduleName : String ): ModuleInfo = moduleInfoForModuleName.getValue(moduleName)
165165
166- fun parseModuleEntry (entry : String ): Identifier =
167- entry.substringBefore(' @' ).let { moduleName ->
168- moduleInfo(moduleName).toId()
169- }
166+ fun parseModuleEntry (entry : String ): GoModule =
167+ GoModule (
168+ name = entry.substringBefore(' @' ),
169+ version = entry.substringAfter(' @' , " " )
170+ )
171+
172+ val mainModule = moduleInfoForModuleName.values.single { it.main }.run {
173+ GoModule (path, normalizeModuleVersion(version))
174+ }
170175
171- val mainModuleId = moduleInfoForModuleName.getMainModuleId()
172- var graph = Graph <Identifier >().apply { addNode(mainModuleId) }
176+ var graph = Graph <GoModule >().apply { addNode(mainModule) }
173177
174178 val edges = runGo(" mod" , " graph" , workingDir = projectDir)
175179
@@ -199,12 +203,7 @@ class GoMod(
199203 graph.addEdge(parent, child)
200204 }
201205
202- val mainModuleName = moduleInfoForModuleName.getMainModuleId().name
203- val replacedModules = moduleInfoForModuleName.mapNotNull { (name, info) ->
204- (info.path to name).takeIf { name != info.path }
205- }.toMap()
206-
207- val vendorModules = getVendorModules(graph, projectDir, mainModuleName, replacedModules)
206+ val vendorModules = getVendorModules(graph, projectDir, mainModule.name)
208207 if (vendorModules.size < graph.size) {
209208 logger.debug {
210209 " Removing ${graph.size - vendorModules.size} non-vendor modules from the dependency graph."
@@ -216,8 +215,10 @@ class GoMod(
216215 return graph.breakCycles()
217216 }
218217
219- private fun ModuleInfo.toId (): Identifier =
220- if (version.isBlank()) {
218+ private fun ModuleInfo.toId (): Identifier {
219+ if (replace != null ) return replace.toId() // Apply replace directive.
220+
221+ return if (version.isBlank()) {
221222 // If the version is blank, it is a project in ORT speak.
222223 check(main) { " Found a local module dependency which is not supported." }
223224
@@ -238,6 +239,7 @@ class GoMod(
238239 version = normalizeModuleVersion(version)
239240 )
240241 }
242+ }
241243
242244 /* *
243245 * Return the list of all modules contained in the dependency tree with resolved versions and the 'replace'
@@ -248,44 +250,27 @@ class GoMod(
248250
249251 val moduleInfos = list.stdout.byteInputStream().use { JSON .decodeToSequence<ModuleInfo >(it) }
250252
251- return buildMap {
252- moduleInfos.forEach { moduleInfo ->
253- if (moduleInfo.replace != null ) {
254- // The `replace` object in the output of `go list` does not have the `indirect` flag, so copy it
255- // from the replaced module.
256- val replace = moduleInfo.replace.copy(indirect = moduleInfo.indirect)
257- put(moduleInfo.path, replace)
258- put(moduleInfo.replace.path, replace)
259- } else {
260- put(moduleInfo.path, moduleInfo)
261- }
262- }
263- }
253+ return moduleInfos.associateBy { it.path }
264254 }
265255
266256 /* *
267257 * Return the subset of the modules in [graph] required for building and testing the main module. So, test
268258 * dependencies of dependencies are filtered out.
269259 */
270- private fun getVendorModules (
271- graph : Graph <Identifier >,
272- projectDir : File ,
273- mainModuleName : String ,
274- replacedModules : Map <String , String >
275- ): Set <Identifier > {
260+ private fun getVendorModules (graph : Graph <GoModule >, projectDir : File , mainModuleName : String ): Set <GoModule > {
276261 val vendorModuleNames = mutableSetOf (mainModuleName)
277262
278263 graph.nodes.chunked(WHY_CHUNK_SIZE ).forEach { ids ->
279264 // Use the names of replaced modules, because `go mod why` returns only results for those.
280- val moduleNames = ids.map { replacedModules[it.name] ? : it.name }.toTypedArray()
265+ val moduleNames = ids.map { it.name }.toTypedArray()
281266 // Use the ´-m´ switch to use module names because the graph also uses module names, not package names.
282267 // This fixes the accidental dropping of some modules.
283268 val why = runGo(" mod" , " why" , " -m" , " -vendor" , * moduleNames, workingDir = projectDir)
284269
285270 vendorModuleNames + = parseWhyOutput(why.stdout)
286271 }
287272
288- return graph.nodes.filterTo(mutableSetOf ()) { (replacedModules[ it.name] ? : it.name) in vendorModuleNames }
273+ return graph.nodes.filterTo(mutableSetOf ()) { it.name in vendorModuleNames }
289274 }
290275
291276 /* *
@@ -302,7 +287,13 @@ class GoMod(
302287 }
303288 }
304289
305- private fun ModuleInfo.toPackage (): Package {
290+ private fun ModuleInfo.toPackage (): Package ? {
291+ // A ModuleInfo with blank version should be represented by a Project:
292+ if (version.isBlank()) return null
293+
294+ // Apply the replace directive:
295+ if (replace != null ) return replace.toPackage()
296+
306297 val vcsInfo = toVcsInfo().orEmpty()
307298
308299 return Package (
@@ -348,27 +339,31 @@ class GoMod(
348339 private fun runGo (vararg args : CharSequence , workingDir : File ? = null) =
349340 run (args = args, workingDir = workingDir, environment = environment)
350341
351- private fun Map <String , ModuleInfo >.getMainModuleId (): Identifier = values.single { it.main }.toId()
352-
353342 /* *
354343 * Convert this [Graph] to a set of [PackageReference]s that spawn the dependency trees of the direct dependencies
355344 * of the given [root] package. The graph must not contain any cycles, so [Graph.breakCycles] should be called
356345 * before.
357346 */
358- private fun Graph<Identifier>.toPackageReferenceForest (root : Identifier ): Set <PackageReference > {
359- fun getPackageReference (id : Identifier ): PackageReference {
360- val dependencies = getDependencies(id).mapTo(mutableSetOf ()) {
347+ private fun Graph<GoModule>.toPackageReferenceForest (
348+ moduleInfoForModuleName : Map <String , ModuleInfo >
349+ ): Set <PackageReference > {
350+ fun getPackageReference (module : GoModule ): PackageReference {
351+ val dependencies = getDependencies(module).mapTo(mutableSetOf ()) {
361352 getPackageReference(it)
362353 }
363354
364355 return PackageReference (
365- id = id ,
356+ id = moduleInfoForModuleName.getValue(module.name).toId() ,
366357 linkage = PackageLinkage .PROJECT_STATIC ,
367358 dependencies = dependencies
368359 )
369360 }
370361
371- return getDependencies(root).mapTo(mutableSetOf ()) { getPackageReference(it) }
362+ val mainModule = moduleInfoForModuleName.values.single { it.main }.run {
363+ GoModule (path, version)
364+ }
365+
366+ return getDependencies(mainModule).mapTo(mutableSetOf ()) { getPackageReference(it) }
372367 }
373368}
374369
@@ -411,6 +406,18 @@ private data class DepInfo(
411406 val module : ModuleInfo ? = null
412407)
413408
409+ private data class GoModule (
410+ val name : String ,
411+ val version : String
412+ ) {
413+ override fun toString (): String =
414+ if (version.isBlank()) {
415+ name
416+ } else {
417+ " $name @$version "
418+ }
419+ }
420+
414421/* *
415422 * The format of `.info` the Go command line tools cache under '$GOPATH/pkg/mod'.
416423 */
0 commit comments