/** * Copyright 2016 Jake Wharton * * 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 * * http://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. */ task projectDependencyGraph { doLast { def dot = new File(rootProject.rootDir, "docs/static/img/dependency-graph.dot") dot.parentFile.mkdirs() dot.delete() dot << 'digraph {\n' dot << " graph [label=\"${rootProject.name}\\n \",labelloc=t,fontsize=30,ranksep=1.4];\n" dot << ' node [style=filled, fillcolor="#bbbbbb"];\n' dot << ' rankdir=TB;\n' def rootProjects = [] def queue = [rootProject] while (!queue.isEmpty()) { def project = queue.remove(0) rootProjects.add(project) queue.addAll(project.childProjects.values()) } def projects = new LinkedHashSet() def dependencies = new LinkedHashMap, List>() def multiplatformProjects = [] def jsProjects = [] def androidProjects = [] def javaProjects = [] queue = [rootProject] while (!queue.isEmpty()) { def project = queue.remove(0) queue.addAll(project.childProjects.values()) if (project.plugins.hasPlugin('org.jetbrains.kotlin.multiplatform')) { multiplatformProjects.add(project) } if (project.plugins.hasPlugin('org.jetbrains.kotlin.js')) { jsProjects.add(project) } if (project.plugins.hasPlugin('com.android.library') || project.plugins.hasPlugin('com.android.application')) { androidProjects.add(project) } if (project.plugins.hasPlugin('java-library') || project.plugins.hasPlugin('java')) { javaProjects.add(project) } project.configurations.all { config -> config.dependencies .withType(ProjectDependency) .collect { it.dependencyProject } .each { dependency -> projects.add(project) projects.add(dependency) rootProjects.remove(dependency) def graphKey = new Tuple2(project, dependency) def traits = dependencies.computeIfAbsent(graphKey) { new ArrayList() } if (config.name.toLowerCase().endsWith('implementation')) { traits.add('style=dotted') } } } } projects = projects.sort { it.path } dot << '\n # Projects\n\n' for (project in projects) { def traits = [] if (rootProjects.contains(project)) { traits.add('shape=box') } if (project.getPath().startsWith(":core")) { traits.add('fillcolor="#94c1ff"') } else if (project.getPath().startsWith(":app")) { traits.add('fillcolor="#baffc9"') } else if (project.getPath().startsWith(":data")) { traits.add('fillcolor="#fff694"') } else if (project.getPath().startsWith(":services")) { traits.add('fillcolor="#ff9498"') } else if (project.getPath().startsWith(":libs")) { traits.add('fillcolor="#ad94ff"') } dot << " \"${project.path}\" [${traits.join(", ")}];\n" } dot << '\n {rank = same;' for (project in projects) { if (rootProjects.contains(project)) { dot << " \"${project.path}\";" } } dot << '}\n' dot << '\n # Dependencies\n\n' dependencies.forEach { key, traits -> dot << " \"${key.first.path}\" -> \"${key.second.path}\"" if (!traits.isEmpty()) { dot << " [${traits.join(", ")}]" } dot << '\n' } dot << '}\n' def p = 'dot -Tpng -O dependency-graph.dot'.execute([], dot.parentFile) p.waitFor() if (p.exitValue() != 0) { throw new RuntimeException(p.errorStream.text) } println("Project module dependency graph created.") } }