From 10afa523fd28d6a098c0969aaa6f7f13fbe450b5 Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Wed, 1 Apr 2020 22:37:51 +0100 Subject: [PATCH] first functional version --- LICENSE.md | 22 +++ build.gradle.kts | 14 +- settings.gradle.kts | 2 +- .../my/first/plugin/MyFirstPluginPlugin.kt | 39 ---- .../plugins/DependencyExportPlugin.kt | 173 ++++++++++++++++++ .../woggioni/plugins}/Powerup.kt | 2 +- .../plugins/DependencyExportPluginTest.kt} | 33 +--- .../my/first/plugin/test/build.gradle | 8 - .../my/first/plugin/test/build.gradle.kts | 21 --- .../net/woggioni/plugins/build.gradle | 17 ++ .../net/woggioni/plugins/build.gradle.kts | 14 ++ 11 files changed, 245 insertions(+), 100 deletions(-) create mode 100644 LICENSE.md delete mode 100644 src/main/kotlin/my/first/plugin/MyFirstPluginPlugin.kt create mode 100644 src/main/kotlin/net/woggioni/plugins/DependencyExportPlugin.kt rename src/main/kotlin/{my/first/plugin => net/woggioni/plugins}/Powerup.kt (97%) rename src/test/kotlin/{my/first/plugin/test/MyFirstPluginPluginTest.kt => net/woggioni/plugins/DependencyExportPluginTest.kt} (59%) delete mode 100644 src/test/resources/my/first/plugin/test/build.gradle delete mode 100644 src/test/resources/my/first/plugin/test/build.gradle.kts create mode 100644 src/test/resources/net/woggioni/plugins/build.gradle create mode 100644 src/test/resources/net/woggioni/plugins/build.gradle.kts diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..f395a48 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,22 @@ +Copyright 2020 Walter Oggioni + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/build.gradle.kts b/build.gradle.kts index c54ac14..ba01471 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,15 @@ repositories { jcenter() + mavenLocal() } -group = "net.corda" +group = "net.woggioni.plugins" version = 0.1 plugins { `java-gradle-plugin` `maven-publish` - id("org.jetbrains.kotlin.jvm") version("1.3.61") + id("org.jetbrains.kotlin.jvm") version("1.3.71") id("com.gradle.plugin-publish") version("0.10.1") } @@ -26,6 +27,7 @@ dependencies { // Use the Kotlin JDK 8 standard library. implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation("net.woggioni:worth:1.0") // Use the Kotlin test library. // testImplementation("org.jetbrains.kotlin:kotlin-test") @@ -41,8 +43,8 @@ dependencies { gradlePlugin { // Define the plugin val my_first_plugin by plugins.creating { - id = "net.corda.my-first-plugin" - implementationClass = "my.first.plugin.MyFirstPluginPlugin" + id = "net.woggioni.plugins.dependency-export" + implementationClass = "net.woggioni.plugins.DependencyExportPlugin" } } @@ -75,3 +77,7 @@ tasks.withType().configureEach { gradleVersion = "6.1.1" distributionType = Wrapper.DistributionType.ALL } + +tasks.withType().configureEach { + kotlinOptions.jvmTarget = "1.8" +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 50e3297..28c0ec0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,4 +7,4 @@ * in the user manual at https://docs.gradle.org/6.1.1/userguide/multi_project_builds.html */ -rootProject.name = "my-first-plugin" +rootProject.name = "dependency-export" diff --git a/src/main/kotlin/my/first/plugin/MyFirstPluginPlugin.kt b/src/main/kotlin/my/first/plugin/MyFirstPluginPlugin.kt deleted file mode 100644 index f28e6d3..0000000 --- a/src/main/kotlin/my/first/plugin/MyFirstPluginPlugin.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This Kotlin source file was generated by the Gradle 'init' task. - */ -package my.first.plugin - -import org.gradle.api.Project -import org.gradle.api.Plugin - - -///** -// * A simple 'hello world' plugin. -// */ -//class MyFirstPluginPlugin: Plugin { -// override fun apply(project: Project) { -// // Register a task -// project.tasks.register("greeting") { task -> -// task.doLast { -// println("Hello from plugin 'my.first.plugin.greeting'") -// } -// } -// } -//} - -open class MyFirstPluginPluginExtension { - var message: String? = null - var greeter: String? = null -} - -class MyFirstPluginPlugin : Plugin { - override fun apply(project: Project) { - val extension = MyFirstPluginPluginExtension() - project.extensions.add(MyFirstPluginPluginExtension::class.java, "my_first_plugin", MyFirstPluginPluginExtension()) - project.tasks.register("hello") { - it.doLast { - println("${extension.message} from ${extension.greeter}") - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/net/woggioni/plugins/DependencyExportPlugin.kt b/src/main/kotlin/net/woggioni/plugins/DependencyExportPlugin.kt new file mode 100644 index 0000000..5d5aa05 --- /dev/null +++ b/src/main/kotlin/net/woggioni/plugins/DependencyExportPlugin.kt @@ -0,0 +1,173 @@ +/* + * This Kotlin source file was generated by the Gradle 'init' task. + */ +package net.woggioni.plugins + +import net.woggioni.jwo.JWO +import net.woggioni.jwo.tree.StackContext +import net.woggioni.jwo.tree.TreeNode +import net.woggioni.jwo.tree.TreeNodeVisitor +import net.woggioni.jwo.tree.TreeWalker +import net.woggioni.worth.serialization.json.JSONDumper +import net.woggioni.worth.value.ObjectValue +import net.woggioni.worth.xface.Value +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.component.ModuleComponentIdentifier +import org.gradle.api.artifacts.component.ProjectComponentIdentifier +import org.gradle.api.artifacts.result.ResolvedComponentResult +import org.gradle.api.artifacts.result.ResolvedDependencyResult +import org.gradle.api.artifacts.result.UnresolvedDependencyResult +import java.io.BufferedWriter +import java.io.OutputStreamWriter +import java.nio.file.Files +import java.nio.file.Paths + +open class DependencyExportPluginExtension { + var configurationName: String = "default" +} + +object DependencyExporter { + + fun graphviz(project: Project, configurationName : String) { + var sequence = 0 + val map = HashMap() + val resolutionResult = project.configurations.single { + it.name == configurationName + }.incoming.resolutionResult + project.buildDir.toPath().let { + Files.createDirectories(it) + }.let { + BufferedWriter( + OutputStreamWriter( + Files.newOutputStream(project.buildDir.toPath().resolve("dependencies.dot")))) + }.use { writer -> + writer.write("digraph G {") + writer.newLine() + writer.write(" #rankdir=\"LR\";") + writer.newLine() + for (component in resolutionResult.allComponents) { + map.computeIfAbsent(component) { + sequence++ + } + val (shape, color) = when (component.id) { + is ProjectComponentIdentifier -> "box" to "#88ff88" + is ModuleComponentIdentifier -> "oval" to "#ffff88" + else -> throw NotImplementedError("${component.id::class}") + } + val attrs = mapOf( + "label" to component.id.displayName, + "shape" to shape, + "style" to "filled", + "fillcolor" to color + ) + writer.write(" node_${map[component]} [" + + attrs.entries + .asSequence() + .map { "${it.key}=\"${it.value}\"" }.joinToString(", ") + + "];") + writer.newLine() + } + + for (component in resolutionResult.allComponents) { + + component.dependencies.map { dependency -> + when (dependency) { + is ResolvedDependencyResult -> dependency + is UnresolvedDependencyResult -> { + throw dependency.failure + } + else -> { + throw NotImplementedError("${dependency::class}") + } + } + }.map(ResolvedDependencyResult::getSelected).forEach { child -> + writer.write(" node_${map[component]} -> node_${map[child]};") + writer.newLine() + } + } + writer.write("}") + writer.newLine() + } + } +} + +class DependencyTreeNode(val component: ResolvedComponentResult) : TreeNode { + override fun children(): Iterator { + return object : Iterator { + val it = component.dependencies.iterator() + override fun hasNext(): Boolean { + return it.hasNext() + } + + override fun next(): DependencyTreeNode { + val dependency = it.next() + return when (dependency) { + is ResolvedDependencyResult -> { + DependencyTreeNode(dependency.selected) + } + is UnresolvedDependencyResult -> { + throw dependency.failure + } + else -> { + throw NotImplementedError("${dependency::class}") + } + } + } + } + } +} + +fun export(project: Project) { + val cfg = Value.Configuration.builder() + .serializeReferences(true) + .objectValueImplementation(ObjectValue.Implementation.ArrayList) + .build() + val result = ObjectValue.newInstance(cfg) + project.configurations.all { configuration -> + if (configuration.isCanBeResolved) { + val configDeps = ObjectValue.newInstance(cfg) + val visitor = object : TreeNodeVisitor { + override fun visitPre(stack: MutableList>): TreeNodeVisitor.VisitOutcome { + val stackElement = JWO.tail(stack) + stackElement.context = ObjectValue.newInstance(cfg) + val id = stackElement.node.component.id + if (stack.size == 1) { + configDeps.put(id.displayName, stackElement.context) + } else if (stack.size > 1) { + val parentStackElement = JWO.tail(stack, -2) + parentStackElement.context.put(id.displayName, stackElement.context) + } + return TreeNodeVisitor.VisitOutcome.CONTINUE + } +// override fun visitPost(stack: MutableList>?) { +// val stackElement = JWO.tail(stack) +// if(stack.size > 1) { +// val parentStackElement = JWO.tail(stack, -2) +// parentStackElement.context.add(StringValue(stackElement.node.component.toString())) +// } +// } + } + TreeWalker(visitor).walk(DependencyTreeNode(configuration.incoming.resolutionResult.root)) + result.put(configuration.name, configDeps) + } + } + BufferedWriter(OutputStreamWriter(Files.newOutputStream(Paths.get("/tmp/dependencies.json")))).use { + JSONDumper.newInstance(cfg).dump(result, it) + } +} + +class DependencyExportPlugin : Plugin { + override fun apply(project: Project) { + val extension = DependencyExportPluginExtension() + project.extensions.add(DependencyExportPluginExtension::class.java, "exportDependencies", extension) + project.tasks.register("exportDependencies") { + it.doLast { + val propertyKey = "exportDependencies.configurationName" + val properties = project.properties + val configurationName = properties.getOrDefault(propertyKey, extension.configurationName) as String + DependencyExporter.graphviz(project, configurationName = configurationName) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/my/first/plugin/Powerup.kt b/src/main/kotlin/net/woggioni/plugins/Powerup.kt similarity index 97% rename from src/main/kotlin/my/first/plugin/Powerup.kt rename to src/main/kotlin/net/woggioni/plugins/Powerup.kt index d6a7741..7164403 100644 --- a/src/main/kotlin/my/first/plugin/Powerup.kt +++ b/src/main/kotlin/net/woggioni/plugins/Powerup.kt @@ -1,4 +1,4 @@ -package my.first.plugin +package net.woggioni.plugins import java.io.FileNotFoundException import java.io.IOException diff --git a/src/test/kotlin/my/first/plugin/test/MyFirstPluginPluginTest.kt b/src/test/kotlin/net/woggioni/plugins/DependencyExportPluginTest.kt similarity index 59% rename from src/test/kotlin/my/first/plugin/test/MyFirstPluginPluginTest.kt rename to src/test/kotlin/net/woggioni/plugins/DependencyExportPluginTest.kt index 1522397..fd387d9 100644 --- a/src/test/kotlin/my/first/plugin/test/MyFirstPluginPluginTest.kt +++ b/src/test/kotlin/net/woggioni/plugins/DependencyExportPluginTest.kt @@ -1,23 +1,17 @@ /* * This Kotlin source file was generated by the Gradle 'init' task. */ -package my.first.plugin.test +package net.woggioni.plugins -import my.first.plugin.installResource -import org.gradle.testfixtures.ProjectBuilder +import net.woggioni.plugins.installResource import org.gradle.testkit.runner.GradleRunner import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir -import java.io.File -import java.net.URLClassLoader import java.nio.file.Path -/** - * A simple unit test for the 'my.first.plugin.greeting' plugin. - */ -class MyFirstPluginPluginTest { +class DependencyExportPluginTest { @TempDir lateinit var testProjectDir : Path @@ -37,33 +31,20 @@ class MyFirstPluginPluginTest { } @Test - fun fooKotlin() { + fun testKotlin() { installResource("build.gradle.kts", testProjectDir) installResource("settings.gradle.kts", testProjectDir) installResource("gradle.properties", testProjectDir) - val runner = getStandardGradleRunnerFor("hello") + val runner = getStandardGradleRunnerFor("exportDependencies") val result = runner.build() - println(result.output) } @Test - fun fooGroovy() { + fun testGroovy() { installResource("build.gradle", testProjectDir) installResource("settings.gradle.kts", testProjectDir) installResource("gradle.properties", testProjectDir) - val runner = getStandardGradleRunnerFor("hello") + val runner = getStandardGradleRunnerFor("exportDependencies") val result = runner.build() - println(result.output) } - -// @Test -// fun `plugin registers task`() { -// // Create a test project and apply the plugin -// val project = ProjectBuilder.builder().build() -// project.plugins.apply("my.first.plugin.MyFirstPlugin") -// -// // Verify the result -// val task = project.tasks.findByName("hello")!! -// println(task.outputs.hasOutput) -// } } diff --git a/src/test/resources/my/first/plugin/test/build.gradle b/src/test/resources/my/first/plugin/test/build.gradle deleted file mode 100644 index 8dc3e7b..0000000 --- a/src/test/resources/my/first/plugin/test/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id "net.corda.my-first-plugin" -} - -my_first_plugin { - message = 'Hi' - greeter = 'Gradle' -} \ No newline at end of file diff --git a/src/test/resources/my/first/plugin/test/build.gradle.kts b/src/test/resources/my/first/plugin/test/build.gradle.kts deleted file mode 100644 index 18dc0b8..0000000 --- a/src/test/resources/my/first/plugin/test/build.gradle.kts +++ /dev/null @@ -1,21 +0,0 @@ -//buildscript { -// repositories { -// mavenLocal() -// jcenter() -// mavenCentral() -// } -// dependencies { -// classpath("net.corda:my-first-plugin:0.1") -// } -//} - -plugins { - id("net.corda.my-first-plugin") -} -//apply(plugin = "net.corda.my-first-plugin") -import my.first.plugin.MyFirstPluginPluginExtension - -configure { - message = "Hi" - greeter = "Gradle" -} diff --git a/src/test/resources/net/woggioni/plugins/build.gradle b/src/test/resources/net/woggioni/plugins/build.gradle new file mode 100644 index 0000000..477a53d --- /dev/null +++ b/src/test/resources/net/woggioni/plugins/build.gradle @@ -0,0 +1,17 @@ +plugins { + id "org.jetbrains.kotlin.jvm" version "1.3.61" + id "net.woggioni.plugins.dependency-export" +} + +exportDependencies { + configurationName = 'runtime' +} + +repositories { + jcenter() + mavenLocal() +} + +dependencies { + runtime("org.hibernate:hibernate-core:5.4.13.Final") +} \ No newline at end of file diff --git a/src/test/resources/net/woggioni/plugins/build.gradle.kts b/src/test/resources/net/woggioni/plugins/build.gradle.kts new file mode 100644 index 0000000..71b1bbb --- /dev/null +++ b/src/test/resources/net/woggioni/plugins/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + kotlin("jvm") version "1.3.71" + id("net.woggioni.plugins.dependency-export") +} + +repositories { + jcenter() + mavenLocal() +} + +dependencies { + runtime("org.hibernate:hibernate-core:5.4.13.Final") +} +