mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-12-03 21:08:06 +00:00
This adds SPDX license headers to all source code files, following the REUSE[1] specification. This does not include any asset files (such as generated JSON files, or textures). While REUSE does support doing so with ".license" files, for now we define these licences using the .reuse/dep5 file. [1]: https://reuse.software/
175 lines
6.7 KiB
Kotlin
175 lines
6.7 KiB
Kotlin
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
//
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package cc.tweaked.gradle
|
|
|
|
import org.gradle.api.Project
|
|
import org.gradle.api.logging.Logging
|
|
import org.w3c.dom.Attr
|
|
import org.w3c.dom.Document
|
|
import org.w3c.dom.Node
|
|
import org.xml.sax.InputSource
|
|
import java.nio.file.Files
|
|
import java.nio.file.Path
|
|
import javax.xml.parsers.DocumentBuilderFactory
|
|
import javax.xml.transform.TransformerFactory
|
|
import javax.xml.transform.dom.DOMSource
|
|
import javax.xml.transform.stream.StreamResult
|
|
import javax.xml.xpath.XPathConstants
|
|
import javax.xml.xpath.XPathFactory
|
|
|
|
/**
|
|
* Patches up run configurations from ForgeGradle and Loom.
|
|
*
|
|
* Would be good to PR some (or all) of these changes upstream at some point.
|
|
*
|
|
* @see net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask
|
|
* @see net.minecraftforge.gradle.common.util.runs.IntellijRunGenerator
|
|
*/
|
|
internal class IdeaRunConfigurations(project: Project) {
|
|
private val rootProject = project.rootProject
|
|
|
|
private val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
|
private val xpath = XPathFactory.newInstance().newXPath()
|
|
private val writer = TransformerFactory.newInstance().newTransformer()
|
|
|
|
private val ideaDir = rootProject.file(".idea/")
|
|
private val buildDir: Lazy<String?> = lazy {
|
|
val ideaMisc = ideaDir.resolve("misc.xml")
|
|
|
|
try {
|
|
val doc = Files.newBufferedReader(ideaMisc.toPath()).use {
|
|
documentBuilder.parse(InputSource(it))
|
|
}
|
|
val node =
|
|
xpath.evaluate("//component[@name=\"ProjectRootManager\"]/output", doc, XPathConstants.NODE) as Node
|
|
val attr = node.attributes.getNamedItem("url") as Attr
|
|
attr.value.removePrefix("file://")
|
|
} catch (e: Exception) {
|
|
LOGGER.error("Failed to find root directory", e)
|
|
null
|
|
}
|
|
}
|
|
|
|
fun patch() = synchronized(LOCK) {
|
|
val runConfigDir = ideaDir.resolve("runConfigurations")
|
|
if (!runConfigDir.isDirectory) return
|
|
|
|
Files.list(runConfigDir.toPath()).use {
|
|
for (configuration in it) {
|
|
val filename = configuration.fileName.toString();
|
|
when {
|
|
filename.endsWith("_fabric.xml") -> patchFabric(configuration)
|
|
filename.startsWith("forge_") && filename.endsWith(".xml") -> patchForge(configuration)
|
|
else -> {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun patchFabric(path: Path) = withXml(path) {
|
|
setXml("//configuration", "folderName") { "Fabric" }
|
|
}
|
|
|
|
private fun patchForge(path: Path) = withXml(path) {
|
|
val configId = path.fileName.toString().removePrefix("forge_").removeSuffix(".xml")
|
|
val sourceSet = forgeConfigs[configId]
|
|
if (sourceSet == null) {
|
|
LOGGER.error("[{}] Cannot map run configuration to a known source set", path)
|
|
return@withXml
|
|
}
|
|
|
|
setXml("//configuration", "folderName") { "Forge" }
|
|
setXml("//configuration/module", "name") { "${rootProject.name}.forge.$sourceSet" }
|
|
|
|
if (buildDir.value == null) return@withXml
|
|
setXml("//configuration/envs/env[@name=\"MOD_CLASSES\"]", "value") { classpath ->
|
|
val classes = classpath!!.split(':')
|
|
val newClasses = mutableListOf<String>()
|
|
fun appendUnique(x: String) {
|
|
if (!newClasses.contains(x)) newClasses.add(x)
|
|
}
|
|
|
|
for (entry in classes) {
|
|
if (!entry.contains("/out/")) {
|
|
appendUnique(entry)
|
|
continue
|
|
}
|
|
|
|
val match = CLASSPATH_ENTRY.matchEntire(entry)
|
|
if (match != null) {
|
|
val modId = match.groups["modId"]!!.value
|
|
val proj = match.groups["proj"]!!.value
|
|
var component = match.groups["component"]!!.value
|
|
if (component == "production") component = "main"
|
|
|
|
appendUnique(forgeModEntry(modId, proj, component))
|
|
} else {
|
|
LOGGER.warn("[{}] Unknown classpath entry {}", path, entry)
|
|
appendUnique(entry)
|
|
}
|
|
}
|
|
|
|
// Ensure common code is on the classpath
|
|
for (proj in listOf("common", "common-api")) {
|
|
for (component in listOf("main", "client")) {
|
|
appendUnique(forgeModEntry("computercraft", proj, component))
|
|
}
|
|
}
|
|
|
|
if (newClasses.any { it.startsWith("cctest%%") }) {
|
|
appendUnique(forgeModEntry("cctest", "core", "testFixtures"))
|
|
appendUnique(forgeModEntry("cctest", "common", "testFixtures"))
|
|
appendUnique(forgeModEntry("cctest", "common", "testMod"))
|
|
}
|
|
|
|
newClasses.joinToString(":")
|
|
}
|
|
}
|
|
|
|
private fun forgeModEntry(mod: String, project: String, component: String) =
|
|
"$mod%%${buildDir.value}/production/${rootProject.name}.$project.$component"
|
|
|
|
private fun LocatedDocument.setXml(xpath: String, attribute: String, value: (String?) -> String) {
|
|
val node = this@IdeaRunConfigurations.xpath.evaluate(xpath, document, XPathConstants.NODE) as Node?
|
|
if (node == null) {
|
|
LOGGER.error("[{}] Cannot find {}", path.fileName, xpath)
|
|
return
|
|
}
|
|
|
|
val attr = node.attributes.getNamedItem(attribute) as Attr? ?: document.createAttribute(attribute)
|
|
val oldValue = attr.value
|
|
attr.value = value(attr.value)
|
|
node.attributes.setNamedItem(attr)
|
|
|
|
if (oldValue != attr.value) {
|
|
LOGGER.info("[{}] Setting {}@{}:\n Old: {}\n New: {}", path.fileName, xpath, attribute, oldValue, attr.value)
|
|
}
|
|
}
|
|
|
|
private fun withXml(path: Path, run: LocatedDocument.() -> Unit) {
|
|
val doc = Files.newBufferedReader(path).use { documentBuilder.parse(InputSource(it)) }
|
|
run(LocatedDocument(path, doc))
|
|
Files.newBufferedWriter(path).use { writer.transform(DOMSource(doc), StreamResult(it)) }
|
|
}
|
|
|
|
private class LocatedDocument(val path: Path, val document: Document)
|
|
|
|
companion object {
|
|
private val LOGGER = Logging.getLogger(IdeaRunConfigurations::class.java)
|
|
private val LOCK = Any()
|
|
|
|
private val CLASSPATH_ENTRY =
|
|
Regex("(?<modId>[a-z]+)%%\\\$PROJECT_DIR\\\$/projects/(?<proj>[a-z-]+)/out/(?<component>\\w+)/(?<type>[a-z]+)\$")
|
|
|
|
private val forgeConfigs = mapOf(
|
|
"runClient" to "client",
|
|
"runData" to "main",
|
|
"runGameTestServer" to "testMod",
|
|
"runServer" to "main",
|
|
"runTestClient" to "testMod",
|
|
)
|
|
}
|
|
}
|