// ============================================================================ // Submodule Dependency Management // ============================================================================ // Manages CustomNPC+ and GeckoLib as git submodule dependencies. // // How it works: // - Both mods are git submodules built automatically alongside this mod // - Their dev JARs are referenced as devOnlyNonPublishable dependencies // - This means: available at COMPILE + RUNTIME, but NOT in the output JAR // - Submodule builds are skipped when their source hasn't changed // - When they do rebuild, old JARs are cleaned first to prevent duplicates // // Tasks: // ./gradlew initSubmodules — first-time: clone/update submodules // ./gradlew buildSubmodules — force rebuild both (always runs) // ./gradlew buildCNPC — force rebuild CustomNPC+ only // ./gradlew buildGeckoLib — force rebuild GeckoLib only // ============================================================================ def isWindows = System.getProperty('os.name').toLowerCase().contains('windows') // Resolve the gradlew executable for a submodule directory def gradlewFor = { File submoduleDir -> if (isWindows) { return new File(submoduleDir, 'gradlew.bat').absolutePath } else { return new File(submoduleDir, 'gradlew').absolutePath } } // Helper: find the dev JAR in a submodule's build output (resolved at execution time) def findDevJar = { String submoduleDir -> def libsDir = file("${submoduleDir}/build/libs") if (!libsDir.exists()) return [] return libsDir.listFiles()?.findAll { it.name.endsWith('-dev.jar') && !it.name.contains('-preshadow') } ?: [] } // ---- Submodule Init Task ---- tasks.register('initSubmodules', Exec) { group = 'submodules' description = 'Initialize and update all git submodules recursively' commandLine 'git', 'submodule', 'update', '--init', '--recursive' } // Helper: clean old dev JARs from a submodule's build/libs before rebuilding // Prevents stale JARs from different versions accumulating on the classpath def cleanDevJars = { String submoduleDir -> def libsDir = file("${submoduleDir}/build/libs") if (libsDir.exists()) { libsDir.listFiles()?.each { if (it.name.endsWith('.jar')) it.delete() } } } // Helper: touch a marker file after a successful build // We track this marker as the task output instead of build/libs (whose filenames // change with every commit due to version stamps, confusing Gradle's up-to-date check) def touchMarker = { String submoduleDir -> def marker = file("${submoduleDir}/build/libs/.build-marker") marker.parentFile.mkdirs() marker.text = "${System.currentTimeMillis()}" } // ---- Build CustomNPC+ ---- // Uses -Pnomixin to avoid embedding Mixin into the JAR (UniMixins provides it at runtime) // Skipped automatically when source hasn't changed. doFirst cleans stale JARs when it runs. tasks.register('buildCNPC', Exec) { group = 'submodules' description = 'Builds CustomNPC+ dev JAR from submodule source (no embedded Mixin)' workingDir file('CustomNPC-Plus') doFirst { cleanDevJars('CustomNPC-Plus') } doLast { touchMarker('CustomNPC-Plus') } def gradlew = gradlewFor(file('CustomNPC-Plus')) if (isWindows) { commandLine 'cmd', '/c', gradlew, 'build', '-Pnomixin', '-x', 'test' } else { commandLine gradlew, 'build', '-Pnomixin', '-x', 'test' } inputs.dir file('CustomNPC-Plus/src') inputs.files file('CustomNPC-Plus/build.gradle'), file('CustomNPC-Plus/dependencies.gradle') outputs.file file('CustomNPC-Plus/build/libs/.build-marker') } // ---- Build GeckoLib ---- // Skipped automatically when source hasn't changed. doFirst cleans stale JARs when it runs. tasks.register('buildGeckoLib', Exec) { group = 'submodules' description = 'Builds GeckoLib dev JAR from submodule source' workingDir file('GeckoLib') doFirst { cleanDevJars('GeckoLib') } doLast { touchMarker('GeckoLib') } def gradlew = gradlewFor(file('GeckoLib')) if (isWindows) { commandLine 'cmd', '/c', gradlew, 'build', '-x', 'test' } else { commandLine gradlew, 'build', '-x', 'test' } inputs.dir file('GeckoLib/src') inputs.files file('GeckoLib/build.gradle'), file('GeckoLib/dependencies.gradle') outputs.file file('GeckoLib/build/libs/.build-marker') } // ---- Convenience: build all submodules ---- tasks.register('buildSubmodules') { group = 'submodules' description = 'Builds all submodule dev JARs' dependsOn 'buildCNPC', 'buildGeckoLib' } // ---- Auto-build & dependency mode ---- // Both submodule dev JARs are always built and on the classpath. // In source injection mode, the dev JARs serve as FML mod containers (so FML // discovers each @Mod in its own JAR). Hotswap works via build/classes/java/main/. // In JAR mode (-PnoSourceInjection=true), the dev JARs are the sole source of submodule code. tasks.named('compileJava').configure { dependsOn 'buildCNPC', 'buildGeckoLib' } dependencies { devOnlyNonPublishable files({ findDevJar('CustomNPC-Plus') }) devOnlyNonPublishable files({ findDevJar('GeckoLib') }) }