From 469aefb1a5d0dd1dc13b406e6cbab1cd9ba2cd29 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Mon, 26 Sep 2022 16:17:05 -0400 Subject: [PATCH] Converted Stone Gradle Code into a Kotlin Gradle Plugin (#451) * Plugin Structure * Added Stone Plugin * Working Gradle Plugin * Swap the default and the custom configs. * Config Updated * No longer need stone.gradle * Added new plugin to examples. * Add output directory as source afterEvaluate. * Moved plugin to different package. * Removed unused code. * Code cleanup pass. --- build.gradle | 1 + dropbox-sdk-java/build.gradle | 69 ++++- dropbox-sdk-java/stone.gradle | 292 ------------------ examples/build.gradle | 1 + examples/settings.gradle | 8 +- settings.gradle | 8 +- stone-java-gradle-plugin/README.md | 3 + stone-java-gradle-plugin/build.gradle.kts | 31 ++ stone-java-gradle-plugin/settings.gradle | 9 + .../com/dropbox/stone/java/StonePlugin.kt | 85 +++++ .../com/dropbox/stone/java/StoneTask.kt | 220 +++++++++++++ .../dropbox/stone/java/model/ClientSpec.kt | 11 + .../dropbox/stone/java/model/StoneConfig.kt | 11 + 13 files changed, 451 insertions(+), 298 deletions(-) delete mode 100644 dropbox-sdk-java/stone.gradle create mode 100644 stone-java-gradle-plugin/README.md create mode 100644 stone-java-gradle-plugin/build.gradle.kts create mode 100644 stone-java-gradle-plugin/settings.gradle create mode 100644 stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StonePlugin.kt create mode 100644 stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StoneTask.kt create mode 100644 stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/ClientSpec.kt create mode 100644 stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/StoneConfig.kt diff --git a/build.gradle b/build.gradle index 15ea0d721..6f708e82c 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ buildscript { classpath files('gradle/dropbox-pem-converter-plugin') classpath(dropboxJavaSdkLibs.android.gradle.plugin) classpath(dropboxJavaSdkLibs.kotlin.gradle.plugin) + classpath("com.dropbox.gradle.plugins:stone-java-gradle-plugin") } } diff --git a/dropbox-sdk-java/build.gradle b/dropbox-sdk-java/build.gradle index 688b28297..8625945e9 100644 --- a/dropbox-sdk-java/build.gradle +++ b/dropbox-sdk-java/build.gradle @@ -1,3 +1,6 @@ +import com.dropbox.stone.java.model.ClientSpec +import com.dropbox.stone.java.model.StoneConfig + plugins { id 'java-library' id "com.github.blindpirate.osgi" @@ -7,6 +10,9 @@ plugins { id "com.dropbox.dependency-guard" } +dependencyGuard { + configuration("runtimeClasspath") +} sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -239,9 +245,64 @@ dependencyUpdates.resolutionStrategy = { } } -apply from: 'stone.gradle' +apply plugin: "com.dropbox.stone.java" + +generateStone { + String unusedClassesToGenerate = 'AuthError, PathRoot, PathRootError, AccessError, RateLimitError' + String packageName = 'com.dropbox.core.v2' + String globalRouteFilter = 'alpha_group=null and beta_group=null' + config[ + new StoneConfig( + packageName: packageName, + globalRouteFilter: globalRouteFilter, + client: new ClientSpec( + name: 'DbxClientV2Base', + javadoc: 'Base class for user auth clients.', + requestsClassnamePrefix: "DbxUser", + routeFilter: 'auth="user" or auth="noauth" or auth="app, user"', + unusedClassesToGenerate: unusedClassesToGenerate, + ), + ), + new StoneConfig( + packageName: packageName, + globalRouteFilter: globalRouteFilter, + client: new ClientSpec( + name: 'DbxTeamClientV2Base', + javadoc: 'Base class for team auth clients.', + requestsClassnamePrefix: 'DbxTeam', + routeFilter: 'auth="team"', + ), + ), + new StoneConfig( + packageName: packageName, + globalRouteFilter: globalRouteFilter, + client: new ClientSpec( + name: 'DbxAppClientV2Base', + javadoc: 'Base class for app auth clients.', + requestsClassnamePrefix: "DbxApp", + routeFilter: 'auth="app" or auth="app, user"', + ) + ), + + ] + outputDir "${project.projectDir}/generated_stone_source/main" +} -dependencyGuard { - configuration("runtimeClasspath") - configuration("compileClasspath") +generateTestStone { + String packageName = 'com.dropbox.core.stone' + config[ + new StoneConfig( + packageName: packageName, + dataTypesOnly: true, + ), + new StoneConfig( + packageName: packageName, + client: new ClientSpec( + name: 'DbxClientV2Base', + javadoc: 'TestClass.', + requestsClassnamePrefix: "DbxTest", + ) + ), + ] + outputDir "${project.projectDir}/generated_stone_source/test" } diff --git a/dropbox-sdk-java/stone.gradle b/dropbox-sdk-java/stone.gradle deleted file mode 100644 index 38c55bf55..000000000 --- a/dropbox-sdk-java/stone.gradle +++ /dev/null @@ -1,292 +0,0 @@ -import javax.inject.Inject - -apply plugin: 'java' - -class StoneConfig implements Serializable { - String packageName = 'com.dropbox.stone' - String globalRouteFilter = null - boolean dataTypesOnly = false - ClientSpec client = null - String routeWhitelistFilter = null -} - -class ClientSpec implements Serializable { - String name = null - String javadoc = null - String routeFilter = null - String requestsClassnamePrefix = null - String unusedClassesToGenerate = null -} - -abstract class StoneTask extends DefaultTask { - - @Input - protected abstract ListProperty getStoneConfigs(); - - @Input - protected abstract Property getGeneratorDir() - - @Input - protected abstract Property getSpecDir() - - @Optional - @Input - protected abstract Property getRouteWhitelistFilter() - - @Input - protected abstract Property getStoneDir() - - @Input - protected abstract Property getPythonCommand() - - @OutputDirectory - protected abstract DirectoryProperty getOutputDir(); - - @Inject - protected abstract ObjectFactory getObjectFactory(); - - @Inject - protected abstract ExecOperations getExecOperations(); - - - public void config(List configs) { - getStoneConfigs().set(configs) - } - - public void generatorDir(String generatorDir) { - getGeneratorDir().set(generatorDir) - } - - public void routeWhitelistFilter(String routeWhitelistFilter) { - getRouteWhitelistFilter().set(routeWhitelistFilter) - } - - public void stoneDir(String stoneDir) { - getStoneDir().set(stoneDir) - } - - public void pythonCommand(String pythonCommand) { - getPythonCommand().set(pythonCommand) - } - - public void specDir(String specDir) { - getSpecDir().set(specDir) - } - - public void outputDir(String outputDir) { - getOutputDir().set(new File(outputDir)) - } - - @TaskAction - public void processStone() { - def generatorFileTree = getObjectFactory().fileTree().from(getGeneratorDir().get()) - generatorFileTree.include('**/*stoneg.py') - def generatorFile = generatorFileTree.getSingleFile() - def specFiles = getSpecFiles(getObjectFactory(), getSpecDir().get()).getFiles() - for (StoneConfig config in getStoneConfigs().get()) { - config.routeWhitelistFilter = getRouteWhitelistFilter().getOrNull() - } - runStoneGenerator( - getStoneConfigs().get(), - getObjectFactory().fileTree().from(getStoneDir().get()).getDir(), - generatorFile, - specFiles, - getOutputDir().getAsFile().get(), - getPythonCommand().get() - ) - } - - static FileTree getSpecFiles(ObjectFactory objectFactory, String specDir) { - def fileTree = objectFactory.fileTree().from(specDir) - fileTree.include('**/*.stone') - return fileTree - } - - def runStoneGenerator( - List configs, - File stoneDir, - File generatorFile, - Collection specFiles, - File outputDir, - String pythonCommand - ) { - def srcOutputDir = new File(outputDir, "src") - def refsFile = new File(outputDir, "refs/javadoc-refs.json") - def logFile = new File(outputDir, "log/stone.log") - - // delete output dir for a clean build - if (outputDir.exists()) { - if (!outputDir.deleteDir()) { - throw new GradleException("Failed to delete output directory: ${outputDir.absolutePath}") - } - } - - srcOutputDir.mkdirs() - logFile.parentFile.mkdirs() - refsFile.parentFile.mkdirs() - - boolean isFirst = true - - for (StoneConfig c : configs) { - boolean append = !isFirst - if (c.dataTypesOnly) { - // generate only data types. This is a much simpler call - if (c.client) { - throw new GradleException("Cannot specify dataTypesOnly and clients for Stone generation.") - } - - - getExecOperations().exec { - standardOutput = new FileOutputStream(logFile, append) - commandLine pythonCommand, "-m", "stone.cli" - - environment PYTHONPATH: stoneDir.absolutePath - - if (isFirst) { - args "--clean-build" - } - if (c.routeWhitelistFilter) { - args "--route-whitelist-filter", config.routeWhitelistFilter - } - args generatorFile.absolutePath - args srcOutputDir.absolutePath - args specFiles.collect({ f -> f.absolutePath }) - args "--" - args "--package", c.packageName - args "--data-types-only" - } - } else { - def client = c.client - def routeFilters = [c.globalRouteFilter, client.routeFilter] - def routeFilter = routeFilters\ - .findAll { filter -> filter != null }\ - .collect { filter -> "(${filter})" }\ - .join " and " - - getExecOperations().exec { - standardOutput = new FileOutputStream(logFile, append) - commandLine pythonCommand, "-m", "stone.cli" - - environment PYTHONPATH: stoneDir.absolutePath - if (isFirst) { - args "--clean-build" - } - args "--attribute", ":all" - if (routeFilter) { - args "--filter-by-route-attr", routeFilter - } - if (c.routeWhitelistFilter) { - args "--route-whitelist-filter", c.routeWhitelistFilter - } - args generatorFile.absolutePath - args srcOutputDir.absolutePath - args specFiles.collect({ f -> f.absolutePath }) - args "--" - args "--package", c.packageName - args "--javadoc-refs", refsFile.absolutePath - - if (client.name) { - args "--client-class", client.name - } - if (client.javadoc != null) { - args "--client-javadoc", client.javadoc - } - if (client.requestsClassnamePrefix != null) { - args "--requests-classname-prefix", client.requestsClassnamePrefix - } - if (client.unusedClassesToGenerate != null) { - args "--unused-classes-to-generate", client.unusedClassesToGenerate - } - } - } - isFirst = false - } - } -} - -// add generateStone task for all source sets (e.g. generateTestStone, etc) -project.sourceSets.all { SourceSet sourceSet -> - def taskName = "main" == sourceSet.name ? "generateStone" : "generate${sourceSet.name.capitalize()}Stone" - tasks.register(taskName, StoneTask) { - description "Generate Stone Java source files for ${sourceSet.name}." - - def specDirPropName = "com.dropbox.api.${sourceSet.name}.specDir".toString() - def mySpecDir = project.properties.get(specDirPropName, "src/${sourceSet.name}/stone") - specDir mySpecDir - def routeWhitelistFilterPropName = "com.dropbox.api.${sourceSet.name}.routeWhitelistFilter".toString() - generatorDir 'generator' - stoneDir 'stone' - routeWhitelistFilter project.properties.get(routeWhitelistFilterPropName, null) - pythonCommand 'python' - outputDir "${project.getProjectDir()}/generated_stone_source/${sourceSet.name}" - - inputs.dir { project.fileTree(dir: generatorDir, exclude: '**/*.pyc') }.withPropertyName("stone").withPathSensitivity(PathSensitivity.RELATIVE) - inputs.dir(getSpecFiles(objects, getSpecDir().get())).withPathSensitivity(PathSensitivity.RELATIVE).withPropertyName("stoneSpec").skipWhenEmpty(true) - inputs.property "configs", { new groovy.json.JsonBuilder(getStoneConfigs().get()).toString() } - outputs.dir { getOutputDir().get() }.withPropertyName("generatedStone") - outputs.cacheIf { true } - } - - sourceSet.java.srcDir project.tasks."${taskName}".getOutputDir().get().toString() + "/src" - Task compile = project.tasks.getByName(sourceSet.getCompileTaskName("java")) - compile.dependsOn project.tasks."${taskName}" -} - - -generateStone { - String unusedClassesToGenerate = 'AuthError, PathRoot, PathRootError, AccessError, RateLimitError' - String packageName = 'com.dropbox.core.v2' - String globalRouteFilter = 'alpha_group=null and beta_group=null' - config [ - new StoneConfig( - packageName: packageName, - globalRouteFilter: globalRouteFilter, - client: new ClientSpec( - name: 'DbxClientV2Base', - javadoc: 'Base class for user auth clients.', - requestsClassnamePrefix: "DbxUser", - routeFilter: 'auth="user" or auth="noauth" or auth="app, user"', - unusedClassesToGenerate: unusedClassesToGenerate, - ), - ), - new StoneConfig( - packageName: packageName, - globalRouteFilter: globalRouteFilter, - client: new ClientSpec( - name: 'DbxTeamClientV2Base', - javadoc: 'Base class for team auth clients.', - requestsClassnamePrefix: 'DbxTeam', - routeFilter: 'auth="team"', - ), - ), - new StoneConfig( - packageName: packageName, - globalRouteFilter: globalRouteFilter, - client: new ClientSpec( - name: 'DbxAppClientV2Base', - javadoc: 'Base class for app auth clients.', - requestsClassnamePrefix: "DbxApp", - routeFilter: 'auth="app" or auth="app, user"', - ) - ), - - ] -} - -generateTestStone { - String packageName = 'com.dropbox.core.stone' - config [ - new StoneConfig( - packageName: packageName, - dataTypesOnly: true, - ), - new StoneConfig( - packageName: packageName, - client: new ClientSpec( - name: 'DbxClientV2Base', - javadoc: 'TestClass.', - requestsClassnamePrefix: "DbxTest", - ) - ), - ] -} diff --git a/examples/build.gradle b/examples/build.gradle index f670b1ea7..b3160eeb8 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -8,6 +8,7 @@ buildscript { classpath "com.android.tools.build:gradle:7.2.1" classpath files('../gradle/dropbox-pem-converter-plugin') classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21" + classpath("com.dropbox.gradle.plugins:stone-java-gradle-plugin") } } plugins { diff --git a/examples/settings.gradle b/examples/settings.gradle index 7c64767b4..a6cdc9575 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -20,4 +20,10 @@ dependencyResolutionManagement { from(files("../gradle/dropboxJavaSdkLibs.versions.toml")) } } -} \ No newline at end of file +} + +includeBuild("../stone-java-gradle-plugin") { + dependencySubstitution { + substitute module('com.dropbox.gradle.plugins:stone-java-gradle-plugin') + } +} diff --git a/settings.gradle b/settings.gradle index c868c3f4b..f094799e5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,4 +8,10 @@ dependencyResolutionManagement { from(files("gradle/dropboxJavaSdkLibs.versions.toml")) } } -} \ No newline at end of file +} + +includeBuild("stone-java-gradle-plugin") { + dependencySubstitution { + substitute module('com.dropbox.gradle.plugins:stone-java-gradle-plugin') + } +} diff --git a/stone-java-gradle-plugin/README.md b/stone-java-gradle-plugin/README.md new file mode 100644 index 000000000..7ce20757c --- /dev/null +++ b/stone-java-gradle-plugin/README.md @@ -0,0 +1,3 @@ +# Stone Gradle Plugin + +Used to generate Java Code from Stone API specs. \ No newline at end of file diff --git a/stone-java-gradle-plugin/build.gradle.kts b/stone-java-gradle-plugin/build.gradle.kts new file mode 100644 index 000000000..31d7c1117 --- /dev/null +++ b/stone-java-gradle-plugin/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + `kotlin-dsl` +} + +group = "com.dropbox.gradle.plugins" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() + google() + gradlePluginPortal() +} + +dependencies { + implementation(dropboxJavaSdkLibs.kotlin.gradle.plugin) +} + +gradlePlugin { + plugins { + fun createPlugin(id: String, className: String) { + plugins.create(id) { + this.id = id + implementationClass = className + } + } + createPlugin( + id = "com.dropbox.stone.java", + className = "com.dropbox.stone.java.StonePlugin" + ) + } +} diff --git a/stone-java-gradle-plugin/settings.gradle b/stone-java-gradle-plugin/settings.gradle new file mode 100644 index 000000000..68abea72b --- /dev/null +++ b/stone-java-gradle-plugin/settings.gradle @@ -0,0 +1,9 @@ +rootProject.setName("stone-java-gradle-plugin") + +dependencyResolutionManagement { + versionCatalogs { + dropboxJavaSdkLibs { + from(files("../gradle/dropboxJavaSdkLibs.versions.toml")) + } + } +} diff --git a/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StonePlugin.kt b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StonePlugin.kt new file mode 100644 index 000000000..1d91790f8 --- /dev/null +++ b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StonePlugin.kt @@ -0,0 +1,85 @@ +package com.dropbox.stone.java + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.TaskProvider + +/** + * Android Application Convention Plugin + * + * We'll apply all of Dropbox's Custom Config here. + */ +public class StonePlugin : Plugin { + + private lateinit var project: Project + + override fun apply(target: Project) { + this.project = target + val javaPluginExtension = project.extensions.getByType(JavaPluginExtension::class.java) + + // add generateStone task for all source sets (e.g. generateTestStone, etc) + javaPluginExtension.sourceSets.forEach { sourceSet: SourceSet -> + createTaskForSourceSet(project, sourceSet) + } + } + + private fun createTaskForSourceSet( + project: Project, + sourceSet: SourceSet + ) { + val isMainSourceSet = sourceSet.name == "main" + val taskName: String = if (isMainSourceSet) { + "generateStone" + } else { + "generate${sourceSet.name.capitalize()}Stone" + } + val stoneTaskProvider: TaskProvider = project.tasks.register(taskName, StoneTask::class.java) + stoneTaskProvider.get().also { stoneTask -> + stoneTask.description = "Generate Stone Java source files for ${sourceSet.name}." + + val routeWhitelistFilterPropName = "com.dropbox.api.${sourceSet.name}.routeWhitelistFilter" + val routeWhitelistFilterValue: String? = project.properties[routeWhitelistFilterPropName] as String? + routeWhitelistFilterValue?.let { + stoneTask.routeWhitelistFilter(it) + } + + val specDirPropName = "com.dropbox.api.${sourceSet.name}.specDir" + val specDirPropNameValue: String? = project.properties[specDirPropName] as String? + val mySpecDir: String = specDirPropNameValue ?: "src/${sourceSet.name}/stone" + + stoneTask.specDir(mySpecDir) + stoneTask.generatorDir("generator") + stoneTask.stoneDir("stone") + stoneTask.pythonCommand("python") + stoneTask.outputDir("${project.buildDir}/generated/source/stone/${sourceSet.name}") + stoneTask.inputs + .dir { project.fileTree(stoneTask.getGeneratorDir()) { filter { !it.name.endsWith(".pyc") } } } + .withPropertyName("stone") + .withPathSensitivity(PathSensitivity.RELATIVE) + + val specDir = stoneTask.getSpecDir().get() + stoneTask.inputs + .dir(stoneTask.getSpecFiles(project.objects, specDir)) + .withPathSensitivity(PathSensitivity.RELATIVE) + .withPropertyName("stoneSpec") + .skipWhenEmpty(true) + stoneTask.outputs + .dir { stoneTask.getOutputDir() } + .withPropertyName("generatedStone") + stoneTask.outputs.cacheIf { true } + + val compile: Task = project.tasks.getByName(sourceSet.getCompileTaskName("java")) + compile.dependsOn(stoneTask) + + project.afterEvaluate { + // Must run afterEvaluate so we can honor any output directory specified in user's config + sourceSet.java.srcDir("${stoneTask.getOutputDir().get()}/src") + } + } + } + +} diff --git a/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StoneTask.kt b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StoneTask.kt new file mode 100644 index 000000000..8e6a3cd69 --- /dev/null +++ b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StoneTask.kt @@ -0,0 +1,220 @@ +package com.dropbox.stone.java + +import com.dropbox.stone.java.model.StoneConfig +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileTree +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecOperations +import java.io.File +import java.io.FileOutputStream +import java.nio.file.Files +import javax.inject.Inject + +abstract class StoneTask : DefaultTask() { + + init { + group = "Dropbox Stone Java Generator" + } + + @Input + abstract fun getStoneConfigs(): ListProperty + + @Input + abstract fun getGeneratorDir(): Property + + @Input + abstract fun getSpecDir(): Property + + @Optional + @Input + abstract fun getRouteWhitelistFilter(): Property + + @Input + abstract fun getStoneDir(): Property + + @Input + abstract fun getPythonCommand(): Property + + @OutputDirectory + abstract fun getOutputDir(): DirectoryProperty + + @Inject + abstract fun getObjectFactory(): ObjectFactory + + @Inject + abstract fun getExecOperations(): ExecOperations + + + public fun config(configs: List) { + getStoneConfigs().set(configs) + } + + public fun generatorDir(generatorDir: String) { + getGeneratorDir().set(generatorDir) + } + + public fun routeWhitelistFilter(routeWhitelistFilter: String) { + getRouteWhitelistFilter().set(routeWhitelistFilter) + } + + public fun stoneDir(stoneDir: String) { + getStoneDir().set(stoneDir) + } + + public fun pythonCommand(pythonCommand: String) { + getPythonCommand().set(pythonCommand) + } + + public fun specDir(specDir: String) { + getSpecDir().set(specDir) + } + + public fun outputDir(outputDir: String) { + getOutputDir().set(File(outputDir)) + } + + @TaskAction + public fun processStone() { + val generatorFileTree = getObjectFactory().fileTree().from(getGeneratorDir().get()) + generatorFileTree.include("**/*stoneg.py") + val generatorFile = generatorFileTree.singleFile + val specFiles = getSpecFiles(getObjectFactory(), getSpecDir().get()).files + for (config: StoneConfig in getStoneConfigs().get()) { + config.routeWhitelistFilter = getRouteWhitelistFilter().orNull + } + runStoneGenerator( + getStoneConfigs().get(), + getObjectFactory().fileTree().from(getStoneDir().get()).getDir(), + generatorFile, + specFiles, + getOutputDir().asFile.get(), + getPythonCommand().get() + ) + } + + fun getSpecFiles(objectFactory: ObjectFactory, specDir: String): FileTree { + val fileTree = objectFactory.fileTree().from(specDir) + fileTree.include("**/*.stone") + return fileTree + } + + fun deleteDir(file: File) { + val contents = file.listFiles() + if (contents != null) { + for (f in contents) { + if (!Files.isSymbolicLink(f.toPath())) { + deleteDir(f) + } + } + } + file.delete() + } + + fun runStoneGenerator( + configs: List, + stoneDir: File, + generatorFile: File, + specFiles: Collection, + outputDir: File, + pythonCommand: String, + ) { + val srcOutputDir = File(outputDir, "src") + val refsFile = File(outputDir, "refs/javadoc-refs.json") + val logFile = File(outputDir, "log/stone.log") + + // delete output dir for a clean build + if (outputDir.exists()) { + try { + deleteDir(outputDir) + } catch (e: Exception) { + throw GradleException("Failed to delete output directory: ${outputDir.absolutePath}") + } + } + + srcOutputDir.mkdirs() + logFile.parentFile.mkdirs() + refsFile.parentFile.mkdirs() + + configs.forEachIndexed { index, stoneConfig -> + val isFirst = (index == 0) + val append: Boolean = !isFirst + if (stoneConfig.dataTypesOnly) { + // generate only data types. This is a much simpler call + if (stoneConfig.client != null) { + throw GradleException("Cannot specify dataTypesOnly and clients for Stone generation.") + } + getExecOperations().exec { + standardOutput = FileOutputStream(logFile, append) + commandLine(pythonCommand, "-m", "stone.cli") + + environment["PYTHONPATH"] = stoneDir.absolutePath + + if (isFirst) { + args("--clean-build") + } + if (stoneConfig.routeWhitelistFilter != null) { + args("--route-whitelist-filter", getRouteWhitelistFilter().get()) + } + args(generatorFile.absolutePath) + args(srcOutputDir.absolutePath) + specFiles.map { f -> f.absolutePath }.forEach { + args(it) + } + args("--") + args("--package", stoneConfig.packageName) + args("--data-types-only") + } + } else { + val client = stoneConfig.client + val routeFilters = listOf(stoneConfig.globalRouteFilter, client?.routeFilter).filterNotNull() + val routeFilter = routeFilters.joinToString(" and ") { filter -> "(${filter})" } + + getExecOperations().exec { + standardOutput = FileOutputStream(logFile, append) + environment["PYTHONPATH"] = stoneDir.absolutePath + commandLine(this@StoneTask.getPythonCommand().get(), "-m", "stone.cli") + + if (isFirst) { + args("--clean-build") + } + args("--attribute", ":all") + if (routeFilter.isNotEmpty()) { + args("--filter-by-route-attr", routeFilter) + } + if (stoneConfig.routeWhitelistFilter != null) { + args("--route-whitelist-filter", stoneConfig.routeWhitelistFilter) + } + args(generatorFile.absolutePath) + args(srcOutputDir.absolutePath) + specFiles.map { f -> f.absolutePath }.forEach { + args(it) + } + args("--") + args("--package", stoneConfig.packageName) + args("--javadoc-refs", refsFile.absolutePath) + + if (client?.name != null) { + args("--client-class", client.name) + } + if (client?.javadoc != null) { + args("--client-javadoc", client.javadoc) + } + if (client?.requestsClassnamePrefix != null) { + args("--requests-classname-prefix", client.requestsClassnamePrefix) + } + if (client?.unusedClassesToGenerate != null) { + args("--unused-classes-to-generate", client.unusedClassesToGenerate) + } + } + } + } + } +} \ No newline at end of file diff --git a/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/ClientSpec.kt b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/ClientSpec.kt new file mode 100644 index 000000000..626278755 --- /dev/null +++ b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/ClientSpec.kt @@ -0,0 +1,11 @@ +package com.dropbox.stone.java.model + +import java.io.Serializable + +class ClientSpec( + var name: String? = null, + var javadoc: String? = null, + var routeFilter: String? = null, + var requestsClassnamePrefix: String? = null, + var unusedClassesToGenerate: String? = null, +) : Serializable \ No newline at end of file diff --git a/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/StoneConfig.kt b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/StoneConfig.kt new file mode 100644 index 000000000..501925ea5 --- /dev/null +++ b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/StoneConfig.kt @@ -0,0 +1,11 @@ +package com.dropbox.stone.java.model + +import java.io.Serializable + +class StoneConfig( + var packageName: String = "com.dropbox.stone", + var globalRouteFilter: String? = null, + var dataTypesOnly: Boolean = false, + var client: ClientSpec? = null, + var routeWhitelistFilter: String? = null, +) : Serializable \ No newline at end of file