VolmLib pin removed

- Local builds use the sibling VolmLib checkout for every VolmLib module with no pinned revision
- Builds without a local VolmLib use VolmLib master-SNAPSHOT like the other plugins
- The native runtime manifest and keep rules come from the embedded natives
- Iris loads only embedded natives and never downloads them
- Every jar records the VolmLib commit it was built from
This commit is contained in:
Brian Neumann-Fopiano
2026-09-29 09:58:09 -04:00
23 changed files with 448 additions and 569 deletions
+6
View File
@@ -271,6 +271,12 @@ when it exists, otherwise `build/consumers/` inside the repo. `-Plocation=/path/
changes only the consumer directory. Both tasks copy all four verified platform jars into changes only the consumer directory. Both tasks copy all four verified platform jars into
`../PluginOuts/`. `../PluginOuts/`.
VolmLib is built from source when a `VolmLib` checkout sits beside the repo or in a parent directory
(`VOLMLIB_DIR` or `-PlocalVolmLibDirectory=<path>` pick another one). Without one, or with
`-PuseLocalVolmLib=false`, the build uses VolmLib `master-SNAPSHOT` from JitPack. There is no VolmLib
version to bump. The build logs which VolmLib it used, and every jar records it in
`META-INF/volmit/volmlib.properties`.
If you need help compiling as a developer or contributor, ask in the Discord. If you need help compiling as a developer or contributor, ask in the Discord.
## Adapters / modded development ## Adapters / modded development
+2 -6
View File
@@ -2,15 +2,11 @@ String apiVersion = providers.gradleProperty('apiVersion').get()
def mainClass = 'art.arcane.iris.Iris' def mainClass = 'art.arcane.iris.Iris'
def bootstrapperClass = 'art.arcane.iris.IrisBootstrap' def bootstrapperClass = 'art.arcane.iris.IrisBootstrap'
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
.orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')
.get()
dependencies { dependencies {
compileOnly(project(':core')) compileOnly(project(':core'))
compileOnly(libs.lombok) compileOnly(libs.lombok)
annotationProcessor(libs.lombok) annotationProcessor(libs.lombok)
compileOnly(volmLibCoordinate) { compileOnly('com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
compileOnly(libs.paper.api) compileOnly(libs.paper.api)
@@ -23,7 +19,7 @@ dependencies {
testImplementation(libs.bstats) testImplementation(libs.bstats)
testImplementation(project(':core')) testImplementation(project(':core'))
testImplementation(testFixtures(project(':core'))) testImplementation(testFixtures(project(':core')))
testImplementation(volmLibCoordinate) { testImplementation('com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
compileOnly(libs.placeholderApi) compileOnly(libs.placeholderApi)
+7 -8
View File
@@ -32,11 +32,7 @@ buildscript {
maven { url = uri('https://jitpack.io') } maven { url = uri('https://jitpack.io') }
} }
dependencies { dependencies {
Properties libraryProperties = new Properties() classpath('com.github.VolmitSoftware.VolmLib:packaging:master-SNAPSHOT')
file('../../gradle.properties').withInputStream { InputStream stream -> libraryProperties.load(stream) }
String libraryCoordinate = providers.gradleProperty('volmLibCoordinate')
.getOrElse(libraryProperties.getProperty('volmLibCoordinate'))
classpath("com.github.VolmitSoftware.VolmLib:packaging:${libraryCoordinate.tokenize(':').last()}")
} }
} }
@@ -56,13 +52,13 @@ interface IrisFileOperations {
IrisFileOperations irisFileOperations = objects.newInstance(IrisFileOperations) IrisFileOperations irisFileOperations = objects.newInstance(IrisFileOperations)
apply from: file('../../gradle/modded-generation-build-revision.gradle') apply from: file('../../gradle/modded-generation-build-revision.gradle')
apply from: file('../../gradle/volmlib-stamp.gradle')
Properties rootProperties = new Properties() Properties rootProperties = new Properties()
file('../../gradle.properties').withInputStream { InputStream stream -> rootProperties.load(stream) } file('../../gradle.properties').withInputStream { InputStream stream -> rootProperties.load(stream) }
String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.1.2-26.2')) String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.1.2-26.2'))
String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.3')) String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.3'))
String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse(rootProperties.getProperty('fabricLoaderVersion', '0.19.5')) String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse(rootProperties.getProperty('fabricLoaderVersion', '0.19.5'))
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate').getOrElse(rootProperties.getProperty('volmLibCoordinate', 'com.github.VolmitSoftware:VolmLib:master-SNAPSHOT'))
Closure<String> irisArtifactName = { String platform, String targetVersion -> Closure<String> irisArtifactName = { String platform, String targetVersion ->
return "Iris v${project.version} [${platform}] ${targetVersion}.jar" return "Iris v${project.version} [${platform}] ${targetVersion}.jar"
} }
@@ -180,10 +176,10 @@ dependencies {
compileOnly(libs.spigot) { compileOnly(libs.spigot) {
transitive = false transitive = false
} }
add('bundle', volmLibCoordinate) { add('bundle', 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
add('devBundle', volmLibCoordinate) { add('devBundle', 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
add('bundle', libs.zip) { add('bundle', libs.zip) {
@@ -286,6 +282,9 @@ tasks.named('shadowJar', ShadowJar).configure {
spec.delete(staleLoaderJars, legacyLoaderJar) spec.delete(staleLoaderJars, legacyLoaderJar)
} }
} }
from(tasks.named('generateVolmLibStamp')) {
into('META-INF/volmit')
}
archiveFileName.set(irisArtifactName('Fabric', "${minecraftVersion}+${fabricLoaderVersion}")) archiveFileName.set(irisArtifactName('Fabric', "${minecraftVersion}+${fabricLoaderVersion}"))
configurations = [project.configurations.named('bundle').get()] configurations = [project.configurations.named('bundle').get()]
mergeServiceFiles() mergeServiceFiles()
+8 -9
View File
@@ -32,11 +32,7 @@ buildscript {
maven { url = uri('https://jitpack.io') } maven { url = uri('https://jitpack.io') }
} }
dependencies { dependencies {
Properties libraryProperties = new Properties() classpath('com.github.VolmitSoftware.VolmLib:packaging:master-SNAPSHOT')
file('../../gradle.properties').withInputStream { InputStream stream -> libraryProperties.load(stream) }
String libraryCoordinate = providers.gradleProperty('volmLibCoordinate')
.getOrElse(libraryProperties.getProperty('volmLibCoordinate'))
classpath("com.github.VolmitSoftware.VolmLib:packaging:${libraryCoordinate.tokenize(':').last()}")
} }
} }
@@ -56,13 +52,13 @@ interface IrisFileOperations {
IrisFileOperations irisFileOperations = objects.newInstance(IrisFileOperations) IrisFileOperations irisFileOperations = objects.newInstance(IrisFileOperations)
apply from: file('../../gradle/modded-generation-build-revision.gradle') apply from: file('../../gradle/modded-generation-build-revision.gradle')
apply from: file('../../gradle/volmlib-stamp.gradle')
Properties rootProperties = new Properties() Properties rootProperties = new Properties()
file('../../gradle.properties').withInputStream { InputStream stream -> rootProperties.load(stream) } file('../../gradle.properties').withInputStream { InputStream stream -> rootProperties.load(stream) }
String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.1.2-26.2')) String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.1.2-26.2'))
String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.3')) String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.3'))
String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse(rootProperties.getProperty('forgeVersion', '26.3-66.0.8')) String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse(rootProperties.getProperty('forgeVersion', '26.3-66.0.8'))
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate').getOrElse(rootProperties.getProperty('volmLibCoordinate', 'com.github.VolmitSoftware:VolmLib:master-SNAPSHOT'))
Closure<String> loaderDisplayVersion = { String loaderVersion -> Closure<String> loaderDisplayVersion = { String loaderVersion ->
String coordinatePrefix = "${minecraftVersion}-" String coordinatePrefix = "${minecraftVersion}-"
if (loaderVersion.startsWith(coordinatePrefix)) { if (loaderVersion.startsWith(coordinatePrefix)) {
@@ -173,13 +169,13 @@ dependencies {
compileOnly(libs.spigot) { compileOnly(libs.spigot) {
transitive = false transitive = false
} }
add('bundle', volmLibCoordinate) { add('bundle', 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
add('devBundle', volmLibCoordinate) { add('devBundle', 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
add('runBundle', volmLibCoordinate) { add('runBundle', 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
add('bundle', libs.zip) { add('bundle', libs.zip) {
@@ -271,6 +267,9 @@ tasks.named('shadowJar', ShadowJar).configure {
spec.delete(staleLoaderJars, legacyLoaderJar) spec.delete(staleLoaderJars, legacyLoaderJar)
} }
} }
from(tasks.named('generateVolmLibStamp')) {
into('META-INF/volmit')
}
manifest { manifest {
// FML 26.3 has no TOML mixin handling at all (ModFileParser reads neither [[mixins]] nor // FML 26.3 has no TOML mixin handling at all (ModFileParser reads neither [[mixins]] nor
// accessTransformers on Forge). This manifest attribute is the only registration mechanism; // accessTransformers on Forge). This manifest attribute is the only registration mechanism;
+7 -8
View File
@@ -32,11 +32,7 @@ buildscript {
maven { url = uri('https://jitpack.io') } maven { url = uri('https://jitpack.io') }
} }
dependencies { dependencies {
Properties libraryProperties = new Properties() classpath('com.github.VolmitSoftware.VolmLib:packaging:master-SNAPSHOT')
file('../../gradle.properties').withInputStream { InputStream stream -> libraryProperties.load(stream) }
String libraryCoordinate = providers.gradleProperty('volmLibCoordinate')
.getOrElse(libraryProperties.getProperty('volmLibCoordinate'))
classpath("com.github.VolmitSoftware.VolmLib:packaging:${libraryCoordinate.tokenize(':').last()}")
} }
} }
@@ -56,13 +52,13 @@ interface IrisFileOperations {
IrisFileOperations irisFileOperations = objects.newInstance(IrisFileOperations) IrisFileOperations irisFileOperations = objects.newInstance(IrisFileOperations)
apply from: file('../../gradle/modded-generation-build-revision.gradle') apply from: file('../../gradle/modded-generation-build-revision.gradle')
apply from: file('../../gradle/volmlib-stamp.gradle')
Properties rootProperties = new Properties() Properties rootProperties = new Properties()
file('../../gradle.properties').withInputStream { InputStream stream -> rootProperties.load(stream) } file('../../gradle.properties').withInputStream { InputStream stream -> rootProperties.load(stream) }
String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.1.2-26.2')) String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.1.2-26.2'))
String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.3')) String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.3'))
String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse(rootProperties.getProperty('neoForgeVersion', '26.3.0.33-beta')) String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse(rootProperties.getProperty('neoForgeVersion', '26.3.0.33-beta'))
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate').getOrElse(rootProperties.getProperty('volmLibCoordinate', 'com.github.VolmitSoftware:VolmLib:master-SNAPSHOT'))
Closure<String> irisArtifactName = { String platform, String targetVersion -> Closure<String> irisArtifactName = { String platform, String targetVersion ->
return "Iris v${project.version} [${platform}] ${targetVersion}.jar" return "Iris v${project.version} [${platform}] ${targetVersion}.jar"
} }
@@ -152,10 +148,10 @@ dependencies {
compileOnly(libs.spigot) { compileOnly(libs.spigot) {
transitive = false transitive = false
} }
add('bundle', volmLibCoordinate) { add('bundle', 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
add('devBundle', volmLibCoordinate) { add('devBundle', 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
add('bundle', libs.zip) { add('bundle', libs.zip) {
@@ -274,6 +270,9 @@ tasks.named('shadowJar', ShadowJar).configure {
spec.delete(staleLoaderJars, legacyLoaderJar) spec.delete(staleLoaderJars, legacyLoaderJar)
} }
} }
from(tasks.named('generateVolmLibStamp')) {
into('META-INF/volmit')
}
archiveFileName.set(irisArtifactName('NeoForge', "${minecraftVersion}+${neoForgeVersion}")) archiveFileName.set(irisArtifactName('NeoForge', "${minecraftVersion}+${neoForgeVersion}"))
configurations = [project.configurations.named('bundle').get()] configurations = [project.configurations.named('bundle').get()]
from(sourceSets.main.output) from(sourceSets.main.output)
+56 -105
View File
@@ -43,8 +43,7 @@ buildscript {
maven { url = uri('https://jitpack.io') } maven { url = uri('https://jitpack.io') }
} }
dependencies { dependencies {
String volmLibRevision = providers.gradleProperty('volmLibCoordinate').get().tokenize(':').last() classpath('com.github.VolmitSoftware.VolmLib:packaging:master-SNAPSHOT')
classpath("com.github.VolmitSoftware.VolmLib:packaging:${volmLibRevision}")
} }
} }
@@ -55,6 +54,7 @@ plugins {
apply plugin: 'art.arcane.volmit-packaging' apply plugin: 'art.arcane.volmit-packaging'
apply from: file('gradle/runtime-relocations.gradle') apply from: file('gradle/runtime-relocations.gradle')
apply from: file('gradle/volmlib-stamp.gradle')
interface IrisFileOperations { interface IrisFileOperations {
@Inject @Inject
@@ -76,15 +76,9 @@ String bukkitMinecraftRange = providers.gradleProperty('bukkitMinecraftRange').g
String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse('0.19.5') String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse('0.19.5')
String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse('26.3-66.0.8') String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse('26.3-66.0.8')
String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse('26.3.0.33-beta') String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse('26.3.0.33-beta')
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate') String volmLibShared = 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT'
.orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT') File localVolmLibCheckout = (File) gradle.extensions.extraProperties.get('volmLibDirectory')
.get() Map<String, String> volmLibStamp = (Map<String, String>) gradle.extensions.extraProperties.get('volmLibStamp')
String useLocalVolmLib = providers.gradleProperty('useLocalVolmLib').getOrElse('true')
String localVolmLibDirectory = providers.gradleProperty('localVolmLibDirectory').getOrNull()
boolean useMavenLocal = providers.gradleProperty('useMavenLocal')
.orElse(useLocalVolmLib.equalsIgnoreCase('true') ? 'false' : 'true')
.map { String value -> value.equalsIgnoreCase('true') }
.get()
Closure<String> loaderDisplayVersion = { String loaderVersion -> Closure<String> loaderDisplayVersion = { String loaderVersion ->
String coordinatePrefix = "${minecraftVersion}-" String coordinatePrefix = "${minecraftVersion}-"
if (loaderVersion.startsWith(coordinatePrefix)) { if (loaderVersion.startsWith(coordinatePrefix)) {
@@ -153,11 +147,11 @@ nmsBindings.each { String key ->
} }
dependencies { dependencies {
implementation("com.github.VolmitSoftware.VolmLib:native-${key}:${volmLibCoordinate.tokenize(':').last()}") { implementation("com.github.VolmitSoftware.VolmLib:native-${key}:master-SNAPSHOT") {
transitive = false transitive = false
} }
compileOnly(project(':core')) compileOnly(project(':core'))
compileOnly(volmLibCoordinate) { compileOnly(volmLibShared) {
transitive = false transitive = false
} }
compileOnly(rootProject.libs.annotations) compileOnly(rootProject.libs.annotations)
@@ -169,10 +163,10 @@ nmsBindings.each { String key ->
} }
testImplementation(project(':core')) testImplementation(project(':core'))
testImplementation(testFixtures(project(':core'))) testImplementation(testFixtures(project(':core')))
testImplementation("com.github.VolmitSoftware.VolmLib:native-common:${volmLibCoordinate.tokenize(':').last()}") { testImplementation('com.github.VolmitSoftware.VolmLib:native-common:master-SNAPSHOT') {
transitive = false transitive = false
} }
testCompileOnly(volmLibCoordinate) { testCompileOnly(volmLibShared) {
transitive = false transitive = false
} }
testRuntimeOnly(project(':core')) testRuntimeOnly(project(':core'))
@@ -183,49 +177,32 @@ nmsBindings.each { String key ->
def included = configurations.create('included') def included = configurations.create('included')
def nativeRuntime = configurations.create('nativeRuntime') def nativeRuntime = configurations.create('nativeRuntime')
def publishedNativeRuntime = configurations.create('publishedNativeRuntime') {
canBeConsumed = false
transitive = false
resolutionStrategy.useGlobalDependencySubstitutionRules = false
}
repositories.configureEach { repository ->
if (repository.name != 'publishedNativeProviders') {
repository.content { notForConfigurations('publishedNativeRuntime') }
}
}
repositories.maven {
name = 'publishedNativeProviders'
url = uri('https://jitpack.io/')
content {
onlyForConfigurations('publishedNativeRuntime')
includeGroup('com.github.VolmitSoftware.VolmLib')
}
}
def nativeRuntimeOutput = layout.buildDirectory.dir('generated/native-runtime') def nativeRuntimeOutput = layout.buildDirectory.dir('generated/native-runtime')
def nativeRuntimeRules = layout.buildDirectory.file('generated/native-runtime-keep.pro') def nativeRuntimeRules = layout.buildDirectory.file('generated/native-runtime-keep.pro')
List<String> nativeRuntimeModules = ['native-common'] + nmsBindings.collect { String adapter -> 'native-' + adapter } List<String> nativeRuntimeModules = ['native-common'] + nmsBindings.collect { String adapter -> 'native-' + adapter }
dependencies { dependencies {
nativeRuntimeModules.each { String module -> nativeRuntimeModules.each { String module ->
add('nativeRuntime', "com.github.VolmitSoftware.VolmLib:${module}:${volmLibCoordinate.tokenize(':').last()}") { add('nativeRuntime', "com.github.VolmitSoftware.VolmLib:${module}:master-SNAPSHOT") {
transitive = false transitive = false
} }
add('publishedNativeRuntime', "com.github.VolmitSoftware.VolmLib:${module}:${volmLibCoordinate.tokenize(':').last()}")
} }
} }
// The natives PackedJar embeds are the single source for the runtime manifest, the reachability roots and
// the ProGuard keep rules, so the shrinker can never prune against a different provider build.
Closure<Map<String, File>> embeddedNatives = { ->
Map<ComponentIdentifier, File> resolved = new LinkedHashMap<>()
nativeRuntime.incoming.artifacts.artifacts.each { artifact ->
resolved.put(artifact.id.componentIdentifier, artifact.file)
}
return PackedJar.resolveNativeArtifacts(resolved)
}
def generateNativeRuntime = tasks.register('generateNativeRuntime') { def generateNativeRuntime = tasks.register('generateNativeRuntime') {
inputs.files(publishedNativeRuntime) inputs.files(nativeRuntime)
inputs.property('version', volmLibCoordinate.tokenize(':').last()) inputs.property('modules', nativeRuntimeModules)
inputs.property('adapters', nmsBindings)
outputs.dir(nativeRuntimeOutput) outputs.dir(nativeRuntimeOutput)
outputs.file(nativeRuntimeRules) outputs.file(nativeRuntimeRules)
doLast { doLast {
Map<ComponentIdentifier, File> resolved = new LinkedHashMap<>() NativeRuntimeArtifacts.generate(embeddedNatives(), nativeRuntimeModules,
publishedNativeRuntime.incoming.artifacts.artifacts.each { artifact ->
resolved.put(artifact.id.componentIdentifier, artifact.file)
}
Map<String, File> artifacts = NativeRuntimeArtifacts.publishedArtifacts(resolved,
nativeRuntimeModules, volmLibCoordinate.tokenize(':').last())
NativeRuntimeArtifacts.generate(artifacts, volmLibCoordinate.tokenize(':').last(),
nativeRuntimeOutput.get().asFile, nativeRuntimeRules.get().asFile) nativeRuntimeOutput.get().asFile, nativeRuntimeRules.get().asFile)
} }
} }
@@ -240,6 +217,9 @@ tasks.named('jar', Jar).configure {
inputs.files(included) inputs.files(included)
dependsOn(generateNativeRuntime) dependsOn(generateNativeRuntime)
from(nativeRuntimeOutput) from(nativeRuntimeOutput)
from(tasks.named('generateVolmLibStamp')) {
into('META-INF/volmit')
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE duplicatesStrategy = DuplicatesStrategy.EXCLUDE
includeEmptyDirs = false includeEmptyDirs = false
from(jarJar) from(jarJar)
@@ -312,31 +292,6 @@ tasks.register('buildBukkit', Copy) {
// task therefore has to carry the full inventory of what those builds read: every shared source tree, // task therefore has to carry the full inventory of what those builds read: every shared source tree,
// every build script and version file they load, and the local VolmLib checkout when the composite // every build script and version file they load, and the local VolmLib checkout when the composite
// resolves one. Anything missing from that list publishes a stale jar. // resolves one. Anything missing from that list publishes a stale jar.
Closure<File> resolveLocalVolmLibDirectory = { ->
String configured = localVolmLibDirectory != null && !localVolmLibDirectory.isBlank()
? localVolmLibDirectory
: System.getenv('VOLMLIB_DIR')
if (configured != null && !configured.isBlank()) {
File configuredDirectory = new File(configured)
if (!configuredDirectory.isAbsolute()) {
configuredDirectory = new File(rootDir, configured)
}
return configuredDirectory.isDirectory() ? configuredDirectory : null
}
File current = rootDir
while (current != null) {
File candidate = new File(current, 'VolmLib')
if (new File(candidate, 'settings.gradle').isFile() || new File(candidate, 'settings.gradle.kts').isFile()) {
return candidate
}
current = current.parentFile
}
return null
}
List<Object> moddedSharedInputs = [] List<Object> moddedSharedInputs = []
[ [
'adapters/modded-common/src', 'adapters/modded-common/src',
@@ -353,11 +308,11 @@ List<Object> moddedSharedInputs = []
'lombok.config', 'lombok.config',
'gradle/libs.versions.toml', 'gradle/libs.versions.toml',
'gradle/volmlib-resolution.settings.gradle', 'gradle/volmlib-resolution.settings.gradle',
'gradle/volmlib-stamp.gradle',
'gradle/generation-build-revision.gradle', 'gradle/generation-build-revision.gradle',
'gradle/modded-generation-build-revision.gradle', 'gradle/modded-generation-build-revision.gradle',
'gradle/native-minecraft-sources.gradle' 'gradle/native-minecraft-sources.gradle'
].each { String path -> moddedSharedInputs.add(layout.projectDirectory.file(path).asFile) } ].each { String path -> moddedSharedInputs.add(layout.projectDirectory.file(path).asFile) }
File localVolmLibCheckout = useLocalVolmLib.equalsIgnoreCase('true') ? resolveLocalVolmLibDirectory() : null
if (localVolmLibCheckout != null) { if (localVolmLibCheckout != null) {
moddedSharedInputs.add(fileTree(localVolmLibCheckout) { moddedSharedInputs.add(fileTree(localVolmLibCheckout) {
include('shared/src/main/**', 'native-api/src/main/**', 'native-terrain-api/src/main/**', include('shared/src/main/**', 'native-api/src/main/**', 'native-terrain-api/src/main/**',
@@ -368,6 +323,16 @@ if (localVolmLibCheckout != null) {
}) })
} }
// The published master-SNAPSHOT is resolved inside the nested build, so no outer input changes when it
// moves. Without a local checkout the Exec tasks always run and the nested build decides what is stale.
Closure<Void> declareVolmLibInputs = { Exec task ->
task.inputs.property('volmLibStamp', volmLibStamp)
if (localVolmLibCheckout == null) {
task.outputs.upToDateWhen { false }
}
return null
}
Closure<Void> declareModdedJarIo = { Exec task, String adapter, String artifactName, Map<String, String> versions -> Closure<Void> declareModdedJarIo = { Exec task, String adapter, String artifactName, Map<String, String> versions ->
task.inputs.files(moddedSharedInputs).withPropertyName('moddedSharedSources') task.inputs.files(moddedSharedInputs).withPropertyName('moddedSharedSources')
.withPathSensitivity(PathSensitivity.RELATIVE) .withPathSensitivity(PathSensitivity.RELATIVE)
@@ -377,8 +342,7 @@ Closure<Void> declareModdedJarIo = { Exec task, String adapter, String artifactN
layout.projectDirectory.file("adapters/${adapter}/settings.gradle").asFile layout.projectDirectory.file("adapters/${adapter}/settings.gradle").asFile
).withPropertyName('adapterSources') ).withPropertyName('adapterSources')
versions.each { String key, String value -> task.inputs.property(key, value) } versions.each { String key, String value -> task.inputs.property(key, value) }
task.inputs.property('useLocalVolmLib', useLocalVolmLib) declareVolmLibInputs(task)
task.inputs.property('volmLibCoordinate', volmLibCoordinate)
task.outputs.file(layout.projectDirectory.file("adapters/${adapter}/build/libs/${artifactName}")) task.outputs.file(layout.projectDirectory.file("adapters/${adapter}/build/libs/${artifactName}"))
return null return null
} }
@@ -394,8 +358,7 @@ tasks.register('fabricJar', Exec) {
"-PirisVersion=${project.version}", "-PirisVersion=${project.version}",
"-PminecraftVersion=${minecraftVersion}", "-PminecraftVersion=${minecraftVersion}",
"-PfabricLoaderVersion=${fabricLoaderVersion}", "-PfabricLoaderVersion=${fabricLoaderVersion}",
"-PuseLocalVolmLib=${useLocalVolmLib}", "-PuseLocalVolmLib=${localVolmLibCheckout != null}"
"-PvolmLibCoordinate=${volmLibCoordinate}"
] ]
if (localVolmLibCheckout != null) { if (localVolmLibCheckout != null) {
environment('ORG_GRADLE_PROJECT_localVolmLibDirectory', localVolmLibCheckout.absolutePath) environment('ORG_GRADLE_PROJECT_localVolmLibDirectory', localVolmLibCheckout.absolutePath)
@@ -432,8 +395,7 @@ tasks.register('forgeJar', Exec) {
"-PirisVersion=${project.version}", "-PirisVersion=${project.version}",
"-PminecraftVersion=${minecraftVersion}", "-PminecraftVersion=${minecraftVersion}",
"-PforgeVersion=${forgeVersion}", "-PforgeVersion=${forgeVersion}",
"-PuseLocalVolmLib=${useLocalVolmLib}", "-PuseLocalVolmLib=${localVolmLibCheckout != null}"
"-PvolmLibCoordinate=${volmLibCoordinate}"
] ]
if (localVolmLibCheckout != null) { if (localVolmLibCheckout != null) {
environment('ORG_GRADLE_PROJECT_localVolmLibDirectory', localVolmLibCheckout.absolutePath) environment('ORG_GRADLE_PROJECT_localVolmLibDirectory', localVolmLibCheckout.absolutePath)
@@ -470,8 +432,7 @@ tasks.register('neoforgeJar', Exec) {
"-PirisVersion=${project.version}", "-PirisVersion=${project.version}",
"-PminecraftVersion=${minecraftVersion}", "-PminecraftVersion=${minecraftVersion}",
"-PneoForgeVersion=${neoForgeVersion}", "-PneoForgeVersion=${neoForgeVersion}",
"-PuseLocalVolmLib=${useLocalVolmLib}", "-PuseLocalVolmLib=${localVolmLibCheckout != null}"
"-PvolmLibCoordinate=${volmLibCoordinate}"
] ]
if (localVolmLibCheckout != null) { if (localVolmLibCheckout != null) {
environment('ORG_GRADLE_PROJECT_localVolmLibDirectory', localVolmLibCheckout.absolutePath) environment('ORG_GRADLE_PROJECT_localVolmLibDirectory', localVolmLibCheckout.absolutePath)
@@ -528,8 +489,7 @@ tasks.register('moddedTest', Exec) {
"-PirisVersion=${project.version}", "-PirisVersion=${project.version}",
"-PminecraftVersion=${minecraftVersion}", "-PminecraftVersion=${minecraftVersion}",
"-PfabricLoaderVersion=${fabricLoaderVersion}", "-PfabricLoaderVersion=${fabricLoaderVersion}",
"-PuseLocalVolmLib=${useLocalVolmLib}", "-PuseLocalVolmLib=${localVolmLibCheckout != null}"
"-PvolmLibCoordinate=${volmLibCoordinate}"
] ]
if (localVolmLibCheckout != null) { if (localVolmLibCheckout != null) {
environment('ORG_GRADLE_PROJECT_localVolmLibDirectory', localVolmLibCheckout.absolutePath) environment('ORG_GRADLE_PROJECT_localVolmLibDirectory', localVolmLibCheckout.absolutePath)
@@ -545,8 +505,7 @@ tasks.register('moddedTest', Exec) {
inputs.property('irisVersion', project.version.toString()) inputs.property('irisVersion', project.version.toString())
inputs.property('minecraftVersion', minecraftVersion) inputs.property('minecraftVersion', minecraftVersion)
inputs.property('fabricLoaderVersion', fabricLoaderVersion) inputs.property('fabricLoaderVersion', fabricLoaderVersion)
inputs.property('useLocalVolmLib', useLocalVolmLib) declareVolmLibInputs(it)
inputs.property('volmLibCoordinate', volmLibCoordinate)
outputs.dir(layout.projectDirectory.dir('adapters/fabric/build/test-results/test')) outputs.dir(layout.projectDirectory.dir('adapters/fabric/build/test-results/test'))
mustRunAfter(tasks.named('jar')) mustRunAfter(tasks.named('jar'))
mustRunAfter(project(':core').tasks.named('check')) mustRunAfter(project(':core').tasks.named('check'))
@@ -585,6 +544,7 @@ List<String> commonModdedArtifactEntries = [
'native/windows-x86_64/jlibdeflate.dll', 'native/windows-x86_64/jlibdeflate.dll',
'META-INF/iris/generation-kernels/catalog.tsv', 'META-INF/iris/generation-kernels/catalog.tsv',
'META-INF/iris/generation-kernels/abi-1.revision', 'META-INF/iris/generation-kernels/abi-1.revision',
'META-INF/volmit/volmlib.properties',
'META-INF/services/art.arcane.iris.localization.IrisMessageContributor', 'META-INF/services/art.arcane.iris.localization.IrisMessageContributor',
'art/arcane/iris/generation/runtime/IrisEngine.class', 'art/arcane/iris/generation/runtime/IrisEngine.class',
'art/arcane/iris/pack/datapack/DataVersion.class', 'art/arcane/iris/pack/datapack/DataVersion.class',
@@ -700,6 +660,7 @@ List<String> requiredBukkitArtifactEntries = [
'slimjar.dat', 'slimjar.dat',
'slimjar-resolutions.dat', 'slimjar-resolutions.dat',
'META-INF/iris/native-runtime.properties', 'META-INF/iris/native-runtime.properties',
'META-INF/volmit/volmlib.properties',
'iris-language-source.properties', 'iris-language-source.properties',
'agent.jar', 'agent.jar',
'art/arcane/iris/Iris.class', 'art/arcane/iris/Iris.class',
@@ -725,6 +686,7 @@ tasks.register('verifyBukkitArtifact') {
dependsOn('jar') dependsOn('jar')
File artifact = bukkitArtifact File artifact = bukkitArtifact
inputs.file(artifact) inputs.file(artifact)
inputs.files(nativeRuntime)
inputs.files(commandHandlerSources) inputs.files(commandHandlerSources)
inputs.files(mantleComponentSources) inputs.files(mantleComponentSources)
doLast { doLast {
@@ -733,7 +695,7 @@ tasks.register('verifyBukkitArtifact') {
requiredBukkitArtifactEntries, requiredBukkitArtifactEntries,
16, 16,
runtimeDownloadedPrefixes) runtimeDownloadedPrefixes)
NativeRuntimeArtifacts.verify(artifact, nmsBindings, volmLibCoordinate.tokenize(':').last()) NativeRuntimeArtifacts.verify(artifact, nativeRuntimeModules, embeddedNatives())
logger.lifecycle("Verified ${artifact.name} packaging and class reference graph") logger.lifecycle("Verified ${artifact.name} packaging and class reference graph")
} }
} }
@@ -819,11 +781,6 @@ tasks.register('irisDev', Copy) {
dependsOn(':core:javadocJar') dependsOn(':core:javadocJar')
} }
configurations.configureEach {
resolutionStrategy.cacheChangingModulesFor(0, 'seconds')
resolutionStrategy.cacheDynamicVersionsFor(0, 'seconds')
}
allprojects { allprojects {
apply plugin: 'java' apply plugin: 'java'
@@ -834,9 +791,6 @@ allprojects {
} }
repositories { repositories {
if (useMavenLocal) {
mavenLocal()
}
mavenCentral() mavenCentral()
maven { url = uri('https://repo.papermc.io/repository/maven-public/') } maven { url = uri('https://repo.papermc.io/repository/maven-public/') }
maven { url = uri('https://repo.codemc.org/repository/maven-public/') } maven { url = uri('https://repo.codemc.org/repository/maven-public/') }
@@ -990,7 +944,7 @@ project(':adapters:bukkit:nms:v26_3_R1') {
canBeResolved = true canBeResolved = true
} }
dependencies.add(headlessNativeSources.name, dependencies.add(headlessNativeSources.name,
"com.github.VolmitSoftware.VolmLib:native-minecraft26_2-sources:${volmLibCoordinate.tokenize(':').last()}") 'com.github.VolmitSoftware.VolmLib:native-minecraft26_2-sources:master-SNAPSHOT')
List<String> headlessNativeTypes = ['ModdedBlockState', 'ModdedBiome', 'NativeBlockProperties', 'NativeBlockResolver', List<String> headlessNativeTypes = ['ModdedBlockState', 'ModdedBiome', 'NativeBlockProperties', 'NativeBlockResolver',
'NativeBlockFaces', 'NativeStateRotator', 'NativeStateMerger', 'NativeBlockFaces', 'NativeStateRotator', 'NativeStateMerger',
'NativeRegistryAccess', 'ModdedItem', 'ModdedEntityType', 'NativeTileReader', 'NativeTileData'] 'NativeRegistryAccess', 'ModdedItem', 'ModdedEntityType', 'NativeTileReader', 'NativeTileData']
@@ -1016,10 +970,10 @@ project(':adapters:bukkit:nms:v26_3_R1') {
dependencies { dependencies {
offlineImplementation(project(':probe')) offlineImplementation(project(':probe'))
offlineImplementation(project(':core')) offlineImplementation(project(':core'))
offlineImplementation("com.github.VolmitSoftware.VolmLib:native-common:${volmLibCoordinate.tokenize(':').last()}") { offlineImplementation('com.github.VolmitSoftware.VolmLib:native-common:master-SNAPSHOT') {
transitive = false transitive = false
} }
offlineImplementation(volmLibCoordinate) { offlineImplementation(volmLibShared) {
transitive = false transitive = false
} }
} }
@@ -1132,14 +1086,11 @@ commands:
} }
tasks.withType(PackedJar).configureEach { tasks.withType(PackedJar).configureEach {
nativeManifestPath.set('META-INF/iris/native-runtime.properties') nativeManifestPath.set(NativeRuntimeArtifacts.MANIFEST)
nativeResourceDirectory.set('META-INF/iris/native/') nativeResourceDirectory.set(NativeRuntimeArtifacts.RESOURCES)
dependsOn(tasks.named('verifyBukkitArtifact'), configurations.nativeRuntime) dependsOn(tasks.named('verifyBukkitArtifact'), nativeRuntime)
nativeArtifacts.set(providers.provider { nativeArtifacts.set(providers.provider { embeddedNatives() })
Map<ComponentIdentifier, File> resolved = new LinkedHashMap<>() doLast { PackedJar task ->
configurations.nativeRuntime.incoming.artifacts.artifacts.each { artifact -> NativeRuntimeArtifacts.verifyPacked(task.outputJar.get().asFile, nativeRuntimeModules, embeddedNatives())
resolved.put(artifact.id.componentIdentifier, artifact.file) }
}
PackedJar.resolveNativeArtifacts(resolved)
})
} }
+1
View File
@@ -32,6 +32,7 @@ dependencies {
implementation(gradleApi()) implementation(gradleApi())
implementation('org.ow2.asm:asm:9.10.1') implementation('org.ow2.asm:asm:9.10.1')
implementation('org.apache.commons:commons-compress:1.28.0') implementation('org.apache.commons:commons-compress:1.28.0')
implementation('org.tukaani:xz:1.10')
implementation('io.papermc.paperweight:paperweight-userdev:2.0.0-beta.21') implementation('io.papermc.paperweight:paperweight-userdev:2.0.0-beta.21')
testImplementation('junit:junit:4.13.2') testImplementation('junit:junit:4.13.2')
} }
@@ -1,7 +1,7 @@
import org.gradle.api.GradleException; import org.gradle.api.GradleException;
import org.gradle.api.artifacts.component.ComponentIdentifier; import org.tukaani.xz.XZInputStream;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -11,6 +11,7 @@ import java.nio.file.Path;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HexFormat; import java.util.HexFormat;
import java.util.List; import java.util.List;
@@ -21,46 +22,32 @@ import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public final class NativeRuntimeArtifacts { public final class NativeRuntimeArtifacts {
public static final String MANIFEST = "META-INF/iris/native-runtime.properties"; public static final String MANIFEST = "META-INF/iris/native-runtime.properties";
public static final String RESOURCES = "META-INF/iris/native/";
private static final String PAYLOAD = "META-INF/volmit/runtime.jar.xz";
private static final String GROUP = "com.github.VolmitSoftware.VolmLib"; private static final String GROUP = "com.github.VolmitSoftware.VolmLib";
private static final String NATIVE = "art/arcane/volmlib/nativelib/"; private static final String NATIVE = "art/arcane/volmlib/nativelib/";
private NativeRuntimeArtifacts() { private NativeRuntimeArtifacts() {
} }
public static Map<String, File> publishedArtifacts(Map<ComponentIdentifier, File> resolved, public static void generate(Map<String, File> natives, List<String> modules, File output, File rules) throws IOException {
List<String> modules, String version) { requireModules(natives, modules);
Map<String, File> artifacts = new TreeMap<>(); Map<String, File> providers = new TreeMap<>(natives);
for (Map.Entry<ComponentIdentifier, File> entry : resolved.entrySet()) {
if (!(entry.getKey() instanceof ModuleComponentIdentifier component)
|| !GROUP.equals(component.getGroup()) || !version.equals(component.getVersion())
|| !modules.contains(component.getModule())) {
throw new GradleException("Native runtime providers must resolve to published coordinates at "
+ version + ": " + entry.getKey().getDisplayName());
}
if (artifacts.put(component.getModule(), entry.getValue()) != null) {
throw new GradleException("Duplicate native runtime provider: " + component.getModule());
}
}
if (!artifacts.keySet().equals(new TreeSet<>(modules))) {
throw new GradleException("Published native runtime providers differ: " + artifacts.keySet()
+ "; expected " + modules);
}
return artifacts;
}
public static void generate(Map<String, File> artifacts, String version, File output, File rules) throws IOException {
List<String> manifest = new ArrayList<>(); List<String> manifest = new ArrayList<>();
manifest.add("modules=" + String.join(",", artifacts.keySet())); manifest.add("modules=" + String.join(",", providers.keySet()));
manifest.add("repository=https://jitpack.io/"); manifest.add("storage=embedded");
Set<String> retained = new TreeSet<>(); Set<String> retained = new TreeSet<>();
for (Map.Entry<String, File> artifact : artifacts.entrySet()) { for (Map.Entry<String, File> provider : providers.entrySet()) {
String module = artifact.getKey(); String module = provider.getKey();
manifest.add(module + ".coordinate=" + GROUP + ":" + module + ":" + version); String digest = digest(Files.readAllBytes(provider.getValue().toPath()));
manifest.add(module + ".sha256=" + digest(artifact.getValue())); manifest.add(module + ".coordinate=" + GROUP + ":" + module + ":embedded-" + digest);
try (JarFile jar = new JarFile(artifact.getValue())) { manifest.add(module + ".sha256=" + digest);
try (JarFile jar = new JarFile(provider.getValue())) {
Enumeration<JarEntry> entries = jar.entries(); Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement(); JarEntry entry = entries.nextElement();
@@ -92,31 +79,15 @@ public final class NativeRuntimeArtifacts {
Files.write(rules.toPath(), keep, StandardCharsets.UTF_8); Files.write(rules.toPath(), keep, StandardCharsets.UTF_8);
} }
public static void verify(File artifact, List<String> adapters, String version) throws IOException { public static void verify(File artifact, List<String> modules, Map<String, File> natives) throws IOException {
requireModules(natives, modules);
try (JarFile jar = new JarFile(artifact)) { try (JarFile jar = new JarFile(artifact)) {
JarEntry entry = jar.getJarEntry(MANIFEST); JarEntry entry = jar.getJarEntry(MANIFEST);
if (entry == null) { if (entry == null) {
throw new GradleException("Missing native runtime dependency manifest"); throw new GradleException("Missing native runtime dependency manifest");
} }
Properties manifest = new Properties();
try (InputStream input = jar.getInputStream(entry)) { try (InputStream input = jar.getInputStream(entry)) {
manifest.load(input); verifyManifest(input.readAllBytes(), natives);
}
Set<String> expected = new TreeSet<>();
expected.add("native-common");
for (String adapter : adapters) {
expected.add("native-" + adapter);
}
Set<String> actual = new TreeSet<>(List.of(manifest.getProperty("modules", "").split(",")));
if (!expected.equals(actual)) {
throw new GradleException("Native dependency modules differ: " + actual + "; expected " + expected);
}
for (String module : expected) {
String coordinate = GROUP + ":" + module + ":" + version;
if (!coordinate.equals(manifest.getProperty(module + ".coordinate"))
|| !manifest.getProperty(module + ".sha256", "").matches("[0-9a-f]{64}")) {
throw new GradleException("Invalid native runtime dependency: " + module);
}
} }
Enumeration<JarEntry> entries = jar.entries(); Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
@@ -128,6 +99,82 @@ public final class NativeRuntimeArtifacts {
} }
} }
public static void verifyPacked(File artifact, List<String> modules, Map<String, File> natives) throws IOException {
requireModules(natives, modules);
Map<String, byte[]> contents;
try (JarFile jar = new JarFile(artifact)) {
JarEntry payload = jar.getJarEntry(PAYLOAD);
if (payload == null) {
contents = read(Files.newInputStream(artifact.toPath()));
} else {
try (InputStream input = new XZInputStream(jar.getInputStream(payload))) {
contents = read(new ByteArrayInputStream(input.readAllBytes()));
}
JarEntry outer = jar.getJarEntry(MANIFEST);
byte[] embedded = contents.get(MANIFEST);
if (outer != null && embedded != null) {
try (InputStream input = jar.getInputStream(outer)) {
if (!Arrays.equals(input.readAllBytes(), embedded)) {
throw new GradleException("Packed native runtime manifests differ");
}
}
}
}
}
byte[] manifest = contents.get(MANIFEST);
if (manifest == null) {
throw new GradleException("Missing native runtime dependency manifest");
}
verifyManifest(manifest, natives);
for (String module : natives.keySet()) {
byte[] provider = contents.get(RESOURCES + module + ".jar");
if (provider == null) {
throw new GradleException("Missing embedded native provider: " + module);
}
if (!digest(provider).equals(digest(Files.readAllBytes(natives.get(module).toPath())))) {
throw new GradleException("Embedded native provider differs from the resolved build: " + module);
}
}
}
private static void verifyManifest(byte[] bytes, Map<String, File> natives) throws IOException {
Properties manifest = new Properties();
manifest.load(new ByteArrayInputStream(bytes));
Set<String> actual = new TreeSet<>(List.of(manifest.getProperty("modules", "").split(",")));
if (!natives.keySet().equals(actual)) {
throw new GradleException("Native dependency modules differ: " + actual + "; expected " + natives.keySet());
}
if (!"embedded".equals(manifest.getProperty("storage"))) {
throw new GradleException("Native runtime providers must be embedded: " + manifest.getProperty("storage"));
}
for (Map.Entry<String, File> provider : natives.entrySet()) {
String module = provider.getKey();
String digest = digest(Files.readAllBytes(provider.getValue().toPath()));
if (!digest.equals(manifest.getProperty(module + ".sha256"))
|| !(GROUP + ":" + module + ":embedded-" + digest).equals(manifest.getProperty(module + ".coordinate"))) {
throw new GradleException("Native runtime manifest does not describe the embedded provider: " + module);
}
}
}
private static void requireModules(Map<String, File> natives, List<String> modules) {
if (!natives.keySet().equals(new TreeSet<>(modules))) {
throw new GradleException("Embedded native providers differ: " + natives.keySet() + "; expected " + modules);
}
}
private static Map<String, byte[]> read(InputStream source) throws IOException {
Map<String, byte[]> contents = new TreeMap<>();
try (ZipInputStream archive = new ZipInputStream(source)) {
for (ZipEntry entry = archive.getNextEntry(); entry != null; entry = archive.getNextEntry()) {
if (!entry.isDirectory()) {
contents.put(entry.getName(), archive.readAllBytes());
}
}
}
return contents;
}
private static boolean implementation(String name) { private static boolean implementation(String name) {
if (!name.startsWith(NATIVE)) { if (!name.startsWith(NATIVE)) {
return false; return false;
@@ -137,9 +184,9 @@ public final class NativeRuntimeArtifacts {
|| relative.matches("v[0-9_]+R[0-9]+/.*"); || relative.matches("v[0-9_]+R[0-9]+/.*");
} }
private static String digest(File file) throws IOException { private static String digest(byte[] bytes) {
try { try {
return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(file.toPath()))); return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(bytes));
} catch (NoSuchAlgorithmException exception) { } catch (NoSuchAlgorithmException exception) {
throw new IllegalStateException(exception); throw new IllegalStateException(exception);
} }
@@ -1,12 +1,15 @@
import org.gradle.api.GradleException; import org.gradle.api.GradleException;
import org.gradle.api.artifacts.ModuleIdentifier; import org.junit.Rule;
import org.gradle.api.artifacts.component.ComponentIdentifier;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.tukaani.xz.LZMA2Options;
import org.tukaani.xz.XZOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@@ -15,122 +18,149 @@ import java.util.HexFormat;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.TreeMap;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class NativeRuntimeArtifactsTest { public class NativeRuntimeArtifactsTest {
@Test private static final List<String> MODULES = List.of("native-common", "native-v26_2_R1");
public void acceptsOnlyRequestedPublishedCoordinates() {
File common = new File("common.jar"); @Rule
File provider = new File("provider.jar"); public TemporaryFolder temporary = new TemporaryFolder();
Map<ComponentIdentifier, File> resolved = Map.of(
module("native-common", "revision"), common,
module("native-v26_2_R1", "revision"), provider);
assertEquals(Map.of("native-common", common, "native-v26_2_R1", provider),
NativeRuntimeArtifacts.publishedArtifacts(resolved,
List.of("native-common", "native-v26_2_R1"), "revision"));
}
@Test @Test
public void rejectsCompositeProjectArtifacts() { public void describesTheEmbeddedProvidersAndRetainsTheirSharedReferences() throws Exception {
ComponentIdentifier project = () -> "project :VolmLib:native-common"; Map<String, File> natives = natives();
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.publishedArtifacts( File output = temporary.newFolder("generated");
Map.of(project, new File("native-common-local-SNAPSHOT.jar")), List.of("native-common"), "revision")); File rules = new File(output, "keep.pro");
} NativeRuntimeArtifacts.generate(natives, MODULES, output, rules);
@Test
public void rejectsUnexpectedCoordinates() {
List<ComponentIdentifier> invalid = List.of(
new PublishedModule("other.group", "native-common", "revision"),
module("native-common", "other-revision"),
module("native-v26_3_R1", "revision"));
for (ComponentIdentifier component : invalid) {
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.publishedArtifacts(
Map.of(component, new File("provider.jar")), List.of("native-common"), "revision"));
}
}
@Test
public void rejectsMissingProviders() {
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.publishedArtifacts(
Map.of(module("native-common", "revision"), new File("common.jar")),
List.of("native-common", "native-v26_2_R1"), "revision"));
}
@Test
public void recordsExactDependenciesAndRetainsSharedProviderReferences() throws Exception {
Path directory = Files.createTempDirectory("native-artifacts");
File provider = directory.resolve("provider.jar").toFile();
ClassWriter writer = new ClassWriter(0);
writer.visit(Opcodes.V17, Opcodes.ACC_PUBLIC, "art/arcane/volmlib/nativelib/v26_2_R1/terrain/Provider", null,
"art/arcane/volmlib/util/collection/KList", null);
writer.visitEnd();
try (JarOutputStream jar = new JarOutputStream(Files.newOutputStream(provider.toPath()))) {
jar.putNextEntry(new JarEntry("art/arcane/volmlib/nativelib/v26_2_R1/terrain/Provider.class"));
jar.write(writer.toByteArray());
jar.closeEntry();
}
File rules = directory.resolve("keep.pro").toFile();
NativeRuntimeArtifacts.generate(Map.of("native-common", provider, "native-v26_2_R1", provider),
"revision", directory.toFile(), rules);
Properties manifest = new Properties(); Properties manifest = new Properties();
try (Reader reader = Files.newBufferedReader(directory.resolve(NativeRuntimeArtifacts.MANIFEST))) { try (Reader reader = Files.newBufferedReader(output.toPath().resolve(NativeRuntimeArtifacts.MANIFEST))) {
manifest.load(reader); manifest.load(reader);
} }
String digest = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256") assertEquals("native-common,native-v26_2_R1", manifest.getProperty("modules"));
.digest(Files.readAllBytes(provider.toPath()))); assertEquals("embedded", manifest.getProperty("storage"));
assertEquals(digest, manifest.getProperty("native-common.sha256")); assertFalse(manifest.containsKey("repository"));
assertEquals(digest, manifest.getProperty("native-v26_2_R1.sha256")); for (String module : MODULES) {
String digest = digest(Files.readAllBytes(natives.get(module).toPath()));
assertEquals(digest, manifest.getProperty(module + ".sha256"));
assertEquals("com.github.VolmitSoftware.VolmLib:" + module + ":embedded-" + digest,
manifest.getProperty(module + ".coordinate"));
}
assertTrue(Files.readString(rules.toPath()).contains("-keep class art.arcane.volmlib.util.collection.KList { *; }")); assertTrue(Files.readString(rules.toPath()).contains("-keep class art.arcane.volmlib.util.collection.KList { *; }"));
File plugin = directory.resolve("plugin.jar").toFile(); assertEquals(List.of("art/arcane/volmlib/util/collection/KList"),
try (JarOutputStream jar = new JarOutputStream(Files.newOutputStream(plugin.toPath()))) { Files.readAllLines(output.toPath().resolve("META-INF/volmit/runtime-roots.list")));
jar.putNextEntry(new JarEntry(NativeRuntimeArtifacts.MANIFEST));
Files.copy(directory.resolve(NativeRuntimeArtifacts.MANIFEST), jar);
jar.closeEntry();
}
NativeRuntimeArtifacts.verify(plugin, List.of("v26_2_R1"), "revision");
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin, List.of("v26_3_R1"), "revision"));
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin, List.of("v26_2_R1"), "other"));
} }
private static ModuleComponentIdentifier module(String module, String version) { @Test
return new PublishedModule("com.github.VolmitSoftware.VolmLib", module, version); public void rejectsAProviderSetThatDiffersFromTheRuntimeModules() throws Exception {
Map<String, File> natives = natives();
natives.remove("native-v26_2_R1");
File output = temporary.newFolder("generated");
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.generate(natives, MODULES, output,
new File(output, "keep.pro")));
} }
private record PublishedModule(String group, String module, String version) @Test
implements ModuleComponentIdentifier, ModuleIdentifier { public void verifiesThePluginManifestAgainstTheProvidersBeingEmbedded() throws Exception {
@Override Map<String, File> natives = natives();
public String getGroup() { File plugin = plugin(natives, Map.of());
return group; NativeRuntimeArtifacts.verify(plugin, MODULES, natives);
} assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin,
List.of("native-common", "native-v26_3_R1"), natives));
Map<String, File> rebuilt = new TreeMap<>(natives);
rebuilt.put("native-v26_2_R1", provider("rebuilt.jar", "art/arcane/volmlib/util/collection/KMap"));
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin, MODULES, rebuilt));
}
@Override @Test
public String getModule() { public void rejectsProviderImplementationsBundledOutsideTheEmbeddedJars() throws Exception {
return module; Map<String, File> natives = natives();
} File plugin = plugin(natives, Map.of("art/arcane/volmlib/nativelib/v26_2_R1/terrain/Provider.class", new byte[0]));
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin, MODULES, natives));
}
@Override @Test
public String getVersion() { public void verifiesTheEmbeddedProviderBytesInsideThePackedRuntime() throws Exception {
return version; Map<String, File> natives = natives();
} Map<String, byte[]> runtime = runtime(natives, natives);
NativeRuntimeArtifacts.verifyPacked(packed(runtime), MODULES, natives);
NativeRuntimeArtifacts.verifyPacked(jar("ordinary.jar", runtime), MODULES, natives);
Map<String, File> rebuilt = new TreeMap<>(natives);
rebuilt.put("native-v26_2_R1", provider("rebuilt.jar", "art/arcane/volmlib/util/collection/KMap"));
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verifyPacked(packed(runtime(natives, rebuilt)),
MODULES, natives));
Map<String, byte[]> missing = new TreeMap<>(runtime);
missing.remove(NativeRuntimeArtifacts.RESOURCES + "native-v26_2_R1.jar");
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verifyPacked(packed(missing), MODULES, natives));
}
@Override private Map<String, File> natives() throws IOException {
public ModuleIdentifier getModuleIdentifier() { Map<String, File> natives = new TreeMap<>();
return this; natives.put("native-common", provider("common.jar", "java/lang/Object"));
} natives.put("native-v26_2_R1", provider("v26_2_R1.jar", "art/arcane/volmlib/util/collection/KList"));
return natives;
}
@Override private File provider(String name, String superclass) throws IOException {
public String getName() { ClassWriter writer = new ClassWriter(0);
return module; writer.visit(Opcodes.V17, Opcodes.ACC_PUBLIC, "art/arcane/volmlib/nativelib/v26_2_R1/terrain/Provider", null,
} superclass, null);
writer.visitEnd();
return jar(name, Map.of("art/arcane/volmlib/nativelib/v26_2_R1/terrain/Provider.class", writer.toByteArray()));
}
@Override private File plugin(Map<String, File> natives, Map<String, byte[]> extra) throws IOException {
public String getDisplayName() { File generated = temporary.newFolder();
return group + ":" + module + ":" + version; NativeRuntimeArtifacts.generate(natives, MODULES, generated, new File(generated, "keep.pro"));
Map<String, byte[]> contents = new TreeMap<>(extra);
contents.put(NativeRuntimeArtifacts.MANIFEST,
Files.readAllBytes(generated.toPath().resolve(NativeRuntimeArtifacts.MANIFEST)));
return jar("plugin.jar", contents);
}
private Map<String, byte[]> runtime(Map<String, File> described, Map<String, File> embedded) throws IOException {
File generated = temporary.newFolder();
NativeRuntimeArtifacts.generate(described, MODULES, generated, new File(generated, "keep.pro"));
Map<String, byte[]> contents = new TreeMap<>();
contents.put(NativeRuntimeArtifacts.MANIFEST,
Files.readAllBytes(generated.toPath().resolve(NativeRuntimeArtifacts.MANIFEST)));
for (Map.Entry<String, File> provider : embedded.entrySet()) {
contents.put(NativeRuntimeArtifacts.RESOURCES + provider.getKey() + ".jar",
Files.readAllBytes(provider.getValue().toPath()));
} }
return contents;
}
private File packed(Map<String, byte[]> runtime) throws IOException {
ByteArrayOutputStream compressed = new ByteArrayOutputStream();
try (XZOutputStream xz = new XZOutputStream(compressed, new LZMA2Options(1))) {
xz.write(Files.readAllBytes(jar("payload.jar", runtime).toPath()));
}
Map<String, byte[]> outer = new TreeMap<>();
outer.put(NativeRuntimeArtifacts.MANIFEST, runtime.get(NativeRuntimeArtifacts.MANIFEST));
outer.put("META-INF/volmit/runtime.jar.xz", compressed.toByteArray());
return jar("packed.jar", outer);
}
private File jar(String name, Map<String, byte[]> contents) throws IOException {
Path file = temporary.newFolder().toPath().resolve(name);
try (JarOutputStream jar = new JarOutputStream(Files.newOutputStream(file))) {
for (Map.Entry<String, byte[]> entry : contents.entrySet()) {
jar.putNextEntry(new JarEntry(entry.getKey()));
jar.write(entry.getValue());
jar.closeEntry();
}
}
return file.toFile();
}
private static String digest(byte[] bytes) throws Exception {
return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(bytes));
} }
} }
+3 -6
View File
@@ -38,9 +38,6 @@ plugins {
apply from: rootProject.file('gradle/runtime-relocations.gradle') apply from: rootProject.file('gradle/runtime-relocations.gradle')
def lib = 'art.arcane.iris.util' def lib = 'art.arcane.iris.util'
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
.orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')
.get()
String languageSourceReference = providers.gradleProperty('languageRevision').orElse('master').get() String languageSourceReference = providers.gradleProperty('languageRevision').orElse('master').get()
List<File> languageSourceFiles = fileTree('src/main/resources/languages') { List<File> languageSourceFiles = fileTree('src/main/resources/languages') {
@@ -93,10 +90,10 @@ tasks.named('processResources').configure {
*/ */
dependencies { dependencies {
api(project(':spi')) api(project(':spi'))
api("com.github.VolmitSoftware.VolmLib:native-api:${volmLibCoordinate.tokenize(':').last()}") { api('com.github.VolmitSoftware.VolmLib:native-api:master-SNAPSHOT') {
transitive = false transitive = false
} }
api("com.github.VolmitSoftware.VolmLib:native-terrain-api:${volmLibCoordinate.tokenize(':').last()}") { api('com.github.VolmitSoftware.VolmLib:native-terrain-api:master-SNAPSHOT') {
transitive = false transitive = false
} }
@@ -129,7 +126,7 @@ dependencies {
// Shaded // Shaded
implementation('de.crazydev22.slimjar.helper:spigot:2.1.9') implementation('de.crazydev22.slimjar.helper:spigot:2.1.9')
implementation(volmLibCoordinate) { implementation('com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
implementation('com.moandjiezana.toml:toml4j:0.7.2') { implementation('com.moandjiezana.toml:toml4j:0.7.2') {
@@ -2,26 +2,9 @@ package art.arcane.iris.platform.bootstrap;
import art.arcane.volmlib.nativelib.NativeVersion; import art.arcane.volmlib.nativelib.NativeVersion;
import art.arcane.volmlib.nativelib.NativeAdapters; import art.arcane.volmlib.nativelib.NativeAdapters;
import io.github.slimjar.app.builder.ApplicationBuilder;
import io.github.slimjar.downloader.DependencyDownloader;
import io.github.slimjar.downloader.URLDependencyDownloader;
import io.github.slimjar.downloader.strategy.FilePathStrategy;
import io.github.slimjar.downloader.verify.FileChecksumCalculator;
import io.github.slimjar.relocation.helper.RelocationHelper;
import io.github.slimjar.relocation.helper.VerifyingRelocationHelperFactory;
import io.github.slimjar.relocation.meta.FlatFileMetaMediatorFactory;
import io.github.slimjar.exceptions.DownloaderException;
import io.github.slimjar.resolver.data.Dependency;
import io.github.slimjar.resolver.data.DependencyData;
import io.github.slimjar.resolver.data.Repository;
import io.github.slimjar.resolver.reader.dependency.DependencyDataProvider;
import io.github.slimjar.resolver.reader.dependency.DependencyReader;
import io.github.slimjar.resolver.reader.dependency.WrappingDependencyDataProviderFactory;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.nio.file.Files; import java.nio.file.Files;
@@ -31,20 +14,16 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HexFormat; import java.util.HexFormat;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties; import java.util.Properties;
public final class NativeRuntimeLibraries { public final class NativeRuntimeLibraries {
private static final String MANIFEST = "META-INF/iris/native-runtime.properties"; private static final String MANIFEST = "META-INF/iris/native-runtime.properties";
private final Map<Dependency, String> dependencies; private static final String GROUP = "com.github.VolmitSoftware.VolmLib";
private final Repository repository; private final List<Provider> providers;
private NativeRuntimeLibraries(Map<Dependency, String> dependencies, Repository repository) { private NativeRuntimeLibraries(List<Provider> providers) {
this.dependencies = dependencies; this.providers = providers;
this.repository = repository;
} }
public static NativeRuntimeLibraries load(String minecraftVersion) { public static NativeRuntimeLibraries load(String minecraftVersion) {
@@ -60,60 +39,27 @@ public final class NativeRuntimeLibraries {
} }
} }
static NativeRuntimeLibraries from(Properties manifest, String minecraftVersion) throws IOException { static NativeRuntimeLibraries from(Properties manifest, String minecraftVersion) {
String storage = manifest.getProperty("storage", "repository"); String storage = manifest.getProperty("storage");
if (!storage.equals("repository") && !storage.equals("embedded")) { if (!"embedded".equals(storage)) {
throw new IllegalStateException("Invalid native runtime storage: " + storage); throw new IllegalStateException("Invalid native runtime storage: " + storage);
} }
boolean embedded = storage.equals("embedded");
NativeVersion version = NativeVersion.resolve(minecraftVersion).orElseThrow( NativeVersion version = NativeVersion.resolve(minecraftVersion).orElseThrow(
() -> new UnsupportedOperationException("No native provider for Minecraft " + minecraftVersion)); () -> new UnsupportedOperationException("No native provider for Minecraft " + minecraftVersion));
List<String> modules = List.of(manifest.getProperty("modules", "").split(",")); List<String> modules = List.of(manifest.getProperty("modules", "").split(","));
Map<Dependency, String> selected = new LinkedHashMap<>(); List<Provider> selected = new ArrayList<>(2);
for (String module : List.of("native-common", "native-" + version.packageName())) { for (String module : List.of("native-common", "native-" + version.packageName())) {
if (!modules.contains(module)) { if (!modules.contains(module)) {
throw new UnsupportedOperationException("Native provider is not distributed: " + module); throw new UnsupportedOperationException("Native provider is not distributed: " + module);
} }
String[] coordinate = manifest.getProperty(module + ".coordinate", "").split(":");
String digest = manifest.getProperty(module + ".sha256", ""); String digest = manifest.getProperty(module + ".sha256", "");
if (coordinate.length != 3 || !coordinate[0].equals("com.github.VolmitSoftware.VolmLib") String coordinate = GROUP + ":" + module + ":embedded-" + digest;
|| !coordinate[1].equals(module) || coordinate[2].isBlank() || !digest.matches("[0-9a-f]{64}") if (!digest.matches("[0-9a-f]{64}") || !coordinate.equals(manifest.getProperty(module + ".coordinate"))) {
|| embedded && !coordinate[2].equals("embedded-" + digest)) {
throw new IllegalStateException("Invalid native dependency manifest entry: " + module); throw new IllegalStateException("Invalid native dependency manifest entry: " + module);
} }
selected.put(new Dependency(coordinate[0], coordinate[1], coordinate[2], null, List.of()), digest); selected.add(new Provider(module, "embedded-" + digest, digest));
} }
Repository repository = embedded ? null : new Repository(URI.create(manifest.getProperty("repository")).toURL()); return new NativeRuntimeLibraries(List.copyOf(selected));
return new NativeRuntimeLibraries(Map.copyOf(selected), repository);
}
public <T extends ApplicationBuilder<?>> T configure(T builder, Path downloads) {
WrappingDependencyDataProviderFactory defaults = new WrappingDependencyDataProviderFactory(DependencyReader.DEFAULT);
builder.dataProviderFactory(url -> {
DependencyDataProvider provider = defaults.create(url);
return () -> merge(provider.get());
});
VerifyingRelocationHelperFactory relocation = new VerifyingRelocationHelperFactory(
new FileChecksumCalculator("SHA-256"),
FilePathStrategy.createRelocationStrategy(downloads.toFile(), "Iris"),
new FlatFileMetaMediatorFactory());
builder.relocationHelperFactory(relocator -> {
RelocationHelper helper = relocation.create(relocator);
return (dependency, file) -> dependencies.containsKey(dependency) ? file : helper.relocate(dependency, file);
});
builder.downloaderFactory((output, resolver, verifier) -> {
DependencyDownloader delegate = new URLDependencyDownloader(output, resolver, verifier);
return dependency -> {
Optional<File> result = delegate.download(dependency);
String expected = dependencies.get(dependency);
if (expected != null) {
File file = result.orElseThrow(() -> new DownloaderException("Native dependency has no jar: " + dependency));
verify(file, expected);
}
return result;
};
});
return builder;
} }
public URLClassLoader openProviderLoader(Path downloads) { public URLClassLoader openProviderLoader(Path downloads) {
@@ -128,31 +74,13 @@ public final class NativeRuntimeLibraries {
} }
} }
DependencyData merge(DependencyData original) {
if (repository == null) {
return original;
}
List<Dependency> combined = new ArrayList<>(original.dependencies());
combined.addAll(dependencies.keySet());
List<Repository> repositories = new ArrayList<>(original.repositories());
if (!repositories.contains(repository)) {
repositories.add(repository);
}
return new DependencyData(original.mirrors(), repositories, combined, original.relocations());
}
List<URL> providerUrls(Path downloads, ClassLoader resources) throws IOException { List<URL> providerUrls(Path downloads, ClassLoader resources) throws IOException {
List<URL> urls = new ArrayList<>(dependencies.size()); List<URL> urls = new ArrayList<>(providers.size());
for (Map.Entry<Dependency, String> entry : dependencies.entrySet()) { for (Provider provider : providers) {
Dependency dependency = entry.getKey(); Path artifact = downloads.resolve(GROUP.replace('.', '/'))
Path artifact = downloads.resolve(dependency.groupId().replace('.', '/')) .resolve(provider.module()).resolve(provider.version())
.resolve(dependency.artifactId()).resolve(dependency.version()) .resolve(provider.module() + "-" + provider.version() + ".jar");
.resolve(dependency.artifactId() + "-" + dependency.version() + ".jar"); extractEmbedded(resources, provider.module(), artifact, provider.sha256());
if (repository == null) {
extractEmbedded(resources, dependency.artifactId(), artifact, entry.getValue());
} else {
verify(artifact.toFile(), entry.getValue());
}
urls.add(artifact.toUri().toURL()); urls.add(artifact.toUri().toURL());
} }
return urls; return urls;
@@ -171,7 +99,7 @@ public final class NativeRuntimeLibraries {
Path temporary = Files.createTempFile(artifact.getParent(), module + "-", ".tmp"); Path temporary = Files.createTempFile(artifact.getParent(), module + "-", ".tmp");
try { try {
Files.copy(input, temporary, StandardCopyOption.REPLACE_EXISTING); Files.copy(input, temporary, StandardCopyOption.REPLACE_EXISTING);
verify(temporary.toFile(), expected); verify(temporary, expected);
Files.move(temporary, artifact, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); Files.move(temporary, artifact, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
} finally { } finally {
Files.deleteIfExists(temporary); Files.deleteIfExists(temporary);
@@ -187,15 +115,12 @@ public final class NativeRuntimeLibraries {
} }
} }
static void verify(File file, String expected) { static void verify(Path file, String expected) throws IOException {
try { if (!digest(file).equals(expected)) {
String actual = digest(file.toPath()); throw new IOException("Native dependency does not match this Iris build: " + file.getFileName());
if (!actual.equals(expected)) {
throw new DownloaderException("Native dependency does not match this Iris build: " + file.getName());
}
} catch (IOException exception) {
throw new DownloaderException("Cannot verify native dependency: " + file, exception);
} }
} }
private record Provider(String module, String version, String sha256) {
}
} }
@@ -33,7 +33,7 @@ public class SlimJar {
return; return;
} }
NativeRuntimeLibraries libraries = NativeRuntimeLibraries.load(minecraftVersion); NativeRuntimeLibraries libraries = NativeRuntimeLibraries.load(minecraftVersion);
libraries.configure(ApplicationBuilder.appending("Iris"), downloadPath) ApplicationBuilder.appending("Iris")
.injectableFactory(InjectableFactory.selecting( .injectableFactory(InjectableFactory.selecting(
InjectableFactory.ERROR, InjectableFactory.ERROR,
InjectableFactory.INJECTABLE, InjectableFactory.INJECTABLE,
@@ -78,7 +78,7 @@ public class SlimJar {
Path downloadPath = plugin.getDataFolder("cache", "libraries").toPath(); Path downloadPath = plugin.getDataFolder("cache", "libraries").toPath();
debug(plugin, "Loading libraries..."); debug(plugin, "Loading libraries...");
NativeRuntimeLibraries libraries = NativeRuntimeLibraries.load(Bukkit.getBukkitVersion()); NativeRuntimeLibraries libraries = NativeRuntimeLibraries.load(Bukkit.getBukkitVersion());
libraries.configure(ApplicationBuilder.appending(plugin.getName()), downloadPath) ApplicationBuilder.appending(plugin.getName())
.injectableFactory(InjectableFactory.selecting(InjectableFactory.ERROR, InjectableFactory.INJECTABLE, .injectableFactory(InjectableFactory.selecting(InjectableFactory.ERROR, InjectableFactory.INJECTABLE,
InjectableFactory.WRAPPED, InjectableFactory.UNSAFE)) InjectableFactory.WRAPPED, InjectableFactory.UNSAFE))
.downloadDirectoryPath(downloadPath) .downloadDirectoryPath(downloadPath)
@@ -1,8 +1,5 @@
package art.arcane.iris.platform.bootstrap; package art.arcane.iris.platform.bootstrap;
import io.github.slimjar.exceptions.DownloaderException;
import io.github.slimjar.resolver.data.Dependency;
import io.github.slimjar.resolver.data.DependencyData;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
@@ -28,7 +25,6 @@ import java.util.stream.Stream;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -37,37 +33,36 @@ public class NativeRuntimeLibrariesTest {
public TemporaryFolder temporary = new TemporaryFolder(); public TemporaryFolder temporary = new TemporaryFolder();
@Test @Test
public void selectsOnlyCommonAndMatchingProvider() throws Exception { public void providersMustBeEmbedded() throws Exception {
Properties manifest = manifest(); EmbeddedFixture fixture = embeddedFixture();
Dependency original = new Dependency("example", "library", "1", null, List.of()); fixture.manifest().setProperty("storage", "repository");
DependencyData data = new DependencyData(List.of(), List.of(), List.of(original), List.of()); assertThrows(IllegalStateException.class, () -> NativeRuntimeLibraries.from(fixture.manifest(), "26.2.0"));
List<String> selected = NativeRuntimeLibraries.from(manifest, "26.3.0").merge(data).dependencies() fixture.manifest().remove("storage");
.stream().map(Dependency::artifactId).sorted().toList(); assertThrows(IllegalStateException.class, () -> NativeRuntimeLibraries.from(fixture.manifest(), "26.2.0"));
assertEquals(List.of("library", "native-common", "native-v26_3_R1"), selected); }
assertThrows(UnsupportedOperationException.class, () -> NativeRuntimeLibraries.from(manifest, "1.21.11"));
@Test
public void rejectsMinecraftVersionsWithoutProvider() throws Exception {
EmbeddedFixture fixture = embeddedFixture();
assertThrows(UnsupportedOperationException.class, () -> NativeRuntimeLibraries.from(fixture.manifest(), "1.21.11"));
fixture.manifest().setProperty("modules", "native-common,native-v26_2_R1");
assertThrows(UnsupportedOperationException.class, () -> NativeRuntimeLibraries.from(fixture.manifest(), "26.3.0"));
} }
@Test @Test
public void rejectsProviderBytesFromDifferentBuild() throws Exception { public void rejectsProviderBytesFromDifferentBuild() throws Exception {
Path jar = Files.createTempFile("native-runtime", ".jar"); Path jar = temporary.newFile("native-runtime.jar").toPath();
try { Files.write(jar, new byte[0]);
Files.write(jar, new byte[0]); NativeRuntimeLibraries.verify(jar, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
NativeRuntimeLibraries.verify(jar.toFile(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); Files.writeString(jar, "different build");
Files.writeString(jar, "different build"); assertThrows(IOException.class, () -> NativeRuntimeLibraries.verify(jar,
assertThrows(DownloaderException.class, () -> NativeRuntimeLibraries.verify(jar.toFile(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
} finally {
Files.deleteIfExists(jar);
}
} }
@Test @Test
public void embeddedProvidersLoadWithoutRepositoryAndSelectOnlyCurrentVersion() throws Exception { public void embeddedProvidersSelectOnlyCommonAndCurrentVersion() throws Exception {
EmbeddedFixture fixture = embeddedFixture(); EmbeddedFixture fixture = embeddedFixture();
NativeRuntimeLibraries libraries = NativeRuntimeLibraries.from(fixture.manifest(), "26.2.0"); NativeRuntimeLibraries libraries = NativeRuntimeLibraries.from(fixture.manifest(), "26.2.0");
Dependency original = new Dependency("example", "library", "1", null, List.of());
DependencyData data = new DependencyData(List.of(), List.of(), List.of(original), List.of());
assertSame(data, libraries.merge(data));
Path downloads = temporary.newFolder("downloads").toPath(); Path downloads = temporary.newFolder("downloads").toPath();
try (URLClassLoader resources = new URLClassLoader(new URL[]{fixture.resources().toUri().toURL()}, null)) { try (URLClassLoader resources = new URLClassLoader(new URL[]{fixture.resources().toUri().toURL()}, null)) {
List<URL> providers = libraries.providerUrls(downloads, resources); List<URL> providers = libraries.providerUrls(downloads, resources);
@@ -125,7 +120,7 @@ public class NativeRuntimeLibrariesTest {
NativeRuntimeLibraries libraries = NativeRuntimeLibraries.from(fixture.manifest(), "26.2.0"); NativeRuntimeLibraries libraries = NativeRuntimeLibraries.from(fixture.manifest(), "26.2.0");
Path downloads = temporary.newFolder("downloads").toPath(); Path downloads = temporary.newFolder("downloads").toPath();
try (URLClassLoader resources = new URLClassLoader(new URL[]{fixture.resources().toUri().toURL()}, null)) { try (URLClassLoader resources = new URLClassLoader(new URL[]{fixture.resources().toUri().toURL()}, null)) {
DownloaderException failure = assertThrows(DownloaderException.class, () -> libraries.providerUrls(downloads, resources)); IOException failure = assertThrows(IOException.class, () -> libraries.providerUrls(downloads, resources));
assertTrue(failure.getMessage().contains("native-v26_2_R1")); assertTrue(failure.getMessage().contains("native-v26_2_R1"));
assertNoFailedExtract(downloads); assertNoFailedExtract(downloads);
} }
@@ -139,12 +134,13 @@ public class NativeRuntimeLibrariesTest {
} }
private EmbeddedFixture embeddedFixture() throws Exception { private EmbeddedFixture embeddedFixture() throws Exception {
Properties properties = manifest(); Properties properties = new Properties();
List<String> modules = List.of("native-common", "native-v26_2_R1", "native-v26_3_R1");
properties.setProperty("modules", String.join(",", modules));
properties.setProperty("storage", "embedded"); properties.setProperty("storage", "embedded");
properties.remove("repository");
Path resources = temporary.newFolder("resources").toPath(); Path resources = temporary.newFolder("resources").toPath();
Path providers = Files.createDirectories(resources.resolve("META-INF/iris/native")); Path providers = Files.createDirectories(resources.resolve("META-INF/iris/native"));
for (String module : properties.getProperty("modules").split(",")) { for (String module : modules) {
ByteArrayOutputStream output = new ByteArrayOutputStream(); ByteArrayOutputStream output = new ByteArrayOutputStream();
try (JarOutputStream jar = new JarOutputStream(output)) { try (JarOutputStream jar = new JarOutputStream(output)) {
jar.putNextEntry(new JarEntry("provider.txt")); jar.putNextEntry(new JarEntry("provider.txt"));
@@ -169,16 +165,4 @@ public class NativeRuntimeLibrariesTest {
private record EmbeddedFixture(Properties manifest, Path resources) { private record EmbeddedFixture(Properties manifest, Path resources) {
} }
private static Properties manifest() {
Properties properties = new Properties();
List<String> modules = List.of("native-common", "native-v26_2_R1", "native-v26_3_R1");
properties.setProperty("modules", String.join(",", modules));
properties.setProperty("repository", "https://jitpack.io/");
for (String module : modules) {
properties.setProperty(module + ".coordinate", "com.github.VolmitSoftware.VolmLib:" + module + ":revision");
properties.setProperty(module + ".sha256", "0".repeat(64));
}
return properties;
}
} }
-1
View File
@@ -28,4 +28,3 @@ apiVersion=26.1
fabricLoaderVersion=0.19.5 fabricLoaderVersion=0.19.5
forgeVersion=26.3-66.0.8 forgeVersion=26.3-66.0.8
neoForgeVersion=26.3.0.33-beta neoForgeVersion=26.3.0.33-beta
volmLibCoordinate=com.github.VolmitSoftware.VolmLib:volmlib-shared:d24d28acddbf3555c0c30ac99569ebacd476ce27
+1 -2
View File
@@ -13,13 +13,12 @@ Configuration kernelDependencies = configurations.create('generationKernelDepend
extendsFrom(configurations.implementation, configurations.slim, configurations.annotationProcessor) extendsFrom(configurations.implementation, configurations.slim, configurations.annotationProcessor)
} }
dependencies.add(kernelDependencies.name, libs.guava) dependencies.add(kernelDependencies.name, libs.guava)
String nativeLibraryVersion = providers.gradleProperty('volmLibCoordinate').get().tokenize(':').last()
List<String> nativeRevisionModules = ['native-common', 'native-minecraft26_2-sources'] List<String> nativeRevisionModules = ['native-common', 'native-minecraft26_2-sources']
for (String adapter : rootProject.extensions.extraProperties.get('nativeDevelopmentAdapters')) { for (String adapter : rootProject.extensions.extraProperties.get('nativeDevelopmentAdapters')) {
nativeRevisionModules.add('native-' + adapter) nativeRevisionModules.add('native-' + adapter)
} }
for (String module : nativeRevisionModules) { for (String module : nativeRevisionModules) {
dependencies.add(kernelDependencies.name, "com.github.VolmitSoftware.VolmLib:${module}:${nativeLibraryVersion}") { dependencies.add(kernelDependencies.name, "com.github.VolmitSoftware.VolmLib:${module}:master-SNAPSHOT") {
transitive = false transitive = false
} }
} }
+1 -5
View File
@@ -3,16 +3,12 @@ Configuration nativeMinecraftSources = configurations.create('nativeMinecraftSou
canBeResolved = true canBeResolved = true
} }
Properties nativeLibraryProperties = new Properties()
file('../../gradle.properties').withInputStream { InputStream stream -> nativeLibraryProperties.load(stream) }
String nativeLibraryCoordinate = providers.gradleProperty('volmLibCoordinate')
.getOrElse(nativeLibraryProperties.getProperty('volmLibCoordinate'))
String nativeSourceLoader = extensions.extraProperties.get('nativeSourceLoader') String nativeSourceLoader = extensions.extraProperties.get('nativeSourceLoader')
String nativeSourceImplementation = 'minecraft26_2' String nativeSourceImplementation = 'minecraft26_2'
String nativeSourceMinecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse('26.3') String nativeSourceMinecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse('26.3')
extensions.extraProperties.set('nativeSourceImplementation', nativeSourceImplementation) extensions.extraProperties.set('nativeSourceImplementation', nativeSourceImplementation)
dependencies.add(nativeMinecraftSources.name, dependencies.add(nativeMinecraftSources.name,
"com.github.VolmitSoftware.VolmLib:native-${nativeSourceImplementation}-sources:${nativeLibraryCoordinate.tokenize(':').last()}") "com.github.VolmitSoftware.VolmLib:native-${nativeSourceImplementation}-sources:master-SNAPSHOT")
TaskProvider<Sync> unpackNativeMinecraftSources = tasks.register('unpackNativeMinecraftSources', Sync) { TaskProvider<Sync> unpackNativeMinecraftSources = tasks.register('unpackNativeMinecraftSources', Sync) {
dependsOn(nativeMinecraftSources) dependsOn(nativeMinecraftSources)
+49 -4
View File
@@ -40,10 +40,17 @@
* otherwise walk up from settingsDir looking for a `VolmLib` directory * otherwise walk up from settingsDir looking for a `VolmLib` directory
* that contains a settings script. * that contains a settings script.
* *
* When nothing is found the build falls back to the published `volmLibCoordinate` from * When nothing is found, or substitution is disabled, every VolmLib coordinate resolves the published
* gradle.properties. * master-SNAPSHOT from JitPack instead, re-checked on every build like the rest of the plugin fleet.
*
* The outcome is logged once per top-level build and handed to the build scripts through `gradle`
* extra properties: `volmLibDirectory` (the substituted checkout, or null) and `volmLibStamp` (the
* content of META-INF/volmit/volmlib.properties in every shipped jar).
*/ */
import org.gradle.api.artifacts.Configuration
import org.gradle.process.ExecOutput
import java.io.File import java.io.File
boolean irisHasVolmLibSettings(File directory) { boolean irisHasVolmLibSettings(File directory) {
@@ -85,9 +92,47 @@ boolean irisUseLocalVolmLib = providers.gradleProperty('useLocalVolmLib')
.orElse('true') .orElse('true')
.map { String value -> value.equalsIgnoreCase('true') } .map { String value -> value.equalsIgnoreCase('true') }
.get() .get()
File irisLocalVolmLibDirectory = irisResolveLocalVolmLibDirectory() File irisLocalVolmLibDirectory = irisUseLocalVolmLib ? irisResolveLocalVolmLibDirectory() : null
if (irisUseLocalVolmLib && irisLocalVolmLibDirectory != null) { String irisGit(File directory, String... arguments) {
try {
ExecOutput result = providers.exec {
commandLine(['git', '-C', directory.absolutePath] + arguments.toList())
ignoreExitValue = true
}
return result.result.get().exitValue == 0 ? result.standardOutput.asText.get().trim() : null
} catch (Exception ignored) {
return null
}
}
Map<String, String> irisVolmLibStamp
String irisVolmLibDescription
if (irisLocalVolmLibDirectory == null) {
irisVolmLibStamp = [source: 'published master-SNAPSHOT']
irisVolmLibDescription = 'published master-SNAPSHOT'
gradle.allprojects { Project project ->
project.buildscript.configurations.configureEach { Configuration configuration ->
configuration.resolutionStrategy.cacheChangingModulesFor(0, 'seconds')
}
project.configurations.configureEach { Configuration configuration ->
configuration.resolutionStrategy.cacheChangingModulesFor(0, 'seconds')
}
}
} else {
String commit = irisGit(irisLocalVolmLibDirectory, 'rev-parse', 'HEAD')
String status = commit == null ? null : irisGit(irisLocalVolmLibDirectory, '--no-optional-locks', 'status', '--porcelain')
String dirty = status == null ? 'unknown' : String.valueOf(!status.isEmpty())
irisVolmLibStamp = [source: 'local', commit: commit ?: 'unknown', dirty: dirty]
irisVolmLibDescription = "${irisLocalVolmLibDirectory.canonicalPath} @ ${commit == null ? 'unknown' : commit.take(7)}${dirty == 'true' ? '+dirty' : ''}"
}
gradle.extensions.extraProperties.set('volmLibDirectory', irisLocalVolmLibDirectory)
gradle.extensions.extraProperties.set('volmLibStamp', irisVolmLibStamp)
if (gradle.parent == null) {
logger.lifecycle("VolmLib: ${irisVolmLibDescription}")
}
if (irisLocalVolmLibDirectory != null) {
includeBuild(irisLocalVolmLibDirectory) { includeBuild(irisLocalVolmLibDirectory) {
dependencySubstitution { dependencySubstitution {
substitute(module('com.github.VolmitSoftware:VolmLib')).using(project(':shared')) substitute(module('com.github.VolmitSoftware:VolmLib')).using(project(':shared'))
+26
View File
@@ -0,0 +1,26 @@
/*
* Iris is a World Generator for Minecraft Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Writes the VolmLib provenance that gradle/volmlib-resolution.settings.gradle resolved. Every shipped jar
// carries it as META-INF/volmit/volmlib.properties.
Map<String, String> volmLibStamp = (Map<String, String>) gradle.extensions.extraProperties.get('volmLibStamp')
tasks.register('generateVolmLibStamp', WriteProperties) {
destinationFile.set(layout.buildDirectory.file('generated/volmlib-stamp/volmlib.properties'))
properties(volmLibStamp)
}
+1 -1
View File
@@ -1,4 +1,4 @@
jdk: jdk:
- openjdk25 - openjdk25
install: install:
- ./gradlew --no-daemon build publishToMavenLocal -PuseLocalVolmLib=false -PuseMavenLocal=false - ./gradlew --no-daemon build publishToMavenLocal -PuseLocalVolmLib=false
+2 -6
View File
@@ -9,10 +9,6 @@ application {
mainClass = 'art.arcane.iris.probe.ClassloadProbe' mainClass = 'art.arcane.iris.probe.ClassloadProbe'
} }
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
.orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')
.get()
configurations { configurations {
studioProbeApi studioProbeApi
} }
@@ -23,10 +19,10 @@ dependencies {
testImplementation('junit:junit:4.13.2') testImplementation('junit:junit:4.13.2')
testImplementation(libs.gson) testImplementation(libs.gson)
testImplementation(testFixtures(project(':core'))) testImplementation(testFixtures(project(':core')))
testImplementation(volmLibCoordinate) { testImplementation('com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
compileOnly(volmLibCoordinate) { compileOnly('com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false transitive = false
} }
runtimeOnly(libs.paralithic) runtimeOnly(libs.paralithic)
-59
View File
@@ -1,59 +0,0 @@
@echo off
REM Publishes VolmLib to the local Maven repository under the coordinate this project
REM resolves by default: com.github.VolmitSoftware:VolmLib:master-SNAPSHOT.
REM
REM VolmLib is normally picked up as a composite build straight from source, so this is
REM only needed when you build with -PuseLocalVolmLib=false, or when another tool has to
REM resolve VolmLib from the local Maven repository rather than the included build.
REM
REM The lookup mirrors settings.gradle: it walks up from this script looking for a
REM VolmLib checkout sitting beside the project root. Set VOLMLIB_DIR to override.
REM Any extra arguments are forwarded to gradle.
setlocal
set "SCRIPT_DIR=%~dp0"
if "%SCRIPT_DIR:~-1%"=="\" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
set "VOLMLIB_DIRECTORY="
if defined VOLMLIB_DIR call :check "%VOLMLIB_DIR%"
if not defined VOLMLIB_DIRECTORY call :search "%SCRIPT_DIR%"
if not defined VOLMLIB_DIRECTORY (
echo Could not find a VolmLib checkout. 1>&2
echo Expected a VolmLib directory beside this project root, or VOLMLIB_DIR pointing at one. 1>&2
exit /b 1
)
echo ==^> Publishing VolmLib from %VOLMLIB_DIRECTORY%
pushd "%VOLMLIB_DIRECTORY%"
if errorlevel 1 exit /b 1
call gradlew.bat publishToMavenLocal -Pgroup=com.github.VolmitSoftware -Pversion=master-SNAPSHOT -PvolmLibArtifactId=VolmLib %*
set "PUBLISH_RESULT=%ERRORLEVEL%"
popd
exit /b %PUBLISH_RESULT%
:check
if exist "%~1\settings.gradle" set "VOLMLIB_DIRECTORY=%~f1"
if exist "%~1\settings.gradle.kts" set "VOLMLIB_DIRECTORY=%~f1"
goto :eof
:search
set "CURRENT=%~f1"
:search_loop
if exist "%CURRENT%\VolmLib\settings.gradle" (
set "VOLMLIB_DIRECTORY=%CURRENT%\VolmLib"
goto :eof
)
if exist "%CURRENT%\VolmLib\settings.gradle.kts" (
set "VOLMLIB_DIRECTORY=%CURRENT%\VolmLib"
goto :eof
)
for %%I in ("%CURRENT%\..") do set "PARENT=%%~fI"
if /i "%PARENT%"=="%CURRENT%" goto :eof
set "CURRENT=%PARENT%"
goto :search_loop
-57
View File
@@ -1,57 +0,0 @@
#!/usr/bin/env bash
#
# Publishes VolmLib to the local Maven repository under the coordinate this project
# resolves by default: com.github.VolmitSoftware:VolmLib:master-SNAPSHOT.
#
# VolmLib is normally picked up as a composite build straight from source, so this is
# only needed when you build with -PuseLocalVolmLib=false, or when another tool has to
# resolve VolmLib from ~/.m2 rather than from the included build.
#
# The lookup mirrors settings.gradle: it walks up from this script looking for a
# VolmLib checkout sitting beside the project root. Set VOLMLIB_DIR to override.
# Any extra arguments are forwarded to gradle.
set -o pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
has_volmlib_settings() {
[[ -f "$1/settings.gradle" || -f "$1/settings.gradle.kts" ]]
}
resolve_volmlib_directory() {
if [[ -n "$VOLMLIB_DIR" ]] && has_volmlib_settings "$VOLMLIB_DIR"; then
printf '%s' "$VOLMLIB_DIR"
return 0
fi
local current="$SCRIPT_DIR"
while [[ -n "$current" ]]; do
if has_volmlib_settings "$current/VolmLib"; then
printf '%s' "$current/VolmLib"
return 0
fi
local parent
parent="$(dirname -- "$current")"
[[ "$parent" == "$current" ]] && break
current="$parent"
done
return 1
}
if ! VOLMLIB_DIRECTORY="$(resolve_volmlib_directory)"; then
printf 'Could not find a VolmLib checkout.\n' >&2
printf 'Expected a VolmLib directory beside this project root, or VOLMLIB_DIR pointing at one.\n' >&2
exit 1
fi
printf '==> Publishing VolmLib from %s\n' "$VOLMLIB_DIRECTORY"
cd "$VOLMLIB_DIRECTORY" || exit 1
./gradlew publishToMavenLocal \
-Pgroup=com.github.VolmitSoftware \
-Pversion=master-SNAPSHOT \
-PvolmLibArtifactId=VolmLib \
"$@"
+1 -1
View File
@@ -12,7 +12,7 @@ java {
} }
dependencies { dependencies {
api("com.github.VolmitSoftware.VolmLib:native-api:${providers.gradleProperty('volmLibCoordinate').get().tokenize(':').last()}") { api('com.github.VolmitSoftware.VolmLib:native-api:master-SNAPSHOT') {
transitive = false transitive = false
} }
testImplementation('junit:junit:4.13.2') testImplementation('junit:junit:4.13.2')