@@ -130,18 +130,19 @@ data object Srcx {
130130 writeProjectReport(rootProject, summary, extension)
131131 " OK ${project.path} "
132132 }
133- generateIncludedBuildReports(rootProject, extension)
133+ val includedBuilds = rootProject.gradle.includedBuilds
134+ generateIncludedBuildReports(includedBuilds, extension)
134135
135136 // Context report
136137 val summaries = projects.map { extractProjectSummary(it, rootProject) }
137138 val includedBuildRefs =
138- rootProject.gradle. includedBuilds.map { build ->
139+ includedBuilds.map { build ->
139140 val relPath = build.projectDir.relativeTo(rootProject.projectDir).path
140141 DashboardRenderer .IncludedBuildRef (build.name, relPath)
141142 }
142- val builds = rootProject.gradle. includedBuilds.map { it.name to it.projectDir }
143- val includedBuildSummaries = collectIncludedBuildSummaries(builds)
144- val buildEdges = computeBuildEdges(builds , includedBuildSummaries)
143+ val includedBuildSummaries = collectIncludedBuildSummaries( includedBuilds)
144+ val buildPairs = includedBuilds.map { it.name to it.projectDir }
145+ val buildEdges = computeBuildEdges(buildPairs , includedBuildSummaries)
145146 val classDiagram = generateClassDiagram(rootProject)
146147 val renderer =
147148 DashboardRenderer (
@@ -242,30 +243,22 @@ data object Srcx {
242243 }
243244
244245 internal fun generateIncludedBuildReports (
245- rootProject : Project ,
246- extension : SettingsExtension ,
247- ) {
248- val builds = rootProject.gradle.includedBuilds.map { it.name to it.projectDir }
249- generateIncludedBuildReports(builds, extension)
250- }
251-
252- internal fun generateIncludedBuildReports (
253- builds : List <Pair <String , File >>,
246+ builds : Collection <org.gradle.api.initialization.IncludedBuild >,
254247 extension : SettingsExtension ,
255248 ) {
256- for ((name, buildDir) in builds) {
249+ for (build in builds) {
250+ val buildDir = build.projectDir
257251 val buildOutputDir = File (buildDir, extension.outputDir)
258252 buildOutputDir.mkdirs()
259253
260- val projectDirs = mutableListOf (buildDir to " :" )
261- for (sub in discoverSubprojects(buildDir)) {
262- val subDir = File (buildDir, sub.removePrefix(" :" ).replace(" :" , " /" ))
263- projectDirs.add(subDir to sub)
264- }
254+ val projectDirs =
255+ discoverIncludedBuildProjects(build).map { (path, dir) ->
256+ dir to path
257+ }
265258
266259 val summaries =
267260 projectDirs.map { (dir, path) ->
268- extractStandaloneProjectSummary(dir, path, buildDir )
261+ extractStandaloneProjectSummary(dir, path)
269262 }
270263
271264 for (summary in summaries) {
@@ -280,7 +273,7 @@ data object Srcx {
280273 File (reportDir, " symbols.md" ).writeText(renderer.render())
281274 }
282275
283- val buildRenderer = IncludedBuildRenderer (name, summaries)
276+ val buildRenderer = IncludedBuildRenderer (build. name, summaries)
284277 File (buildOutputDir, " context.md" ).writeText(buildRenderer.render())
285278 writeGitignoreAt(buildOutputDir)
286279 }
@@ -295,25 +288,14 @@ data object Srcx {
295288 }
296289
297290 internal fun collectIncludedBuildSummaries (
298- rootProject : Project ,
299- ): Map <String , List <ProjectSummary >> {
300- val builds = rootProject.gradle.includedBuilds.map { it.name to it.projectDir }
301- return collectIncludedBuildSummaries(builds)
302- }
303-
304- internal fun collectIncludedBuildSummaries (
305- builds : List <Pair <String , File >>,
291+ builds : Collection <org.gradle.api.initialization.IncludedBuild >,
306292 ): Map <String , List <ProjectSummary >> {
307293 val result = mutableMapOf<String , List <ProjectSummary >>()
308- for ((name, buildDir) in builds) {
309- val projectDirs = mutableListOf (buildDir to " :" )
310- for (sub in discoverSubprojects(buildDir)) {
311- val subDir = File (buildDir, sub.removePrefix(" :" ).replace(" :" , " /" ))
312- projectDirs.add(subDir to sub)
313- }
314- result[name] =
315- projectDirs.map { (dir, path) ->
316- extractStandaloneProjectSummary(dir, path, buildDir)
294+ for (build in builds) {
295+ val projectEntries = discoverIncludedBuildProjects(build)
296+ result[build.name] =
297+ projectEntries.map { (path, dir) ->
298+ extractStandaloneProjectSummary(dir, path)
317299 }
318300 }
319301 return result
@@ -322,7 +304,6 @@ data object Srcx {
322304 internal fun extractStandaloneProjectSummary (
323305 projectDir : File ,
324306 projectPath : String ,
325- buildDir : File ,
326307 ): ProjectSummary {
327308 val sourceSets = discoverSourceSets(projectDir)
328309 val allSymbols = mutableListOf<SymbolEntry >()
@@ -350,12 +331,8 @@ data object Srcx {
350331 .map { it.relativeTo(projectDir).path }
351332 }
352333
353- val subprojects =
354- if (projectPath == " :" ) {
355- discoverSubprojects(buildDir)
356- } else {
357- emptyList()
358- }
334+ // Subprojects are discovered by the caller via Gradle API
335+ val subprojects = emptyList<String >()
359336
360337 val dependencies = extractDependenciesFromBuildFile(projectDir)
361338
@@ -580,17 +557,41 @@ data object Srcx {
580557 return results
581558 }
582559
583- internal fun discoverSubprojects (buildDir : File ): List <String > {
584- val settingsFile =
585- File (buildDir, " settings.gradle.kts" ).takeIf { it.exists() }
586- ? : File (buildDir, " settings.gradle" ).takeIf { it.exists() }
587- ? : return emptyList()
588-
589- val includePattern = Regex (""" include\("([^"]+)"\)""" )
590- return settingsFile
591- .readLines()
592- .mapNotNull { line -> includePattern.find(line)?.groupValues?.get(1 ) }
593- }
560+ @Suppress(" SwallowedException" )
561+ internal fun discoverIncludedBuildProjects (
562+ build : org.gradle.api.initialization.IncludedBuild ,
563+ ): List <Pair <String , File >> =
564+ runCatching {
565+ val target = build.javaClass.getMethod(" getTarget" ).invoke(build)
566+ val registry =
567+ target!! .javaClass.getMethod(" getProjects" ).let {
568+ it.isAccessible = true
569+ it.invoke(target)
570+ }
571+ val allProjects =
572+ registry!! .javaClass.getMethod(" getAllProjects" ).let {
573+ it.isAccessible = true
574+ it.invoke(registry) as Set <* >
575+ }
576+ allProjects.mapNotNull { ps ->
577+ val path =
578+ ps!! .javaClass.getMethod(" getIdentityPath" ).let {
579+ it.isAccessible = true
580+ it.invoke(ps).toString()
581+ }
582+ val dir =
583+ ps.javaClass.getMethod(" getProjectDir" ).let {
584+ it.isAccessible = true
585+ it.invoke(ps) as File
586+ }
587+ // Strip the build prefix from the path to get project-relative path
588+ val relativePath =
589+ path
590+ .removePrefix(" :${build.name} " )
591+ .ifEmpty { " :" }
592+ relativePath to dir
593+ }
594+ }.getOrDefault(listOf (" :" to build.projectDir))
594595
595596 internal fun buildFileName (project : Project ): String =
596597 when {
0 commit comments