From 52f1a18a1bcd45c19b73470d2b9ef865eec4788b Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Wed, 21 Oct 2026 03:36:24 -0400 Subject: [PATCH] Declared sandbox build targets - Gradle exposes Bukkit, API, SPI, and loader targets with isolated build requirements - Build tasks retain verified artifacts inside their project directories - API generation writes a jar without publishing or external copies - Captured source hashes identify sandbox builds - Build instructions describe manager deployment --- README.md | 40 ++-- build-all.sh | 2 +- build.gradle | 207 ++----------------- buildSrc/src/main/java/ApiGenerator.java | 17 +- buildSrc/src/test/java/ApiGeneratorTest.java | 5 +- core/build.gradle | 7 + gradle/multiplexor.gradle | 73 +++++++ 7 files changed, 122 insertions(+), 229 deletions(-) create mode 100644 gradle/multiplexor.gradle diff --git a/README.md b/README.md index 1242ccfff..c9fc89bdf 100644 --- a/README.md +++ b/README.md @@ -248,34 +248,26 @@ which is what a board reader means. Requirements: JDK 25 (set `JAVA_HOME` to it). The Gradle wrapper handles everything else. -``` -./gradlew buildAllToOut +```sh +./gradlew buildAll ``` -builds every platform artifact into the workspace-level `../PluginOuts/` directory: the Bukkit jar -and a Fabric, Forge and NeoForge jar for every Minecraft version in `gradle/mod-targets.json` -(1.21.1, 26.1.2, 26.2 and 26.3): +This builds and verifies the Bukkit plugin, API, SPI and the Fabric, Forge and NeoForge jars +for every Minecraft version declared in `gradle/mod-targets.json`. Artifacts stay in the +producing module's `build/` directory. The packed Bukkit plugin is in `build/libs/`, the +API is in `build/api/`, the SPI is in `spi/build/libs/`, and mod jars are in +`adapters//build//libs/`. -``` -Iris v [CraftBukkit] .jar -Iris v [Fabric] +.jar -Iris v [Forge] +.jar -Iris v [NeoForge] +.jar -``` +Use `./gradlew buildBukkit` for the plugin, or `verify` for one mod jar, +for example `verifyFabric26_2` or `verifyNeoForge1_21_1`. `buildFabric`, `buildForge` +and `buildNeoforge` select the `minecraftVersion` Gradle property. `./gradlew checkAll` +runs every check plus the complete mod matrix. Forge 1.21.1 compiles its tests because +Forge 52 cannot bootstrap Minecraft outside FML. -Per-platform tasks: `./gradlew buildBukkit`, and `build` for one mod jar, for example -`buildFabric26_2` or `buildNeoForge1_21_1`. `buildFabric`, `buildForge` and `buildNeoforge` export the -26.3 jars. `./gradlew checkAll` runs every check, then builds and verifies all twelve mod jars and runs their -unit tests; Forge 1.21.1 only compiles its tests because Forge 52 cannot bootstrap Minecraft outside -FML. The -SPI jar (the pure-JVM adapter/platform contract, not the stable plugin API) is built to `spi/build/libs/` by -`./gradlew :spi:jar`. - -`./gradlew buildAll` also runs `buildAllToOut` and copies the jars into a consumer dropin tree -for a local test server. The consumer directory defaults to `../../[Minecraft Server]/consumers/` -when it exists, otherwise `build/consumers/` inside the repo. `-Plocation=/path/to/consumers` -changes only the consumer directory. Both tasks copy every verified platform jar into -`../PluginOuts/`. +Multiplexor reads `gradle/multiplexor.gradle` to discover targets and build each in a +separate source sandbox. Use its project build and deployment commands to export jars +or install them into managed test instances. Gradle builds do not copy artifacts to +external destinations. VolmLib is built from source when a `VolmLib` checkout sits beside the repo or in a parent directory (`VOLMLIB_DIR` or `-PlocalVolmLibDirectory=` pick another one). Without one, or with diff --git a/build-all.sh b/build-all.sh index c827831de..50d85f485 100755 --- a/build-all.sh +++ b/build-all.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" -./gradlew buildAllToOut --no-parallel -PuseLocalVolmLib=false "$@" +./gradlew buildAll "$@" diff --git a/build.gradle b/build.gradle index 421011946..0124517c5 100644 --- a/build.gradle +++ b/build.gradle @@ -9,9 +9,6 @@ import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.provider.Provider import org.gradle.jvm.toolchain.JavaLauncher import org.gradle.jvm.toolchain.JavaToolchainService -import org.gradle.api.file.CopySpec -import org.gradle.api.file.DeleteSpec -import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.FileTree import org.gradle.api.plugins.JavaPlugin import org.gradle.api.initialization.IncludedBuild @@ -20,8 +17,6 @@ import org.gradle.api.tasks.compile.JavaCompile import org.gradle.jvm.tasks.Jar import org.gradle.jvm.toolchain.JavaLanguageVersion -import javax.inject.Inject - /* * Iris is a World Generator for Minecraft Bukkit Servers * Copyright (c) 2021 Arcane Arts (Volmit Software) @@ -59,13 +54,6 @@ apply plugin: 'art.arcane.volmit-packaging' apply from: file('gradle/runtime-relocations.gradle') apply from: file('gradle/volmlib-stamp.gradle') -interface IrisFileOperations { - @Inject - FileSystemOperations getFileSystemOperations() -} - -IrisFileOperations irisFileOperations = objects.newInstance(IrisFileOperations) - group = 'art.arcane' version = providers.gradleProperty('irisVersion').getOrElse('4.3.5') @@ -99,41 +87,11 @@ Closure irisArtifactName = { String platform, String targetVersion -> return "Iris v${project.version} [${platform}] ${targetVersion}.jar" } String bukkitArtifactName = irisArtifactName('CraftBukkit', bukkitMinecraftRange) -String fabricArtifactName = irisArtifactName('Fabric', "${minecraftVersion}+${loaderDisplayVersion(fabricLoaderVersion)}") -String forgeArtifactName = irisArtifactName('Forge', "${minecraftVersion}+${loaderDisplayVersion(forgeVersion)}") -String neoForgeArtifactName = irisArtifactName('NeoForge', "${minecraftVersion}+${loaderDisplayVersion(neoForgeVersion)}") Map runtimeDownloadRelocations = (Map) ext.runtimeDownloadRelocations List runtimeDownloadedPrefixes = (List) ext.runtimeDownloadedPrefixes -File pluginOutDirectory = file(providers.gradleProperty('pluginOutDirectory').getOrElse('../PluginOuts')) -File legacyBukkitJar = layout.buildDirectory.file("libs/Iris-${project.version}.jar").get().asFile File bukkitArtifact = layout.buildDirectory.file("libs/${bukkitArtifactName}").get().asFile -File consumerBukkitArtifact = layout.buildDirectory.file("consumer/${bukkitArtifactName}").get().asFile -File fabricArtifact = layout.projectDirectory.file("adapters/fabric/build/${minecraftVersion}/libs/${fabricArtifactName}").asFile -File forgeArtifact = layout.projectDirectory.file("adapters/forge/build/${minecraftVersion}/libs/${forgeArtifactName}").asFile -File neoForgeArtifact = layout.projectDirectory.file("adapters/neoforge/build/${minecraftVersion}/libs/${neoForgeArtifactName}").asFile -File consumerDirectory = layout.buildDirectory.dir('consumer').get().asFile -FileTree staleConsumerBukkitJars = fileTree(consumerDirectory) { - include('Iris v* [CraftBukkit] *.jar') -} -FileTree staleBukkitOutJars = fileTree(pluginOutDirectory) { - include('Iris v* [CraftBukkit] *.jar') -} -String selectedModdedSuffix = minecraftVersion.replace('.', '_') apply plugin: ApiGenerator -// Where `buildAll` drops the per-platform jars for a local test server. Use the approved sibling -// Multiplexor workspace when it exists, with a repo-local fallback for fresh clones. -File multiplexorConsumerLocation = new File(rootDir, '../../[Minecraft Server]/consumers').canonicalFile -String defaultConsumerLocation = multiplexorConsumerLocation.isDirectory() - ? multiplexorConsumerLocation.absolutePath - : layout.buildDirectory.dir('consumers').get().asFile.absolutePath -String consumerLocation = providers.gradleProperty('location') - .getOrElse(defaultConsumerLocation) -String bukkitConsumerPath = "${consumerLocation}/plugin-consumers/dropins/plugins" -String fabricConsumerPath = "${consumerLocation}/fabric-mod-consumers/dropins/mods" -String forgeConsumerPath = "${consumerLocation}/forge-mod-consumers/dropins/mods" -String neoForgeConsumerPath = "${consumerLocation}/neoforge-mod-consumers/dropins/mods" - List nmsBindings = ['v26_2_R1', 'v26_3_R1'] ext.nativeDevelopmentAdapters = nmsBindings nmsBindings.each { String key -> @@ -240,65 +198,14 @@ tasks.named('jar', Jar).configure { exclude('com/fren_gor/**') } - doFirst { - irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(legacyBukkitJar) } - } archiveFileName.set(bukkitArtifactName) } -tasks.register('iris', Copy) { - group = 'iris' - dependsOn('packedJar') - from({ tasks.named('packedJar').get().outputJar }) - rename { String ignored -> bukkitArtifactName } - into(layout.buildDirectory.dir('consumer')) - doFirst { - irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(staleConsumerBukkitJars) } - } -} - -tasks.register('buildSwiftSwamp') { - group = 'iris' - description = 'Builds every platform jar and drops them in the local BUILDS output directory.' - outputs.upToDateWhen { false } - dependsOn('iris', "verifyFabric${selectedModdedSuffix}", "verifyForge${selectedModdedSuffix}", - "verifyNeoForge${selectedModdedSuffix}") - File swiftSwampOut = file('C://VolmitSoftware/BUILDS') - Map swiftSwampSources = [ - 'Iris.jar' : consumerBukkitArtifact, - 'Iris-Fabric.jar' : fabricArtifact, - 'Iris-Forge.jar' : forgeArtifact, - 'Iris-Neoforge.jar': neoForgeArtifact - ] - doLast { - swiftSwampSources.each { String target, File source -> - irisFileOperations.fileSystemOperations.copy { CopySpec spec -> - spec.from(source) - spec.into(swiftSwampOut) - spec.rename { String ignored -> target } - } - } - println('') - println('=== Iris buildSwiftSwamp -> ' + swiftSwampOut + ' ===') - ['Iris.jar', 'Iris-Fabric.jar', 'Iris-Forge.jar', 'Iris-Neoforge.jar'].each { String jar -> - File produced = new File(swiftSwampOut, jar) - println(String.format(' %-24s %8d KB', jar, (long) (produced.length() / 1024))) - } - } -} - -tasks.register('buildBukkit', Copy) { - group = 'iris' - dependsOn('packedJar') - from({ tasks.named('packedJar').get().outputJar }) - rename { String ignored -> bukkitArtifactName } - into(pluginOutDirectory) - File legacyOutJar = new File(pluginOutDirectory, "Iris-${project.version}+mc${minecraftVersion}.jar") - doFirst { - irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> - spec.delete(staleBukkitOutJars, legacyOutJar) - } - } +tasks.register('buildBukkit') { + group = 'build' + description = 'Builds and verifies the packed Bukkit plugin.' + dependsOn('packedJar', 'check', ':core:check', ':spi:check', ':adapters:bukkit:plugin:check') + dependsOn(nmsBindings.collect { String binding -> ":adapters:bukkit:nms:${binding}:check".toString() }) } // The three loader jars come out of nested standalone Gradle builds driven through Exec. The outer @@ -417,9 +324,7 @@ TaskProvider verifyModdedMatrix = tasks.register('verifyModdedMatrix') { group = 'verification' description = 'Builds, tests and verifies the Fabric, Forge and NeoForge jars of every supported Minecraft mod target.' } -List> moddedExports = [] Map> selectedModdedBuilds = [:] -Map> selectedModdedExports = [:] int matrixParallelism = Math.min(gradle.startParameter.maxWorkerCount, Math.max(1, providers.gradleProperty('moddedBuildParallelism').getOrElse( Math.min(12, Math.max(1, Runtime.runtime.availableProcessors() - 4)).toString()).toInteger())) @@ -428,6 +333,9 @@ Provider matrixWorkers = gradle.sharedServices.registerI maxParallelUsages.set(matrixParallelism) } List nestedBuildArguments = [gradle.startParameter.buildCacheEnabled ? '--build-cache' : '--no-build-cache'] +if (providers.gradleProperty('multiplexorSandbox').getOrElse('false').toBoolean()) { + nestedBuildArguments.add('--no-daemon') +} String nestedJvmArguments = providers.gradleProperty('org.gradle.jvmargs').getOrElse('') + ' -XX:ActiveProcessorCount=2' if (gradle.startParameter.rerunTasks) { @@ -512,26 +420,8 @@ irisModTargets.each { String targetMinecraft, Map target -> dependsOn(buildTarget) } verifyModdedMatrix.configure { dependsOn(buildTarget) } - File legacyOutJar = new File(pluginOutDirectory, "Iris-${project.version}+mc${targetMinecraft}-${loader}.jar") - FileTree staleTargetJars = fileTree(pluginOutDirectory) { - include("Iris v* [${label}] ${targetMinecraft}+*.jar") - } - TaskProvider export = tasks.register("build${label}${suffix}", Copy) { - group = 'iris' - description = "Exports the verified ${label} ${targetMinecraft} jar to the plugin output directory." - dependsOn(buildTarget) - from(artifact) - into(pluginOutDirectory) - doFirst { - irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> - spec.delete(staleTargetJars, legacyOutJar) - } - } - } - moddedExports.add(export) if (selected) { selectedModdedBuilds[loader] = buildTarget - selectedModdedExports[loader] = export } } } @@ -541,24 +431,20 @@ if (selectedModdedBuilds.isEmpty()) { tasks.register('buildFabric') { group = 'iris' - description = "Exports the verified Fabric ${minecraftVersion} jar." - dependsOn(selectedModdedExports.fabric) + description = "Builds and verifies the Fabric ${minecraftVersion} jar." + dependsOn(selectedModdedBuilds.fabric) } tasks.register('buildForge') { group = 'iris' - description = "Exports the verified Forge ${minecraftVersion} jar." - dependsOn(selectedModdedExports.forge) + description = "Builds and verifies the Forge ${minecraftVersion} jar." + dependsOn(selectedModdedBuilds.forge) } tasks.register('buildNeoforge') { group = 'iris' - description = "Exports the verified NeoForge ${minecraftVersion} jar." - dependsOn(selectedModdedExports.neoforge) -} - -tasks.named('check').configure { - dependsOn(selectedModdedBuilds.fabric) + description = "Builds and verifies the NeoForge ${minecraftVersion} jar." + dependsOn(selectedModdedBuilds.neoforge) } tasks.register('checkAll') { @@ -643,67 +529,10 @@ tasks.named('buildBukkit').configure { dependsOn('verifyBukkitArtifact') } -tasks.named('iris').configure { - dependsOn('verifyBukkitArtifact') -} - -tasks.register('buildAllToOut') { - group = 'iris' - description = 'Builds and verifies the Bukkit jar and every mod target jar into the plugin output directory.' - dependsOn('buildBukkit', ':spi:jar') - dependsOn(moddedExports) - doLast { - File[] jars = pluginOutDirectory.listFiles((java.io.FileFilter) { File f -> f.name.startsWith('Iris v') && f.name.endsWith('.jar') }) - println('') - println('=== Iris buildAllToOut -> ' + pluginOutDirectory + ' ===') - if (jars != null) { - jars.sort { it.name }.each { File f -> - println(String.format(' %-60s %8d KB', f.name, (long) (f.length() / 1024))) - } - } - } -} - tasks.register('buildAll') { - group = 'iris' - outputs.upToDateWhen { false } - dependsOn('iris', 'buildAllToOut') - Map> consumerDrops = [ - (bukkitConsumerPath) : ['Iris.jar', consumerBukkitArtifact], - (fabricConsumerPath) : ['Iris-Fabric.jar', fabricArtifact], - (forgeConsumerPath) : ['Iris-Forge.jar', forgeArtifact], - (neoForgeConsumerPath): ['Iris-Neoforge.jar', neoForgeArtifact] - ] - doLast { - consumerDrops.each { String consumerPath, List drop -> - String keep = (String) drop.get(0) - File source = (File) drop.get(1) - irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> - consumerDrops.values().each { List other -> - String name = (String) other.get(0) - if (name != keep) { - spec.delete(new File(consumerPath, name)) - } - } - } - irisFileOperations.fileSystemOperations.copy { CopySpec spec -> - spec.from(source) - spec.into(consumerPath) - spec.rename { String ignored -> keep } - } - } - println('') - println("=== Iris buildAll -> ${consumerLocation} ===") - println(" ${bukkitConsumerPath}/Iris.jar") - println(" ${fabricConsumerPath}/Iris-Fabric.jar") - println(" ${forgeConsumerPath}/Iris-Forge.jar") - println(" ${neoForgeConsumerPath}/Iris-Neoforge.jar") - } -} -tasks.register('buildPsychoLT') { - group = 'development' - description = 'Builds all verified Iris platform jars and stages them in the local test-server dropins and PluginOuts.' - dependsOn('buildAll') + group = 'build' + description = 'Builds and verifies the Bukkit plugin, API, SPI and every supported mod target.' + dependsOn('buildBukkit', 'irisApi', ':spi:jar', verifyModdedMatrix) } tasks.register('irisDev', Copy) { @@ -1074,3 +903,5 @@ tasks.withType(PackedJar).configureEach { NativeRuntimeArtifacts.verifyPacked(task.outputJar.get().asFile, nativeRuntimeModules, embeddedNatives()) } } + +apply from: file('gradle/multiplexor.gradle') diff --git a/buildSrc/src/main/java/ApiGenerator.java b/buildSrc/src/main/java/ApiGenerator.java index 2c6b48b32..f5389e70f 100644 --- a/buildSrc/src/main/java/ApiGenerator.java +++ b/buildSrc/src/main/java/ApiGenerator.java @@ -37,11 +37,6 @@ public class ApiGenerator implements Plugin { throw new GradleException("Publishing extension not found"); } - publishing.getRepositories().maven(repository -> { - repository.setName("deployDir"); - repository.setUrl(targetDirectory(target).toURI()); - }); - publishing.getPublications().create("maven", MavenPublication.class, publication -> { publication.setGroupId(target.getGroup().toString()); publication.setArtifactId("iris"); @@ -49,27 +44,19 @@ public class ApiGenerator implements Plugin { publication.artifact(task); }); } - - public static File targetDirectory(Project project) { - String dir = System.getenv("DEPLOY_DIR"); - if (dir == null) { - return project.getLayout().getBuildDirectory().dir("api").get().getAsFile(); - } - return new File(dir); - } } abstract class GenerateApiTask extends DefaultTask { public GenerateApiTask() { setGroup("iris"); dependsOn("jar"); - finalizedBy("publishMavenPublicationToDeployDirRepository"); doLast(task -> getLogger().lifecycle("The API is located at " + getOutputFile().get().getAsFile().getAbsolutePath())); TaskProvider jarTask = getProject().getTasks().named("jar", Jar.class); getInputFile().convention(jarTask.flatMap(Jar::getArchiveFile)); getOutputFile().convention(getProject().getLayout().file(getInputFile().getLocationOnly().map(input -> - new File(ApiGenerator.targetDirectory(getProject()), input.getAsFile().getName())))); + new File(getProject().getLayout().getBuildDirectory().dir("api").get().getAsFile(), + input.getAsFile().getName())))); } @InputFile diff --git a/buildSrc/src/test/java/ApiGeneratorTest.java b/buildSrc/src/test/java/ApiGeneratorTest.java index e3ab0b0b3..d441af44c 100644 --- a/buildSrc/src/test/java/ApiGeneratorTest.java +++ b/buildSrc/src/test/java/ApiGeneratorTest.java @@ -50,9 +50,12 @@ public class ApiGeneratorTest { assertEquals(jar.getArchiveFile().get().getAsFile(), api.getInputFile().get().getAsFile()); assertEquals(jar.getArchiveFileName().get(), api.getOutputFile().get().getAsFile().getName()); - assertEquals(ApiGenerator.targetDirectory(project), api.getOutputFile().get().getAsFile().getParentFile()); + assertEquals(project.getLayout().getBuildDirectory().dir("api").get().getAsFile(), + api.getOutputFile().get().getAsFile().getParentFile()); + assertTrue(api.getFinalizedBy().getDependencies(api).isEmpty()); PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); + assertTrue(publishing.getRepositories().isEmpty()); MavenPublication publication = (MavenPublication) publishing.getPublications().getByName("maven"); assertEquals("art.arcane", publication.getGroupId()); assertEquals("iris", publication.getArtifactId()); diff --git a/core/build.gradle b/core/build.gradle index abaab4f4e..d88512810 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -217,6 +217,13 @@ def generateTemplates = tasks.register('generateTemplates', Copy) { return 'development' }, commit: providers.provider { + String sourceHash = providers.environmentVariable('MULTIPLEXOR_SOURCE_HASH').orNull + if (sourceHash != null) { + if (!(sourceHash ==~ /[a-f0-9]{64}/)) { + throw new GradleException('MULTIPLEXOR_SOURCE_HASH must be a SHA-256 source fingerprint') + } + return "source-${sourceHash}" + } String commitId = null Exception failure = null try { diff --git a/gradle/multiplexor.gradle b/gradle/multiplexor.gradle new file mode 100644 index 000000000..e3a46eb5c --- /dev/null +++ b/gradle/multiplexor.gradle @@ -0,0 +1,73 @@ +import org.gradle.jvm.tasks.Jar + +Closure compareMinecraft = { String left, String right -> + List leftParts = left.tokenize('.').collect { String part -> Integer.parseInt(part) } + List rightParts = right.tokenize('.').collect { String part -> Integer.parseInt(part) } + for (int index = 0; index < Math.max(leftParts.size(), rightParts.size()); index++) { + int comparison = Integer.compare(index < leftParts.size() ? leftParts[index] : 0, + index < rightParts.size() ? rightParts[index] : 0) + if (comparison != 0) { + return comparison + } + } + return 0 +} +List bukkitRange = providers.gradleProperty('bukkitMinecraftRange').getOrElse('26.1.2-26.3').tokenize('-') +Map> modTargets = (Map>) project.ext.irisModTargets +List bukkitVersions = modTargets.keySet().findAll { String version -> + compareMinecraft(version, bukkitRange.first()) >= 0 && compareMinecraft(version, bukkitRange.last()) <= 0 +}.toList() +Map loaderLabels = [fabric: 'Fabric', forge: 'Forge', neoforge: 'NeoForge'] +Map loaderPins = [fabric: 'fabricLoader', forge: 'forge', neoforge: 'neoForge'] +Map loaderProperties = [fabric: 'fabricLoaderVersion', forge: 'forgeVersion', neoforge: 'neoForgeVersion'] +String selectedVersion = providers.gradleProperty('minecraftVersion').getOrElse('26.3') +List> buildTargets = [] + +project.ext.multiplexor = [ + schema: 1, + id: 'iris', + name: 'Iris', + sandbox: 'per-target', + parallelTargets: true, + sourceDependencies: ['volmlib'], + targets: buildTargets +] + +gradle.projectsEvaluated { + File bukkitSource = tasks.named('jar', Jar).get().archiveFile.get().asFile + File bukkitArtifact = new File(bukkitSource.parentFile, bukkitSource.name.replaceFirst(/\.jar$/, '-packed.jar')) + File apiArtifact = tasks.named('irisApi', GenerateApiTask).get().outputFile.locationOnly.get().asFile + File spiArtifact = project(':spi').tasks.named('jar', Jar).get().archiveFile.locationOnly.get().asFile + buildTargets.addAll([ + [id: 'bukkit', tasks: [':buildBukkit'], arguments: [], java: 25, + memoryMiB: 6144, workers: 2, dependsOn: [], artifacts: [ + [path: relativePath(bukkitArtifact), role: 'runtime', platforms: ['plugin'], minecraftVersions: bukkitVersions]]], + [id: 'api', tasks: [':irisApi'], arguments: [], java: 25, + memoryMiB: 6144, workers: 2, dependsOn: [], artifacts: [ + [path: relativePath(apiArtifact), role: 'api', platforms: ['plugin'], minecraftVersions: bukkitVersions]]], + [id: 'spi', tasks: [':spi:build'], arguments: [], java: 25, + memoryMiB: 4096, workers: 2, dependsOn: [], artifacts: [ + [path: relativePath(spiArtifact), role: 'library', platforms: [], minecraftVersions: []]]] + ]) + modTargets.each { String minecraft, Map target -> + loaderLabels.each { String loader, String label -> + String pin = minecraft == selectedVersion + ? providers.gradleProperty(loaderProperties[loader]).getOrElse(target[loaderPins[loader]].toString()) + : target[loaderPins[loader]].toString() + String displayedPin = pin.startsWith(minecraft + '-') ? pin.substring(minecraft.length() + 1) : pin + String artifactName = "Iris v${project.version} [${label}] ${minecraft}+${displayedPin}.jar" + buildTargets.add([ + id: "${loader}-${minecraft}".toString(), + tasks: [":verify${label}${minecraft.replace('.', '_')}".toString()], + arguments: ['-PmoddedBuildParallelism=1', '-PmoddedTestForks=1', + '-PmultiplexorSandbox=true'], + java: 25, + memoryMiB: 8192, + workers: 2, + dependsOn: [], + artifacts: [[path: "adapters/${loader}/build/${minecraft}/libs/${artifactName}".toString(), + role: 'runtime', platforms: [loader], minecraftVersions: [minecraft]]] + ]) + } + } +}