diff --git a/.run/taskTree.run.xml b/.run/taskTree.run.xml index 6b951ab..be40bb9 100644 --- a/.run/taskTree.run.xml +++ b/.run/taskTree.run.xml @@ -4,7 +4,7 @@ diff --git a/libs.versions.toml b/libs.versions.toml index 68136c1..92000f3 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -1,6 +1,6 @@ [versions] -tasktree = "0.0.10" +tasktree = "0.0.12" [libraries] diff --git a/readme.md b/readme.md index 74580fa..b9fb9e8 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ Apply plugin in your module's `build.gradle`: ```kotlin plugins { - id("com.github.klee0kai.tasktree") version "0.0.10" + id("com.github.klee0kai.tasktree") version "0.0.12" } tasktree { @@ -20,7 +20,7 @@ tasktree { } ``` -Report your build graph +Project build report in the form of a build graph. [Diagon](https://github.com/ArthurSonzogni/Diagon) must be installed ```bash ./gradlew taskTree assemble @@ -37,7 +37,8 @@ Verify project's module dependency depth ```bash ./gradlew projectTree --verifyDepth=1 ->> Heavy projects: ':example' depth: 2 +>> :dynamic_findstorage price: 3; depth: 3; importance: 0; relativePrice: 1,00; relativeDepth: 1,00; depth dependencies: :dynamic_findstorage <- :app_mobile <- :core; + Heavy projects: ':dynamic_findstorage' depth: 3 ``` Build graphs @@ -65,11 +66,11 @@ initscript { maven(url = "https://jitpack.io") } dependencies { - classpath("com.github.klee0kai.tasktree:com.github.klee0kai.tasktree.gradle.plugin:0.0.10") + classpath("com.github.klee0kai:tasktree:0.0.12") } } -rootProject{ +rootProject { pluginManager.apply(com.github.klee0kai.tasktree.TaskTreePlugin::class.java) extensions.findByType(com.github.klee0kai.tasktree.TaskTreeExtension::class.java) diff --git a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/TaskTreePlugin.kt b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/TaskTreePlugin.kt index 2a3e6ac..5cdb676 100644 --- a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/TaskTreePlugin.kt +++ b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/TaskTreePlugin.kt @@ -21,13 +21,14 @@ open class TaskTreePlugin : Plugin { private fun Project.applyTaskReportOnProject(ext: TaskTreeExtension) { - afterEvaluate { + afterEvaluate(10) { val taskTree = tasks.register("taskTree", TaskTreeTask::class.java, ext) val taskDag = tasks.register("taskGraph", TaskGraphTask::class.java) val flatlist = tasks.register("flatList", FlatListTask::class.java, ext) taskGraph.whenReady { - val isTaskTreeRequested = hasTask(taskTree.get()) || hasTask(taskDag.get()) || hasTask(flatlist.get()) + val isTaskTreeRequested = + hasTask(taskTree.get()) || hasTask(taskDag.get()) || hasTask(flatlist.get()) if (isTaskTreeRequested) { allRequestedTasks.forEach { it.enabled = false @@ -35,7 +36,19 @@ open class TaskTreePlugin : Plugin { } } } + } + /** + * we make sure that dependencies between tasks will no longer be configured + */ + private fun Project.afterEvaluate(count: Int = 1, block: () -> Unit) { + if (count <= 0) { + block() + } else { + afterEvaluate { + afterEvaluate(count - 1, block) + } + } } } diff --git a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/info/TaskStat.kt b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/info/TaskStat.kt index a210508..0d14e3f 100644 --- a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/info/TaskStat.kt +++ b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/info/TaskStat.kt @@ -37,21 +37,28 @@ class TaskStat( return field } - var depth: Int = 0 + val depth: Int get() = depthDependencies.size + + var depthDependencies: List = emptyList() private set get() { - if (field != 0) return field - var maxDepth = 1 - val checked = mutableSetOf() - val deps = LinkedList(dependencies.map { it to 2 }.toMutableList()) + if (field.isNotEmpty()) return field + val checked = mutableMapOf>() + val deps = LinkedList(dependencies.map { listOf(this@TaskStat, it) }.toMutableList()) while (deps.isNotEmpty()) { val dep = deps.pollFirst() - if (checked.contains(dep.first.id)) continue - if (dep.second > maxDepth) maxDepth = dep.second - checked.add(dep.first.id) - deps.addAll(0, dep.first.dependencies.map { it to dep.second + 1 }) + val checkedDepthDeps = checked.getOrDefault(dep.last().id, emptyList()) + if (checkedDepthDeps.size >= dep.size + || checkedDepthDeps.isNotEmpty() + && dep.take(checkedDepthDeps.size).map { it.id } == checkedDepthDeps.map { it.id } // ignore doubles + ) { + continue + } + checked[dep.last().id] = dep + deps.removeAll { task -> task.last().id in dep.last().dependencies.map { it.id } } + deps.addAll(0, dep.last().dependencies.map { dep + it }) } - field = maxDepth + field = checked.values.maxByOrNull { it.size } ?: emptyList() return field } diff --git a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/info/TaskStatHelper.kt b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/info/TaskStatHelper.kt index ed17cb4..33272dc 100644 --- a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/info/TaskStatHelper.kt +++ b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/info/TaskStatHelper.kt @@ -8,7 +8,8 @@ import org.gradle.api.tasks.diagnostics.internal.ProjectDetails object TaskStatHelper { fun collectAllTasksInfo(project: Project): List { - val tasksInfos = project.tasks.map { task -> + val allTasks = project.tasks.toList() + val tasksInfos = allTasks.map { task -> TaskInfo( id = System.identityHashCode(task), taskName = task.name, @@ -20,7 +21,7 @@ object TaskStatHelper { ) }.associateBy { task -> task.id } - project.tasks.forEach { task -> + allTasks.forEach { task -> runCatching { task.taskDependencies.getDependencies(task).forEach { dependsOn -> val taskStat = tasksInfos[System.identityHashCode(dependsOn)] ?: return@forEach diff --git a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/projectInfo/ProjectInfo.kt b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/projectInfo/ProjectInfo.kt index 5fc9f7f..b5f1ef7 100644 --- a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/projectInfo/ProjectInfo.kt +++ b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/projectInfo/ProjectInfo.kt @@ -27,7 +27,7 @@ class ProjectInfo( private set get() { if (field != 0) return field - field = allDependedOnTasks.count() + field = allDependedOnProject.count() return field } @@ -44,7 +44,7 @@ class ProjectInfo( } } - val allDependedOnTasks = sequence { + val allDependedOnProject = sequence { val sent = mutableSetOf() val deps = LinkedList(dependedOnProjects.toMutableList()) while (deps.isNotEmpty()) { @@ -56,21 +56,27 @@ class ProjectInfo( } } - var depth: Int = 0 + val depth: Int get() = depthDependencies.size + + var depthDependencies: List = emptyList() private set get() { - if (field != 0) return field - var maxDepth = 1 - val checked = mutableSetOf() - val deps = LinkedList(dependencies.map { it to 2 }.toMutableList()) + if (field.isNotEmpty()) return field + val checked = mutableMapOf>() + val deps = LinkedList(dependencies.map { listOf(this@ProjectInfo, it) }.toMutableList()) while (deps.isNotEmpty()) { val dep = deps.pollFirst() - if (checked.contains(dep.first.path)) continue - if (dep.second > maxDepth) maxDepth = dep.second - checked.add(dep.first.path) - deps.addAll(0, dep.first.dependencies.map { it to dep.second + 1 }) + val checkedDepthDeps = checked.getOrDefault(dep.last().path, emptyList()) + if (checkedDepthDeps.size >= dep.size + || checkedDepthDeps.isNotEmpty() + && dep.take(checkedDepthDeps.size).map { it.path } == checkedDepthDeps.map { it.path } // ignore doubles + ) { + continue + } + checked[dep.last().path] = dep + deps.addAll(0, dep.last().dependencies.map { dep + it }) } - field = maxDepth + field = checked.values.maxByOrNull { it.size } ?: emptyList() return field } diff --git a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/projectInfo/ProjectStatHelper.kt b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/projectInfo/ProjectStatHelper.kt index 217154c..b51c94b 100644 --- a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/projectInfo/ProjectStatHelper.kt +++ b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/projectInfo/ProjectStatHelper.kt @@ -15,16 +15,18 @@ object ProjectStatHelper { path = it.path, projectDetails = ProjectDetails.of(it), ) - }.associateBy { it.name } + }.associateBy { it.path } rootProject.allprojects.forEach { project -> - val projectInfo = projectInfos[project.name] ?: return@forEach + val projectInfo = projectInfos[project.path] ?: return@forEach project.configurations .firstOrNull { it.name.contains("implementation") } ?.allDependencies ?.filterIsInstance() ?.forEach { dep -> - val depProjectInfo = projectInfos[dep.name] ?: return@forEach + val depProjectInfo = runCatching { projectInfos[dep.path] }.getOrNull() + ?: runCatching { projectInfos[dep.dependencyProject.path] }.getOrNull() + ?: return@forEach projectInfo.dependencies.add(depProjectInfo) } } @@ -54,4 +56,14 @@ object ProjectStatHelper { } + fun filterByRequestedProject( + projectStats: List, + target: String?, + ): List { + if (target.isNullOrBlank()) return projectStats + return projectStats.filter { + it.fullName == target || it.allDependedOnProject.any { it.fullName == target } + } + } + } \ No newline at end of file diff --git a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/ProjectTreeTask.kt b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/ProjectTreeTask.kt index c5d870b..338f5e1 100644 --- a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/ProjectTreeTask.kt +++ b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/ProjectTreeTask.kt @@ -23,7 +23,12 @@ open class ProjectTreeTask @Inject constructor( private val projectsInfos = Cached.of { ProjectStatHelper.collectProjectDependencies(project) } - private val projectsStats by lazy { ProjectStatHelper.calcToProjectStats(projectsInfos.get()) } + private var projectsStats: List = emptyList() + + @Input + @Optional + @set:Option(option = "target", description = "Build graph to module as target") + protected var projectTarget: String? = null @Input @Optional @@ -35,8 +40,13 @@ open class ProjectTreeTask @Inject constructor( @set:Option(option = "verifyPrice", description = "Verify project's module price") protected var verifyPrice: String? = null + override fun getDescription(): String = "Display the hierarchy of module dependencies in a project" + @TaskAction fun generate() { + projectsStats = ProjectStatHelper.calcToProjectStats(projectInfos = projectsInfos.get()) + .let { ProjectStatHelper.filterByRequestedProject(projectStats = it, target = projectTarget) } + renderedProjects.clear() reportGenerator().generateReport( listOf(projectDetails.get()), @@ -44,7 +54,13 @@ open class ProjectTreeTask @Inject constructor( ) { projects -> val graphRenderer = GraphRenderer(renderer.textOutput) val topProjects = projectsStats - .filter { project -> project.allDependedOnCount <= 0L } + .filter { project -> + if (!projectTarget.isNullOrBlank()) { + project.fullName == projectTarget + } else { + project.allDependedOnCount <= 0L + } + } .sortedByDescending { it.depth } topProjects.forEach { graphRenderer.render(it) } @@ -103,14 +119,17 @@ open class ProjectTreeTask @Inject constructor( allStat.forEach { renderer.textOutput - .printProjectShort(it) + .printProjectShort(projectInfo = it, printDepthLine = true) .println() } textOutput.println() } } - private fun StyledTextOutput.printProjectShort(projectInfo: ProjectInfo) = apply { + private fun StyledTextOutput.printProjectShort( + projectInfo: ProjectInfo, + printDepthLine: Boolean = false, + ) = apply { withStyle(Identifier) .text(projectInfo.fullName) @@ -120,6 +139,8 @@ open class ProjectTreeTask @Inject constructor( withStyle(Description) .text(" depth: ${projectInfo.depth};") + + } if (ext.printImportance) { withStyle(Description) @@ -132,6 +153,11 @@ open class ProjectTreeTask @Inject constructor( withStyle(Description) .text(" relativeDepth: ${projectInfo.relativeDepth.formatString()};") } + + if (ext.printPrice && printDepthLine) { + withStyle(Description) + .text(" depth dependencies: ${projectInfo.depthDependencies.joinToString(" <- ") { it.fullName }};") + } } diff --git a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/ProjectsGraphTask.kt b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/ProjectsGraphTask.kt index 8f2dbcf..60d3a9c 100644 --- a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/ProjectsGraphTask.kt +++ b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/ProjectsGraphTask.kt @@ -1,11 +1,13 @@ package com.github.klee0kai.tasktree.tasks +import com.github.klee0kai.tasktree.projectInfo.ProjectInfo import com.github.klee0kai.tasktree.projectInfo.ProjectStatHelper import org.apache.tools.ant.util.TeeOutputStream import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.options.Option import org.gradle.internal.serialization.Cached import org.gradle.process.internal.ExecActionFactory import org.gradle.process.internal.ExecException @@ -22,15 +24,22 @@ open class ProjectsGraphTask @Inject constructor( private val projectsInfos = Cached.of { ProjectStatHelper.collectProjectDependencies(project) } - private val projectsStats by lazy { ProjectStatHelper.calcToProjectStats(projectsInfos.get()) } + private var projectsStats: List = emptyList() + + @Input + @Optional + @set:Option(option = "target", description = "Build graph to module as target") + protected var target: String? = null - @Internal override fun getDescription(): String = - "Draw tasktree graph use Diagon. More: https://github.com/ArthurSonzogni/Diagon" + "Draw project's dependencies graph use Diagon. More: https://github.com/ArthurSonzogni/Diagon" @TaskAction fun generate() { + projectsStats = ProjectStatHelper.calcToProjectStats(projectInfos = projectsInfos.get()) + .let { ProjectStatHelper.filterByRequestedProject(projectStats = it, target = target) } + val depsCode = projectsStats.joinToString("\n") { project -> project.dependencies.joinToString("\n") { dep -> "${dep.fullName} -> ${project.fullName}" diff --git a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/TaskTreeTask.kt b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/TaskTreeTask.kt index 9d2d63d..ed82afb 100644 --- a/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/TaskTreeTask.kt +++ b/tasktree/src/main/kotlin/com/github/klee0kai/tasktree/tasks/TaskTreeTask.kt @@ -23,10 +23,7 @@ open class TaskTreeTask @Inject constructor( private val tasksInfos = Cached.of { TaskStatHelper.collectAllTasksInfo(project) } - private val tasksStats by lazy { - TaskStatHelper.calcToTaskStats(tasksInfos.get()) - .let { TaskStatHelper.filterByRequestedTasks(it, allRequestedTasksIds.get()) } - } + private var tasksStats: List = emptyList() @Input @Optional @@ -40,6 +37,9 @@ open class TaskTreeTask @Inject constructor( @TaskAction fun generate() { + tasksStats = TaskStatHelper.calcToTaskStats(tasksInfos.get()) + .let { TaskStatHelper.filterByRequestedTasks(it, allRequestedTasksIds.get()) } + renderedTasks.clear() reportGenerator().generateReport( listOf(projectDetails.get()), @@ -62,7 +62,11 @@ open class TaskTreeTask @Inject constructor( } } - private fun GraphRenderer.render(taskStat: TaskStat, lastChild: Boolean = true, depth: Int = 0) { + private fun GraphRenderer.render( + taskStat: TaskStat, + lastChild: Boolean = true, + depth: Int = 0, + ) { visit({ printTaskShort(taskStat) @@ -108,7 +112,7 @@ open class TaskTreeTask @Inject constructor( allStat.forEach { renderer.textOutput - .printTaskShort(it) + .printTaskShort(taskStat = it, printDepthLine = true) .println() } textOutput.println() @@ -116,7 +120,10 @@ open class TaskTreeTask @Inject constructor( } - private fun StyledTextOutput.printTaskShort(taskStat: TaskStat) = apply { + private fun StyledTextOutput.printTaskShort( + taskStat: TaskStat, + printDepthLine: Boolean = false, + ) = apply { withStyle(Identifier) .text(taskStat.fullName) @@ -137,6 +144,12 @@ open class TaskTreeTask @Inject constructor( withStyle(Description) .text(" relativeDepth: ${taskStat.relativeDepth.formatString()};") } + + + if (ext.printPrice && printDepthLine) { + withStyle(Description) + .text(" depth dependencies: ${taskStat.depthDependencies.joinToString(" <- ") { it.fullName }};") + } }