From 39e7c25627d77d05e8aad35c4f9e5bcad08d6808 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Wed, 26 Jul 2017 14:05:38 +0100 Subject: [PATCH] Add graphviz-based module-module compile dependency graphs (#1081) --- settings.gradle | 3 +- tools/graphs/build.gradle | 75 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 tools/graphs/build.gradle diff --git a/settings.gradle b/settings.gradle index fef72becbe..974f08fadd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -26,6 +26,7 @@ include 'tools:explorer' include 'tools:explorer:capsule' include 'tools:demobench' include 'tools:loadtest' +include 'tools:graphs' include 'docs/source/example-code' // Note that we are deliberately choosing to use '/' here. With ':' gradle would treat the directories as actual projects. include 'samples:attachment-demo' include 'samples:trader-demo' @@ -34,4 +35,4 @@ include 'samples:network-visualiser' include 'samples:simm-valuation-demo' include 'samples:notary-demo' include 'samples:bank-of-corda-demo' -include 'cordform-common' \ No newline at end of file +include 'cordform-common' diff --git a/tools/graphs/build.gradle b/tools/graphs/build.gradle new file mode 100644 index 0000000000..cd947894b4 --- /dev/null +++ b/tools/graphs/build.gradle @@ -0,0 +1,75 @@ +class GraphProject { + def projects, project, nodeName, safeName + GraphProject(projects, project) { + def path = project.path.split(':').findAll { it } + if (!path) path.add project.rootProject.name + path = path.collect { it.split('[/\\\\]')[-1] } + nodeName = path.join(':') + safeName = path.join('_') + this.projects = projects + this.project = project + } + def getCompileDeps() { + project.configurations.compile.dependencies.matching { it in ProjectDependency }.collect { projects[it.dependencyProject] } + } +} + +class Graph { + def arcs = new LinkedHashSet() + def dotFile, imgFile, project + Graph(graphsDir, project) { + initArcs(project) + dotFile = new File(graphsDir, "${project.safeName}.dot") + imgFile = new File(graphsDir, "${project.safeName}.png") + this.project = project + } + def initArcs(project) { + project.compileDeps.each { + arcs.add([project, it]) + initArcs(it) + } + } + def output() { + dotFile.text = '' + dotFile << "digraph \"$project.nodeName\" {\n" + dotFile << ' rankdir=LR;\n' + arcs.collect { it.collect { it.nodeName } }.each { + dotFile << " \"${it[0]}\" -> \"${it[1]}\";\n" + } + dotFile << '}\n' + project.project.exec { + commandLine 'dot', '-Tpng', '-o', imgFile, dotFile + } + } +} + +def walkProjects(project, block) { + block(project) + project.childProjects.each { walkProjects(it.value, block) } +} + +task graphs { + doLast { + def projects = new LinkedHashMap() + walkProjects(rootProject) { projects[it] = new GraphProject(projects, it) } + def graphsDir = reporting.baseDir + graphsDir.mkdirs() + def graphs = projects.collect { new Graph(graphsDir, it.value) } + graphs.each { graph -> + if (!graph.arcs) { + logger.info "$graph.project.nodeName is a leaf." + return + } + for (def that : graphs) { + if (that != graph && that.arcs + graph.arcs == that.arcs) { + logger.info "$graph.project.nodeName is included in: $that.imgFile" + return + } + } + graph.output() + } + exec { + commandLine 'eog', graphsDir + } + } +}