From 26645c27407ec4591d2a539901fc3eff5aa420d8 Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Wed, 7 Dec 2022 09:20:55 -0800 Subject: [PATCH] Updates PIG Gradle Plugin for publication (#145) --- pig-gradle-plugin/build.gradle.kts | 42 ++-- .../org/partiql/pig/gradle/PigPlugin.kt | 210 ++++++++++++++++++ .../org/partiql/pig/plugin/PigExtension.kt | 33 --- .../org/partiql/pig/plugin/PigPlugin.kt | 89 -------- .../kotlin/org/partiql/pig/plugin/PigTask.kt | 86 ------- pig-tests/build.gradle.kts | 9 +- 6 files changed, 236 insertions(+), 233 deletions(-) create mode 100644 pig-gradle-plugin/src/main/kotlin/org/partiql/pig/gradle/PigPlugin.kt delete mode 100644 pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigExtension.kt delete mode 100644 pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigPlugin.kt delete mode 100644 pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigTask.kt diff --git a/pig-gradle-plugin/build.gradle.kts b/pig-gradle-plugin/build.gradle.kts index f50aee2..2040438 100644 --- a/pig-gradle-plugin/build.gradle.kts +++ b/pig-gradle-plugin/build.gradle.kts @@ -1,23 +1,25 @@ plugins { id("java-gradle-plugin") id("org.jetbrains.kotlin.jvm") version "1.4.0" - id("com.gradle.plugin-publish") version "1.0.0" + id("com.gradle.plugin-publish") version "1.1.0" } repositories { mavenCentral() } -version = "0.5.1-SNAPSHOT" +object Versions { + const val pig = "0.6.1" + const val kotlinTarget = "1.4" + const val javaTarget = "1.8" +} + +// latest maven central release +version = Versions.pig group = "org.partiql" dependencies { - // It is non-trivial to depend on a local plugin within a gradle project - // The simplest way is using a composite build: https://docs.gradle.org/current/userguide/composite_builds.html - // Other methods involved adding the build/lib/... jar to classpath, or publish to maven local - // By adding the plugin as a dep in `pig-tests`, I cannot use an included build of `pig` in the plugin - // Hence it's much simpler to use the latest published version in the plugin - implementation("org.partiql:partiql-ir-generator:0.5.0") + implementation("org.partiql:partiql-ir-generator:${Versions.pig}") testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1") } @@ -35,21 +37,21 @@ pluginBundle { gradlePlugin { plugins { create("pig-gradle-plugin") { - id = "pig-gradle-plugin" + id = "org.partiql.pig.pig-gradle-plugin" displayName = "PIG Gradle Plugin" description = "The PIG gradle plugin exposes a Gradle task to generate sources from a PIG type universe" - implementationClass = "org.partiql.pig.plugin.PigPlugin" + implementationClass = "org.partiql.pig.gradle.PigPlugin" } } } -// -// // TODO https://github.com/partiql/partiql-ir-generator/issues/132 -// publishing { -// repositories { -// maven { -// name = 'mavenLocalPlugin' -// url = '../maven-local-plugin' -// } -// } -// } +java { + sourceCompatibility = JavaVersion.toVersion(Versions.javaTarget) + targetCompatibility = JavaVersion.toVersion(Versions.javaTarget) +} + +tasks.compileKotlin { + kotlinOptions.jvmTarget = Versions.javaTarget + kotlinOptions.apiVersion = Versions.kotlinTarget + kotlinOptions.languageVersion = Versions.kotlinTarget +} diff --git a/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/gradle/PigPlugin.kt b/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/gradle/PigPlugin.kt new file mode 100644 index 0000000..e344d48 --- /dev/null +++ b/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/gradle/PigPlugin.kt @@ -0,0 +1,210 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +package org.partiql.pig.gradle + +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.options.Option +import org.partiql.pig.main +import java.io.File + +abstract class PigPlugin : Plugin { + + override fun apply(target: Project) { + // Ensure `sourceSets` extension exists + target.pluginManager.apply(JavaPlugin::class.java) + + // Adds pig source set extension to all source sets + target.sourceSets().forEach { sourceSet -> + val name = sourceSet.name + val sds = target.objects.sourceDirectorySet(name, "$name PIG source") + sds.srcDir("src/$name/pig") + sds.include("**/*.ion") + sourceSet.extensions.add("pig", sds) + } + + // Extensions for pig compiler arguments + val ext = target.extensions.create("pig", PigExtension::class.java) + + // Create tasks after source sets have been evaluated + target.afterEvaluate { + target.sourceSets().forEach { sourceSet -> + // Pig generate all for the given source set + val pigAllTaskName = getPigAllTaskName(sourceSet) + val pigAllTask = target.tasks.create(pigAllTaskName) { + it.group = "pig" + it.description = "Generate all PIG sources for ${sourceSet.name} source set" + } + + val language = ext.language + val namespace = ext.namespace + var outDir = ext.outputDir + + // The `kotlin` target without an `outputDir` will use the package path as the output dir + if (outDir == null && language == "kotlin") { + val base = "${target.buildDir}/generated-src" + outDir = File("$base/${namespace.replace('.', '/')}") + // Eventually this will need to be sourceSet.kotlin, but that's currently not possible + // sourceSet.java.srcDir(base) + } + + // Create a pig task for each type universe and each source set + (sourceSet.extensions.getByName("pig") as SourceDirectorySet).files.forEach { file -> + val universeName = file.name.removeSuffix(".ion").lowerToCamelCase().capitalize() + val pigTask = target.tasks.create(pigAllTaskName + universeName, PigTask::class.java) { task -> + task.description = "Generated PIG sources for $file" + task.universe.set(file.absolutePath) + task.target.set(language) + if (namespace.isNotEmpty()) { + task.namespace.set(namespace) + } + if (outDir != null) { + task.outputDir.set(outDir.absolutePath) + } + if (ext.outputFile != null) { + task.outputFile.set(ext.outputFile!!.absolutePath) + } + if (ext.template.isNotEmpty()) { + task.template.set(ext.template) + } + } + pigAllTask.dependsOn(pigTask) + if (language == "kotlin") { + target.tasks.named("compileKotlin") { + it.dependsOn(pigAllTask) + } + } + } + } + } + } + + private fun Project.sourceSets(): List = extensions.getByType(SourceSetContainer::class.java).toList() + + private fun getPigAllTaskName(sourceSet: SourceSet) = when (SourceSet.isMain(sourceSet)) { + true -> "generatePigSource" + else -> "generatePig${sourceSet.name.capitalize()}Source" + } + + /** + * Type Universe files are lower hyphen, but Gradle tasks are lower camel + */ + private fun String.lowerToCamelCase(): String = + this.split('-') + .filter { it.isNotEmpty() } + .mapIndexed { i, str -> + when (i) { + 0 -> str + else -> str.capitalize() + } + } + .joinToString(separator = "") +} + +abstract class PigTask : DefaultTask() { + + init { + group = "pig" + } + + @get:Input + @get:Option( + option = "universe", + description = "Type universe input file" + ) + abstract val universe: Property + + @get:Input + @get:Option( + option = "target", + description = "Target language" + ) + abstract val target: Property + + @get:Input + @get:Optional + @get:Option( + option = "outputFile", + description = "Generated output file (for targets that output a single file)" + ) + abstract val outputFile: Property + + @get:Input + @get:Optional + @get:Option( + option = "outputDir", + description = "Generated output directory (for targets that output multiple files)" + ) + abstract val outputDir: Property + + @get:Input + @get:Optional + @get:Option( + option = "namespace", + description = "Namespace for generated code" + ) + abstract val namespace: Property + + @get:Input + @get:Optional + @get:Option( + option = "template", + description = "Path to an Apache FreeMarker template" + ) + abstract val template: Property + + @TaskAction + fun action() { + val args = mutableListOf() + // required args + args += listOf("-u", universe.get()) + args += listOf("-t", target.get()) + // optional args + if (outputFile.isPresent) { + args += listOf("-o", outputFile.get()) + } + if (outputDir.isPresent) { + args += listOf("-d", outputDir.get()) + } + if (namespace.isPresent) { + args += listOf("-n", namespace.get()) + } + if (template.isPresent) { + args += listOf("-e", template.get()) + } + // invoke pig compiler, offloads all arg handling to the application + // also invoking via the public interface for consistency + println("pig ${args.joinToString(" ")}") + main(args.toTypedArray()) + } +} + +abstract class PigExtension { + var language: String = "kotlin" + var outputFile: File? = null + var outputDir: File? = null + var namespace: String = "" + var template: String = "" +} diff --git a/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigExtension.kt b/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigExtension.kt deleted file mode 100644 index d1406f0..0000000 --- a/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigExtension.kt +++ /dev/null @@ -1,33 +0,0 @@ -package org.partiql.pig.plugin - -import org.gradle.api.Project -import org.gradle.api.provider.Property -import javax.inject.Inject - -abstract class PigExtension @Inject constructor(project: Project) { - - private val objects = project.objects - - val conventionalOutDir: String - - init { - conventionalOutDir = "${project.buildDir}/generated-sources/pig" - } - - // required - val target: Property = objects.property(String::class.java) - - // optional - val outputFile: Property = objects.property(String::class.java) - - // optional - val outputDir: Property = objects - .property(String::class.java) - .convention(conventionalOutDir) - - // optional - val namespace: Property = objects.property(String::class.java) - - // optional - val template: Property = objects.property(String::class.java) -} diff --git a/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigPlugin.kt b/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigPlugin.kt deleted file mode 100644 index 90af799..0000000 --- a/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigPlugin.kt +++ /dev/null @@ -1,89 +0,0 @@ -package org.partiql.pig.plugin - -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.file.SourceDirectorySet -import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.SourceSetContainer - -abstract class PigPlugin : Plugin { - - override fun apply(project: Project) { - // Ensure `sourceSets` extension exists - project.pluginManager.apply(JavaPlugin::class.java) - - // Adds pig source set extension to all source sets - project.sourceSets().forEach { sourceSet -> - val name = sourceSet.name - val sds = project.objects.sourceDirectorySet(name, "$name PIG source") - sds.srcDir("src/$name/pig") - sds.include("**/*.ion") - sourceSet.extensions.add("pig", sds) - } - - // Extensions for pig compiler arguments - val ext = project.extensions.create("pig", PigExtension::class.java, project) - - // Create tasks after source sets have been evaluated - project.afterEvaluate { - project.sourceSets().forEach { sourceSet -> - // Pig generate all for the given source set - val pigAllTaskName = getPigAllTaskName(sourceSet) - val pigAllTask = project.tasks.create(pigAllTaskName) { - it.group = "pig" - it.description = "Generate all PIG sources for ${sourceSet.name} source set" - } - - // If outDir is conventional, add generated sources to javac sources - // Else you're responsible for your own configuration choices - var outDir = ext.outputDir.get() - if (outDir == ext.conventionalOutDir) { - outDir = outDir + "/" + sourceSet.name - sourceSet.java.srcDir(outDir) - } - - // Create a pig task for each type universe and each source set - (sourceSet.extensions.getByName("pig") as SourceDirectorySet).files.forEach { file -> - val universeName = file.name.removeSuffix(".ion").lowerToCamelCase().capitalize() - val pigTask = project.tasks.create(pigAllTaskName + universeName, PigTask::class.java) { task -> - task.description = "Generated PIG sources for $universeName" - task.universe.set(file.absolutePath) - task.target.set(ext.target) - task.outputDir.set(outDir) - task.outputFile.set(ext.outputFile) - task.namespace.set(ext.namespace) - task.template.set(ext.template) - } - pigAllTask.dependsOn(pigTask) - } - - // Execute pig tasks before compiling - project.tasks.named(sourceSet.compileJavaTaskName) { - it.dependsOn(pigAllTask) - } - } - } - } - - private fun Project.sourceSets(): List = extensions.getByType(SourceSetContainer::class.java).toList() - - private fun getPigAllTaskName(sourceSet: SourceSet) = when (sourceSet.name) { - "main" -> "generatePigSource" - else -> "generatePig${sourceSet.name.capitalize()}Source" - } - - /** - * Type Universe files are lower hyphen, but Gradle tasks are lower camel - */ - private fun String.lowerToCamelCase(): String = - this.split('-') - .filter { it.isNotEmpty() } - .mapIndexed { i, str -> - when (i) { - 0 -> str - else -> str.capitalize() - } - } - .joinToString(separator = "") -} diff --git a/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigTask.kt b/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigTask.kt deleted file mode 100644 index 0e8cff5..0000000 --- a/pig-gradle-plugin/src/main/kotlin/org/partiql/pig/plugin/PigTask.kt +++ /dev/null @@ -1,86 +0,0 @@ -package org.partiql.pig.plugin - -import org.gradle.api.DefaultTask -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.options.Option - -abstract class PigTask : DefaultTask() { - - init { - group = "pig" - } - - @get:Input - @get:Option( - option = "universe", - description = "Type universe input file" - ) - abstract val universe: Property - - @get:Input - @get:Option( - option = "target", - description = "Target language" - ) - abstract val target: Property - - @get:Input - @get:Optional - @get:Option( - option = "outputFile", - description = "Generated output file (for targets that output a single file)" - ) - abstract val outputFile: Property - - @get:Input - @get:Optional - @get:Option( - option = "outputDir", - description = "Generated output directory (for targets that output multiple files)" - ) - abstract val outputDir: Property - - @get:Input - @get:Optional - @get:Option( - option = "namespace", - description = "Namespace for generated code" - ) - abstract val namespace: Property - - @get:Input - @get:Optional - @get:Option( - option = "template", - description = "Path to an Apache FreeMarker template" - ) - abstract val template: Property - - @TaskAction - fun action() { - val args = mutableListOf() - // required args - args += listOf("-u", universe.get()) - args += listOf("-t", target.get()) - // optional args - if (outputFile.isPresent) { - args += listOf("-o", outputFile.get()) - } - if (outputDir.isPresent) { - args += listOf("-d", outputDir.get()) - } - if (namespace.isPresent) { - args += listOf("-n", namespace.get()) - } - if (template.isPresent) { - args += listOf("-e", template.get()) - } - // invoke pig compiler, offloads all arg handling to the application - // also invoking via the public interface for consistency - println("pig ${args.joinToString(" ")}") - org.partiql.pig.main(args.toTypedArray()) - } -} diff --git a/pig-tests/build.gradle.kts b/pig-tests/build.gradle.kts index 07d0946..4e4a292 100644 --- a/pig-tests/build.gradle.kts +++ b/pig-tests/build.gradle.kts @@ -15,7 +15,7 @@ plugins { id("pig.conventions") - id("pig-gradle-plugin") + id("org.partiql.pig.pig-gradle-plugin") id("java-library") } @@ -24,13 +24,12 @@ dependencies { } // remove after pig-example is created -val pigOutputDir = file("./src/main/kotlin/org/partiql/pig/tests/generated/").absolutePath +val pigOutputDir = file("./src/main/kotlin/org/partiql/pig/tests/generated/") pig { - target.set("kotlin") - namespace.set("org.partiql.pig.tests.generated") + namespace = "org.partiql.pig.tests.generated" // remove after pig-example is created - outputDir.set(pigOutputDir) + outputDir = pigOutputDir } // remove after pig-example is created