Skip to content

Commit 12ad815

Browse files
authored
Merge pull request #1347 from Stefterv/library-plugin
Gradle Plugin for creating Processing Libraries
2 parents c81cda7 + eddeda4 commit 12ad815

File tree

7 files changed

+330
-12
lines changed

7 files changed

+330
-12
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
plugins {
2+
`java-gradle-plugin`
3+
kotlin("jvm") version "2.2.20"
4+
}
5+
6+
gradlePlugin {
7+
plugins {
8+
create("processing.library") {
9+
id = "org.processing.library"
10+
implementationClass = "ProcessingLibraryPlugin"
11+
}
12+
}
13+
}
14+
15+
repositories {
16+
mavenCentral()
17+
}
18+
19+
dependencies {
20+
testImplementation(kotlin("test"))
21+
}
22+
23+
tasks.test {
24+
useJUnitPlatform()
25+
}
26+
kotlin {
27+
jvmToolchain(17)
28+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import org.gradle.api.DefaultTask
2+
import org.gradle.api.GradleException
3+
import org.gradle.api.tasks.Input
4+
import org.gradle.api.tasks.OutputDirectory
5+
import org.gradle.api.tasks.TaskAction
6+
import org.gradle.api.tasks.bundling.Jar
7+
import org.gradle.api.tasks.javadoc.Javadoc
8+
9+
abstract class BundleLibraryFilesTask : DefaultTask() {
10+
@Input
11+
var configuration: ProcessingLibraryConfiguration? = null
12+
13+
@OutputDirectory
14+
val outputDir = project.objects.directoryProperty()
15+
16+
init {
17+
outputDir.convention(project.layout.buildDirectory.dir("library"))
18+
}
19+
20+
@TaskAction
21+
fun bundle() {
22+
val configuration = configuration
23+
?: throw GradleException("Processing library configuration must be provided.")
24+
val libraryName = configuration.name ?: project.name
25+
26+
val buildDir = project.layout.buildDirectory.dir("library/$libraryName").get().asFile
27+
buildDir.mkdirs()
28+
29+
val libDir = buildDir.resolve("library")
30+
libDir.mkdirs()
31+
32+
// Copy the jar file
33+
val jarFile = project.tasks.named("jar", Jar::class.java).get().archiveFile.get().asFile
34+
jarFile.copyTo(libDir.resolve("$libraryName.jar"), overwrite = true)
35+
36+
// Copy all runtime dependencies
37+
val runtimeClasspath = project.configurations.getByName("runtimeClasspath")
38+
runtimeClasspath.resolvedConfiguration.resolvedArtifacts.forEach { artifact ->
39+
val depFile = artifact.file
40+
depFile.copyTo(libDir.resolve(depFile.name), overwrite = true)
41+
}
42+
43+
// Copy Examples folder
44+
val examplesDir = project.projectDir.resolve("examples")
45+
if (!examplesDir.exists() || !examplesDir.isDirectory) {
46+
throw GradleException("Examples folder not found in project directory.")
47+
}
48+
examplesDir.copyRecursively(buildDir.resolve("examples"), overwrite = true)
49+
50+
// Copy javadoc to reference folder
51+
val docsDir = project.tasks.named("javadoc", Javadoc::class.java).get().destinationDir
52+
docsDir?.copyRecursively(buildDir.resolve("reference"), overwrite = true)
53+
54+
// Create library.properties file
55+
val propertiesFile = buildDir.resolve("library.properties")
56+
propertiesFile.bufferedWriter().use { writer ->
57+
val properties = mapOf(
58+
"name" to libraryName,
59+
"version" to (configuration.version ?: "1.0.0"),
60+
"prettyVersion" to (configuration.prettyVersion ?: configuration.version ?: "1.0.0"),
61+
"authors" to (configuration.authors.entries.joinToString(", ") { "[${it.key}](${it.value})" }),
62+
"url" to configuration.url,
63+
"category" to configuration.categories.joinToString(", "),
64+
"sentence" to configuration.sentence,
65+
"paragraph" to configuration.paragraph,
66+
"minRevision" to configuration.minRevision,
67+
"maxRevision" to configuration.maxRevision
68+
)
69+
properties
70+
.filter { it.value != null && it.value.toString().isNotEmpty() }
71+
.forEach { (key, value) ->
72+
writer.write("$key=$value\n")
73+
}
74+
}
75+
propertiesFile.copyTo(buildDir.resolve("../$libraryName.txt"), overwrite = true)
76+
}
77+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import org.gradle.api.Action
2+
import org.gradle.api.model.ObjectFactory
3+
import java.io.Serializable
4+
import javax.inject.Inject
5+
6+
open class ProcessingLibraryExtension @Inject constructor(objects: ObjectFactory) {
7+
var version: String? = null
8+
val library = objects.newInstance(ProcessingLibraryConfiguration::class.java)
9+
fun library(action: Action<ProcessingLibraryConfiguration>) {
10+
action.execute(library)
11+
}
12+
}
13+
14+
open class ProcessingLibraryConfiguration @Inject constructor() : Serializable {
15+
/**
16+
* Name of the library. If not set, the project name will be used.
17+
*/
18+
var name: String? = null
19+
20+
/**
21+
* Version number of the library.
22+
*/
23+
var version: Int? = null
24+
25+
/**
26+
* Pretty version string of the library.
27+
*/
28+
var prettyVersion: String? = null
29+
30+
/**
31+
* Map of author URLs to author names.
32+
*/
33+
var authors: Map<String, String> = emptyMap()
34+
35+
/**
36+
* URL of the library where more information can be found.
37+
*/
38+
var url: String? = null
39+
40+
/**
41+
* List of categories the library belongs to.
42+
*/
43+
var categories: List<String> = emptyList()
44+
45+
/**
46+
* A one-line sentence describing the library.
47+
*/
48+
var sentence: String? = null
49+
50+
/**
51+
* A longer paragraph describing the library.
52+
*/
53+
var paragraph: String? = null
54+
55+
/**
56+
* Minimum Processing revision required.
57+
*/
58+
var minRevision: Int? = null
59+
60+
/**
61+
* Maximum Processing revision supported.
62+
*/
63+
var maxRevision: Int? = null
64+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import org.gradle.api.GradleException
2+
import org.gradle.api.Plugin
3+
import org.gradle.api.Project
4+
import org.gradle.api.plugins.JavaPlugin
5+
import org.gradle.api.plugins.JavaPluginExtension
6+
import org.gradle.api.tasks.bundling.Jar
7+
import org.gradle.api.tasks.bundling.Zip
8+
import org.gradle.api.tasks.javadoc.Javadoc
9+
import org.gradle.jvm.toolchain.JavaLanguageVersion
10+
import java.util.prefs.Preferences
11+
12+
class ProcessingLibraryPlugin : Plugin<Project> {
13+
14+
override fun apply(target: Project) {
15+
val extension = target.extensions.create("processing", ProcessingLibraryExtension::class.java)
16+
target.plugins.apply(JavaPlugin::class.java)
17+
18+
target.repositories.mavenCentral()
19+
target.repositories.maven { it.setUrl("https://jogamp.org/deployment/maven/") }
20+
21+
// Grab processing core if available, otherwise use the published version
22+
val hasCore = try {
23+
val core = target.project(":core")
24+
target.dependencies.add("compileOnly", core)
25+
true
26+
} catch (_: Exception) {
27+
false
28+
}
29+
30+
target.afterEvaluate {
31+
if (!hasCore) {
32+
if (extension.version == null) {
33+
throw GradleException("Processing library version must be specified, please set processing.version in your build.gradle.kts")
34+
}
35+
val processingVersion = extension.version
36+
target.dependencies.add("compileOnly", "org.processing:core:$processingVersion")
37+
}
38+
}
39+
target.extensions.configure(JavaPluginExtension::class.java) { extension ->
40+
extension.toolchain.languageVersion.set(JavaLanguageVersion.of(17))
41+
}
42+
43+
target.plugins.withType(JavaPlugin::class.java) {
44+
val jarTask = target.tasks.named("jar", Jar::class.java)
45+
val javaDocTask = target.tasks.named("javadoc", Javadoc::class.java)
46+
47+
val bundleTask = target.tasks.register("bundleLibrary", BundleLibraryFilesTask::class.java) { task ->
48+
task.configuration = extension.library
49+
task.group = "processing"
50+
task.description = "Creates the Processing library folder with jar, library.properties, and examples."
51+
task.dependsOn(jarTask, javaDocTask)
52+
}
53+
54+
val zipTask = target.tasks.register("zipLibrary", Zip::class.java) { task ->
55+
task.apply {
56+
val libraryName = extension.library.name ?: target.name
57+
val sourceDir = bundleTask.get().outputDir.get().asFile
58+
59+
group = "processing"
60+
description = "Creates a zip & pdex archive of the Processing library folder."
61+
dependsOn(bundleTask)
62+
include("${libraryName}/**")
63+
64+
archiveFileName.set("$libraryName.zip")
65+
from(sourceDir)
66+
destinationDirectory.set(sourceDir)
67+
doLast {
68+
val zip = task.outputs.files.files.first()
69+
zip.copyTo(sourceDir.resolve("$libraryName.pdex"), overwrite = true)
70+
}
71+
}
72+
}
73+
74+
target.tasks.register("installLibrary") { task ->
75+
task.apply {
76+
group = "processing"
77+
dependsOn(zipTask)
78+
doLast {
79+
val preferences = Preferences.userRoot().node("org/processing/app")
80+
81+
val semverRe = Regex("""^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?""")
82+
fun semverKey(v: String): Triple<Long, Boolean, String> {
83+
val m = semverRe.find(v)
84+
val maj = m?.groupValues?.getOrNull(1)?.toLongOrNull() ?: 0L
85+
val min = m?.groupValues?.getOrNull(2)?.toLongOrNull() ?: 0L
86+
val pat = m?.groupValues?.getOrNull(3)?.toLongOrNull() ?: 0L
87+
val pre = m?.groupValues?.getOrNull(4)
88+
val packed = (maj shl 40) or (min shl 20) or pat
89+
return Triple(packed, pre == null, pre ?: "")
90+
}
91+
92+
val installLocations = preferences.get("installLocations", "")
93+
.split(",")
94+
.filter { it.isNotEmpty() }
95+
.mapNotNull {
96+
val parts = it.split("^")
97+
if (parts.size < 2) null else parts[1] to parts[0] // version to path
98+
}
99+
.sortedWith(Comparator { a, b ->
100+
val ka = semverKey(a.first)
101+
val kb = semverKey(b.first)
102+
when {
103+
ka.first != kb.first -> kb.first.compareTo(ka.first)
104+
ka.second != kb.second -> kb.second.compareTo(ka.second)
105+
else -> kb.third.compareTo(ka.third)
106+
}
107+
})
108+
109+
val installPath = installLocations.firstOrNull()?.second
110+
?: throw GradleException("Could not find Processing install location in preferences.")
111+
112+
val libraryName = extension.library.name ?: target.name
113+
val sourceDir = bundleTask.get().outputDir.get().asFile.resolve("$libraryName.pdex")
114+
115+
ProcessBuilder()
116+
.command(installPath, sourceDir.absolutePath)
117+
.inheritIO()
118+
.start()
119+
}
120+
}
121+
}
122+
123+
}
124+
}
125+
}

gradle/plugins/settings.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
plugins {
2+
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
3+
}
4+
5+
include("library")

java/libraries/dxf/build.gradle.kts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
plugins{
2-
java
2+
id("org.processing.library")
3+
}
4+
5+
processing {
6+
library {
7+
version = 1
8+
prettyVersion = "1.0.0"
9+
10+
authors = mapOf(
11+
"The Processing Foundation" to "https://processing.org"
12+
)
13+
url = "https://processing.org/"
14+
categories = listOf("file", "exporter", "dxf")
15+
16+
sentence = "DXF export library for Processing"
17+
paragraph =
18+
"This library allows you to export your Processing drawings as DXF files, which can be opened in CAD applications."
19+
20+
}
321
}
422

523
sourceSets {
@@ -9,27 +27,23 @@ sourceSets {
927
}
1028
}
1129
}
12-
repositories{
13-
mavenCentral()
14-
maven("https://jogamp.org/deployment/maven/")
15-
}
16-
1730
dependencies{
18-
compileOnly(project(":core"))
19-
2031
implementation("com.lowagie:itext:2.1.7")
2132
}
2233

23-
tasks.register<Copy>("createLibrary"){
34+
/**
35+
* @deprecated Legacy task, use 'bundleLibrary' task provided by 'org.processing.library' plugin
36+
*/
37+
tasks.register<Copy>("createLibrary") {
2438
dependsOn("jar")
2539
into(layout.buildDirectory.dir("library"))
2640

27-
from(layout.projectDirectory){
28-
include ("library.properties")
41+
from(layout.projectDirectory) {
42+
include("library.properties")
2943
include("examples/**")
3044
}
3145

32-
from(configurations.runtimeClasspath){
46+
from(configurations.runtimeClasspath) {
3347
into("library")
3448
}
3549

settings.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
rootProject.name = "processing"
2+
3+
pluginManagement {
4+
includeBuild("gradle/plugins")
5+
}
6+
27
include(
38
"core",
49
"core:examples",

0 commit comments

Comments
 (0)