From 1b8a8ed461cb39eb0489564589e144178fb1bef5 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Wed, 5 Feb 2025 09:10:42 +0100 Subject: [PATCH] Gradle Plugin from PoC --- .gitignore | 6 +- core/build.gradle.kts | 2 +- gradle/libs.versions.toml | 3 +- java/gradle/build.gradle.kts | 34 +++++ .../src/main/kotlin/ProcessingPlugin.kt | 143 ++++++++++++++++++ java/gradle/src/main/kotlin/ProcessingTask.kt | 73 +++++++++ java/preprocessor/build.gradle.kts | 2 + settings.gradle.kts | 3 +- 8 files changed, 260 insertions(+), 6 deletions(-) create mode 100644 java/gradle/build.gradle.kts create mode 100644 java/gradle/src/main/kotlin/ProcessingPlugin.kt create mode 100644 java/gradle/src/main/kotlin/ProcessingTask.kt diff --git a/.gitignore b/.gitignore index ebdb29b67..ab72f2264 100644 --- a/.gitignore +++ b/.gitignore @@ -98,7 +98,9 @@ bin-test processing-examples # Maven ignores +/.kotlin/sessions .gradle +.build/ core/build/ build/publish/ app/build @@ -108,7 +110,5 @@ java/build/ /java/libraries/svg/bin /java/preprocessor/build /java/lsp/build -/.kotlin/sessions /core/examples/build - -.build/ +/java/gradle/build \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index d4a1dcacb..7f7438d77 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -6,7 +6,7 @@ plugins { alias(libs.plugins.mavenPublish) } -group = "org.processing" +version = rootProject.version repositories { mavenCentral() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b3203cbcd..c62f17a04 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,4 +31,5 @@ kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } download = { id = "de.undercouch.download", version = "5.6.0" } -mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.30.0" } \ No newline at end of file +mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.30.0" } +gradlePublish = { id = "com.gradle.plugin-publish", version = "1.2.1" } \ No newline at end of file diff --git a/java/gradle/build.gradle.kts b/java/gradle/build.gradle.kts new file mode 100644 index 000000000..c08e316ef --- /dev/null +++ b/java/gradle/build.gradle.kts @@ -0,0 +1,34 @@ +plugins{ + `java-gradle-plugin` + alias(libs.plugins.gradlePublish) + + kotlin("jvm") version libs.versions.kotlin +} + +version = rootProject.version + +repositories { + mavenCentral() + maven { url = uri("https://jogamp.org/deployment/maven") } +} + +dependencies{ + implementation(project(":java:preprocessor")) + + implementation("org.jetbrains.compose:compose-gradle-plugin:1.7.3") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21") +} + +gradlePlugin{ + plugins{ + create("processing"){ + id = "org.processing" + implementationClass = "org.processing.gradle.ProcessingPlugin" + } + } +} +publishing{ + repositories{ + mavenLocal() + } +} \ No newline at end of file diff --git a/java/gradle/src/main/kotlin/ProcessingPlugin.kt b/java/gradle/src/main/kotlin/ProcessingPlugin.kt new file mode 100644 index 000000000..e48c9b4d4 --- /dev/null +++ b/java/gradle/src/main/kotlin/ProcessingPlugin.kt @@ -0,0 +1,143 @@ +package org.processing.gradle + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.internal.file.DefaultSourceDirectorySet +import org.gradle.api.internal.tasks.TaskDependencyFactory +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPluginExtension +import org.jetbrains.compose.ComposeExtension +import org.jetbrains.compose.desktop.DesktopExtension +import java.io.File +import java.util.* +import javax.inject.Inject + +class ProcessingPlugin @Inject constructor(private val objectFactory: ObjectFactory) : Plugin { + override fun apply(project: Project) { + project.plugins.apply(JavaPlugin::class.java) + + // TODO: Only set the build directory when run from the Processing plugin + project.layout.buildDirectory.set(project.layout.projectDirectory.dir(".processing")) + + project.plugins.apply("org.jetbrains.compose") + project.plugins.apply("org.jetbrains.kotlin.jvm") + + project.dependencies.add("implementation", "org.processing:core:4.3.1") + project.dependencies.add("implementation", project.fileTree("src").apply { include("**/code/*.jar") }) + + // Base JOGL and Gluegen dependencies + project.dependencies.add("runtimeOnly", "org.jogamp.jogl:jogl-all-main:2.5.0") + project.dependencies.add("runtimeOnly", "org.jogamp.gluegen:gluegen-rt-main:2.5.0") + + // TODO: Only add the native dependencies for the platform the user is building for + + // MacOS specific native dependencies + project.dependencies.add("runtimeOnly", "org.jogamp.jogl:jogl-all:2.5.0:natives-macosx-universal") + project.dependencies.add("runtimeOnly", "org.jogamp.gluegen:gluegen-rt:2.5.0:natives-macosx-universal") + + // Windows specific native dependencies + project.dependencies.add("runtimeOnly", "org.jogamp.jogl:jogl-all:2.5.0:natives-windows-amd64") + project.dependencies.add("runtimeOnly", "org.jogamp.gluegen:gluegen-rt:2.5.0:natives-windows-amd64") + + // Linux specific native dependencies + project.dependencies.add("runtimeOnly", "org.jogamp.jogl:jogl-all:2.5.0:natives-linux-amd64") + project.dependencies.add("runtimeOnly", "org.jogamp.gluegen:gluegen-rt:2.5.0:natives-linux-amd64") + + // NativeWindow dependencies for all platforms + project.dependencies.add("implementation", "org.jogamp.jogl:nativewindow:2.5.0") + project.dependencies.add("runtimeOnly", "org.jogamp.jogl:nativewindow:2.5.0:natives-macosx-universal") + project.dependencies.add("runtimeOnly", "org.jogamp.jogl:nativewindow:2.5.0:natives-windows-amd64") + project.dependencies.add("runtimeOnly", "org.jogamp.jogl:nativewindow:2.5.0:natives-linux-amd64") + + project.repositories.add(project.repositories.maven { it.setUrl("https://jogamp.org/deployment/maven") }) + project.repositories.add(project.repositories.mavenCentral()) + + project.extensions.configure(ComposeExtension::class.java) { extension -> + extension.extensions.getByType(DesktopExtension::class.java).application { application -> + application.mainClass = project.layout.projectDirectory.asFile.name.replace(Regex("[^a-zA-Z0-9_]"), "_") + application.nativeDistributions.modules("java.management") + } + } + + // TODO: Also only do within Processing + project.tasks.named("wrapper").configure { + it.enabled = false + } + + project.tasks.create("sketch").apply { + group = "processing" + description = "Runs the Processing sketch" + dependsOn("run") + } + project.tasks.create("present").apply { + // TODO: Implement dynamic fullscreen by setting the properties and recompiling the sketch every run + group = "processing" + description = "Presents the Processing sketch" + dependsOn("run") + } + project.tasks.create("export").apply { + group = "processing" + description = "Creates a distributable version of the Processing sketch" + dependsOn("createDistributable") + } + + project.extensions.getByType(JavaPluginExtension::class.java).sourceSets.all { sourceSet -> + // TODO: also supporting normal gradle setup + val pdeSourceSet = objectFactory.newInstance( + DefaultPDESourceDirectorySet::class.java, + objectFactory.sourceDirectorySet("${sourceSet.name}.pde", "${sourceSet.name} Processing Source") + ).apply { + filter.include("**/*.pde") + filter.exclude("${project.layout.buildDirectory.asFile.get().name}/**") + + srcDir("./") + } + sourceSet.allSource.source(pdeSourceSet) + + val outputDirectory = project.layout.buildDirectory.file( "generated/pde/" + sourceSet.name).get().asFile + sourceSet.java.srcDir(outputDirectory) + + // TODO: Support multiple sketches? + // TODO: Preprocess PDE files in this step so we can add the library dependencies + + val taskName = sourceSet.getTaskName("preprocess", "PDE") + project.tasks.register(taskName, ProcessingTask::class.java) { task -> + task.description = "Processes the ${sourceSet.name} PDE" + task.source = pdeSourceSet + task.outputDirectory = outputDirectory + } + + project.tasks.named( + sourceSet.compileJavaTaskName + ) { task -> task.dependsOn(taskName) } + } + + var settingsFolder = File(System.getProperty("user.home"),".processing") + val osName = System.getProperty("os.name").lowercase() + if (osName.contains("win")) { + settingsFolder = File(System.getenv("APPDATA"), "Processing") + } else if (osName.contains("mac")) { + settingsFolder = File(System.getProperty("user.home"), "Library/Processing") + }else if (osName.contains("nix") || osName.contains("nux")) { + settingsFolder = File(System.getProperty("user.home"), ".processing") + } + + val preferences = File(settingsFolder, "preferences.txt") + val prefs = Properties() + prefs.load(preferences.inputStream()) + + val sketchbook = prefs.getProperty("sketchbook.path.four") + + File(sketchbook, "libraries").listFiles { file -> file.isDirectory + }?.forEach{ + project.dependencies.add("implementation", project.fileTree(it).apply { include("**/*.jar") }) + } + } + abstract class DefaultPDESourceDirectorySet @Inject constructor( + sourceDirectorySet: SourceDirectorySet, + taskDependencyFactory: TaskDependencyFactory + ) : DefaultSourceDirectorySet(sourceDirectorySet, taskDependencyFactory), SourceDirectorySet +} + diff --git a/java/gradle/src/main/kotlin/ProcessingTask.kt b/java/gradle/src/main/kotlin/ProcessingTask.kt new file mode 100644 index 000000000..bca1b1bd3 --- /dev/null +++ b/java/gradle/src/main/kotlin/ProcessingTask.kt @@ -0,0 +1,73 @@ +package org.processing.gradle + +import org.gradle.api.file.* +import org.gradle.api.tasks.* +import org.gradle.internal.file.Deleter +import org.gradle.work.ChangeType +import org.gradle.work.FileChange +import org.gradle.work.InputChanges +import processing.mode.java.preproc.PdePreprocessor +import java.io.File +import java.io.IOException +import java.io.UncheckedIOException +import java.util.concurrent.Callable +import javax.inject.Inject + +abstract class ProcessingTask() : SourceTask() { + @get:OutputDirectory + var outputDirectory: File? = null + + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + @get:IgnoreEmptyDirectories + @get:SkipWhenEmpty + open val stableSources: FileCollection = project.files(Callable { this.source }) + + @TaskAction + fun execute(inputChanges: InputChanges) { + val files: MutableSet = HashSet() + if (inputChanges.isIncremental) { + var rebuildRequired = true + for (fileChange: FileChange in inputChanges.getFileChanges(stableSources)) { + if (fileChange.fileType == FileType.FILE) { + if (fileChange.changeType == ChangeType.REMOVED) { + rebuildRequired = true + break + } + files.add(fileChange.file) + } + } + if (rebuildRequired) { + try { + outputDirectory?.let { deleter.ensureEmptyDirectory(it) } + } catch (ex: IOException) { + throw UncheckedIOException(ex) + } + files.addAll(stableSources.files) + } + } else { + files.addAll(stableSources.files) + } + + val name = project.layout.projectDirectory.asFile.name.replace(Regex("[^a-zA-Z0-9_]"), "_") + val combined = files.joinToString("\n") { it.readText() } + File(outputDirectory, "$name.java") + .bufferedWriter() + .use { out -> + val meta = PdePreprocessor + .builderFor(name) + .build() + .write(out, combined) + + + // TODO: Only import the libraries that are actually used + val importStatement = meta.importStatements + } + } + + @get:Inject + open val deleter: Deleter + get() { + throw UnsupportedOperationException("Decorator takes care of injection") + } +} \ No newline at end of file diff --git a/java/preprocessor/build.gradle.kts b/java/preprocessor/build.gradle.kts index 713e1d5c0..d4f34d99c 100644 --- a/java/preprocessor/build.gradle.kts +++ b/java/preprocessor/build.gradle.kts @@ -6,6 +6,8 @@ plugins{ alias(libs.plugins.mavenPublish) } + + repositories{ mavenCentral() google() diff --git a/settings.gradle.kts b/settings.gradle.kts index 4bdcd880e..c4e6a2d38 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,10 +1,11 @@ -rootProject.name = "processing" +rootProject.name = "org.processing" include( "core", "core:examples", "app", "java", "java:preprocessor", + "java:gradle", "java:libraries:dxf", "java:libraries:io", "java:libraries:net",