diff --git a/.gitignore b/.gitignore index cab7d48b0..29a8df20f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,10 +11,11 @@ gwt steam modules/* modules/**/build.gradle +modules/**/build.gradle.kts !modules/core -!modules/subprojects.gradle +!modules/subprojects.gradle.kts libs/* -!libs/subprojects.gradle +!libs/subprojects.gradle.kts ## GWT war/ diff --git a/build-logic/build.gradle b/build-logic/build.gradle deleted file mode 100644 index 11671d72d..000000000 --- a/build-logic/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id('groovy-gradle-plugin') -} - -repositories { - gradlePluginPortal() -} - -compileJava { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -dependencies { - implementation 'com.github.spotbugs.snom:spotbugs-gradle-plugin:5.2.3' - implementation 'ru.vyarus:gradle-animalsniffer-plugin:2.0.1' - implementation 'de.undercouch:gradle-download-task:5.7.0' - implementation 'gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:1.4.1' -} \ No newline at end of file diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 000000000..bf027c8e7 --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + `kotlin-dsl` +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation("com.google.code.gson:gson:2.10.1") + implementation("com.github.spotbugs.snom:spotbugs-gradle-plugin:5.2.3") + implementation("ru.vyarus:gradle-animalsniffer-plugin:2.0.1") + implementation("de.undercouch:gradle-download-task:5.7.0") + implementation("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:1.4.1") +} diff --git a/build-logic/settings.gradle b/build-logic/settings.gradle.kts similarity index 100% rename from build-logic/settings.gradle rename to build-logic/settings.gradle.kts diff --git a/build-logic/src/main/groovy/destination-sol-constants.gradle b/build-logic/src/main/groovy/destination-sol-constants.gradle deleted file mode 100644 index fbea5790f..000000000 --- a/build-logic/src/main/groovy/destination-sol-constants.gradle +++ /dev/null @@ -1,11 +0,0 @@ -ext { - appName = 'DestinationSol' - - engineVersion = '2.1.0' - gestaltVersion = '8.0.2-SNAPSHOT' - gdxVersion = '1.12.1' - // The LibGDX controllers library is versioned differently to the main LibGDX versions. - // See https://github.com/libgdx/gdx-controllers/wiki/Compatibility for compatible versions. - gdxControllersVersion = '2.2.3' - nuiVersion = '4.0.0-SNAPSHOT' -} \ No newline at end of file diff --git a/build-logic/src/main/groovy/destination-sol-ide.gradle b/build-logic/src/main/groovy/destination-sol-ide.gradle deleted file mode 100644 index 869e29732..000000000 --- a/build-logic/src/main/groovy/destination-sol-ide.gradle +++ /dev/null @@ -1,87 +0,0 @@ -plugins { - id 'eclipse' - id 'idea' - id 'org.jetbrains.gradle.plugin.idea-ext' -} - -import groovy.xml.XmlParser -import groovy.xml.XmlNodePrinter - -ext { - ideaPatchAnnotationProcessors = { XmlProvider provider -> - var profile = provider.asNode().component - .find { it.@name == 'CompilerConfiguration' } - .annotationProcessing.profile - for (Node profileNode : profile) { - def moduleName = profileNode.module.@name[0] - - String sourceOutputDir, sourceTestOutputDir - - if (moduleName.startsWith('DestinationSol.modules.')) { - // Modules output to a unified build directory. - sourceOutputDir = "../generated/sources/annotationProcessor/java/main" - sourceTestOutputDir = "../generated/sources/annotationProcessor/java/main" - } else { - // Normal libraries use separated build directories instead. - sourceOutputDir = "../../../generated/sources/annotationProcessor/java/main" - sourceTestOutputDir = "../../../generated/sources/annotationProcessor/java/test" - } - - if (profileNode.sourceOutputDir.size() == 0) { - profileNode.appendNode("sourceOutputDir", [name: sourceOutputDir]) - } else { - profileNode.sourceOutputDir.@name = sourceOutputDir - } - if (profileNode.sourceTestOutputDir.size() == 0) { - profileNode.appendNode("sourceTestOutputDir", [name: sourceTestOutputDir]) - } else { - profileNode.sourceTestOutputDir.@name = sourceTestOutputDir - } - if (profileNode.outputRelativeToContentRoot.size() == 0) { - profileNode.appendNode("outputRelativeToContentRoot", [value: false]) - } else { - profileNode.outputRelativeToContentRoot.@value = false - } - } - } - - ideaPatchEntryPoints = { XmlProvider provider -> - var component = provider.asNode().component - .find { it.@name == 'EntryPointsManager' } - if (component == null) { - return; - } - if (component.list.size() != 0) { - component.remove(component.list) - } - Node entryPointsList = component.appendNode("list", [size: 5]) - entryPointsList.appendNode("item", [index: 0, class: 'java.lang.String', itemvalue: 'org.destinationsol.game.attributes.RegisterUpdateSystem']) - entryPointsList.appendNode("item", [index: 1, class: 'java.lang.String', itemvalue: 'org.destinationsol.game.console.annotations.Command']) - entryPointsList.appendNode("item", [index: 2, class: 'java.lang.String', itemvalue: 'org.terasology.gestalt.assets.module.annotations.RegisterAssetFileFormat']) - entryPointsList.appendNode("item", [index: 3, class: 'java.lang.String', itemvalue: 'org.terasology.gestalt.assets.module.annotations.RegisterAssetType']) - entryPointsList.appendNode("item", [index: 4, class: 'java.lang.String', itemvalue: 'org.terasology.context.annotation.Service']) - - if (component.writeAnnotations.size() != 0) { - component.remove(component.writeAnnotations) - } - Node writeAnnotations = component.appendNode("writeAnnotations", []) - writeAnnotations.appendNode("writeAnnotation", [name: "javax.inject.Inject"]) - writeAnnotations.appendNode("writeAnnotation", [name: "org.destinationsol.common.In"]) - } - - ideaPatchCheckstyle = { XmlProvider provider -> - var checkstyleConfigs = provider.asNode().component - .find { it.@name == 'CheckStyle-IDEA' } - .option.find { it.@name == 'locations' }.list - var terasologyConfig = checkstyleConfigs.ConfigurationLocation.find { it.@id == 'terasology-style'} - if (terasologyConfig != null) { - println "$terasologyConfig" - checkstyleConfigs.ConfigurationLocation.remove(terasologyConfig) - } - terasologyConfig = checkstyleConfigs[0].appendNode('ConfigurationLocation', - [id: 'terasology-style', type: "LOCAL_FILE", scope: "All", description: "Terasology Style"]) - terasologyConfig.value = '$PROJECT_DIR$/config/metrics/checkstyle/checkstyle.xml' - var terasologyConfigProperties = terasologyConfig.appendNode("option", [name: "properties"]).appendNode("map", []) - terasologyConfigProperties.appendNode("entry", [key: "sameDir", value: '$PROJECT_DIR$/config/metrics/checkstyle']) - } -} \ No newline at end of file diff --git a/build-logic/src/main/groovy/destination-sol-java.gradle b/build-logic/src/main/groovy/destination-sol-java.gradle deleted file mode 100644 index 7c8f31a1b..000000000 --- a/build-logic/src/main/groovy/destination-sol-java.gradle +++ /dev/null @@ -1,33 +0,0 @@ -plugins { - id 'java' -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - - withJavadocJar() - withSourcesJar() -} - -compileJava { - options.encoding = 'UTF-8' - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - - // The release flag is required for Java 9+. - if (JavaVersion.current().isJava9Compatible()) { - options.release = 17 - } -} - -compileTestJava { - options.encoding = 'UTF-8' - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - - // The release flag is required for Java 9+. - if (JavaVersion.current().isJava9Compatible()) { - options.release = 17 - } -} \ No newline at end of file diff --git a/build-logic/src/main/groovy/destination-sol-jre.gradle b/build-logic/src/main/groovy/destination-sol-jre.gradle deleted file mode 100644 index bdf6bd01a..000000000 --- a/build-logic/src/main/groovy/destination-sol-jre.gradle +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2022 The Terasology Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -plugins { - id 'de.undercouch.download' -} - -// Uses Bellsoft Liberica JRE -def jreVersion = '11.0.19+7' -def jreUrlBase = "https://download.bell-sw.com/java/$jreVersion/bellsoft-jre$jreVersion" -def jreUrlFilenames = [ - lwjreLinux64 : 'linux-amd64.tar.gz', - lwjre : 'windows-i586.zip', - lwjreOSX : 'macos-amd64.zip', - lwjreOSXArm : 'macos-aarch64.zip' -] - -tasks.register('downloadJreAll') { - group 'Download' - description 'Downloads JRE for all platforms' -} - -jreUrlFilenames.each { os, file -> - def downloadTask = tasks.register("downloadJre$os") { - group 'Download' - description "Downloads JRE for $os" - - def packedJre = new File("$rootDir/jre/$jreVersion/$file") - def unpackedJre = base.distsDirectory.dir("app/$os").get().asFile - - doFirst { - download.run { - src "$jreUrlBase-$file" - dest packedJre - overwrite false - } - } - - doLast { - // Unpack the JRE - if (!unpackedJre.exists()) { - unpackedJre.mkdirs() - copy { - from(file.endsWith("zip") - ? zipTree(packedJre) - : tarTree(packedJre)) { - eachFile { fcd -> - fcd.relativePath = new RelativePath( - true, fcd.relativePath.segments.drop(1)) - } - includeEmptyDirs = false - } - into unpackedJre - } - } - } - } - - downloadJreAll.dependsOn downloadTask -} diff --git a/build-logic/src/main/groovy/destination-sol-module.gradle b/build-logic/src/main/groovy/destination-sol-module.gradle deleted file mode 100644 index 26e52fe62..000000000 --- a/build-logic/src/main/groovy/destination-sol-module.gradle +++ /dev/null @@ -1,76 +0,0 @@ -plugins { - id 'destination-sol-constants' - id 'gestalt-8-module' - id 'destination-sol-common' -} - -group = 'org.destinationsol.modules' - -dependencies { - api (rootProject.findProject("engine") ?: "org.destinationsol.engine:engine:$engineVersion") -} - -gestalt { - modulesPackage = "org.destinationsol.modules" - moduleMetadataFileName = "module.json" -} - -publishing { - publications { - "$project.name"(MavenPublication) { - pom { - url = "https://github.com/DestinationSol/${project.name}" - licenses { - license { - name = 'Apache-2.0' - url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' - distribution = 'repo' - } - } - issueManagement { - system = 'GitHub' - url = "https://github.com/DestinationSol/${project.name}/issues" - } - scm { - connection = "scm:git:https://github.com/DestinationSol/${project.name}.git" - developerConnection = "scm:git:ssh://github.com/DestinationSol/${project.name}.git" - url = "scm:git:https://github.com/DestinationSol/${project.name}.git" - } - } - } - } -} - -// Generate the module directory structure if missing -tasks.register('createSkeleton') { - mkdir('assets') - mkdir('assets/music') - mkdir('assets/sounds') - mkdir('assets/textures') - mkdir('assets/configs') - mkdir('assets/asteroids') - mkdir('assets/schemas') - mkdir('assets/prefabs') - mkdir('assets/ui') - mkdir('assets/skins') - mkdir('overrides') - mkdir('deltas') - mkdir('src/main/java') - mkdir('src/test/java') -} - -idea { - module { - inheritOutputDirs = false - outputDir = file('build/classes') - testOutputDir = file('build/testClasses') - downloadSources = true - } -} - -// For Eclipse just make sure the classpath is right -eclipse { - classpath { - defaultOutputDir = file('build/classes') - } -} \ No newline at end of file diff --git a/build-logic/src/main/groovy/destination-sol-repositories.gradle b/build-logic/src/main/groovy/destination-sol-repositories.gradle deleted file mode 100644 index 416ef11c5..000000000 --- a/build-logic/src/main/groovy/destination-sol-repositories.gradle +++ /dev/null @@ -1,45 +0,0 @@ -repositories { - mavenCentral() { - content { - // Terasology's libraries/modules aren't on maven central, so don't bother looking there. - excludeGroupByRegex('org\\.terasology(\\..+)?') - // Same for Destination Sol. - excludeGroupByRegex('org\\.destinationsol(\\..+)?') - } - } - - // Repos for LibGDX - maven { - url "https://oss.sonatype.org/content/repositories/snapshots/" - content { - includeGroupByRegex('com\\.badlogicgames.gdx(\\..+)?') - } - } - maven { - url "https://oss.sonatype.org/content/repositories/releases/" - content { - includeGroupByRegex('com\\.badlogicgames.gdx(\\..+)?') - } - } - - // everit-org JSON schema dependency - maven { - url "https://jitpack.io" - content { - includeModule('com.github.everit-org.json-schema', 'org.everit.json.schema') - } - } - - // Terasology Artifactory for any shared libs - maven { - url "https://artifactory.terasology.io/artifactory/virtual-repo-live" - content { - includeGroupByRegex('org\\.terasology(\\..+)?') - includeGroupByRegex('org\\.destinationsol(\\..+)?') - // A copy of jpastebin is hosted here - includeModule('brianbb', 'jpastebin') - } - } - - google() -} \ No newline at end of file diff --git a/build-logic/src/main/groovy/gestalt-8-module.gradle b/build-logic/src/main/groovy/gestalt-8-module.gradle deleted file mode 100644 index 121716a31..000000000 --- a/build-logic/src/main/groovy/gestalt-8-module.gradle +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - id 'gestalt-module' -} - -compileJava { - // Specify the module's assets as pre-requisites for compilation, so that they force re-compilation when they change. - inputs.files sourceSets.main.resources.srcDirs - // Asset lists are cached by gestalt-di's annotation processors, which run just before compilation begins. - // Without these manifests, or with outdated manifests, the game won't know what assets are present. - options.compilerArgs = ["-Aresource=${sourceSets.main.resources.srcDirs.join(File.pathSeparator)}"] -} -compileTestJava { - // See above comments. - inputs.files sourceSets.test.resources.srcDirs - options.compilerArgs = ["-Aresource=${sourceSets.test.resources.srcDirs.join(File.pathSeparator)}"] -} - -dependencies { - annotationProcessor "org.terasology.gestalt:gestalt-inject-java:$gestaltVersion" -} \ No newline at end of file diff --git a/build-logic/src/main/groovy/gestalt-module.gradle b/build-logic/src/main/groovy/gestalt-module.gradle deleted file mode 100644 index 374103a83..000000000 --- a/build-logic/src/main/groovy/gestalt-module.gradle +++ /dev/null @@ -1,112 +0,0 @@ -plugins { - //noinspection JavaPluginLanguageLevel - id 'java-library' - id 'gestalt-repositories' - id 'terasology-publish-common' -} - -interface GestaltExtension { - Property getModulesPackage(); - Property getModuleMetadataFileName(); -} - -GestaltExtension gestaltExtension = extensions.create('gestalt', GestaltExtension); - -gestalt { - modulesPackage = "org.terasology.gestalt.modules" - moduleMetadataFileName = "module.txt" -} - -// Change the output dir of each module -sourceSets { - main { - java.destinationDirectory.set(new File("$buildDir/classes")) - } - test { - java.destinationDirectory.set(new File("$buildDir/testClasses")) - } -} - -jar { - from ('module.json') { - into '' - } - from ('assets') { - into 'assets' - } - from ('overrides') { - into 'overrides' - } - from ('deltas') { - into 'deltas' - } -} - -configurations { - gestaltModule - api.extendsFrom(gestaltModule) -} - -import groovy.json.JsonSlurper -afterEvaluate { - File moduleMetadataFile = file(gestaltExtension.moduleMetadataFileName.get()) - if (!moduleMetadataFile.exists()) { - println "${gestaltExtension.moduleMetadataFileName.get()} does not exist!" - throw new GradleException("Failed to find ${gestaltExtension.moduleMetadataFileName.get()} for module " + project.name) - } - - ext { - moduleMetadata = new JsonSlurper().parse(moduleMetadataFile) - modulesRoot = properties.modulesRoot ?: "$rootProject/modules" - } - - version = ext.moduleMetadata.version - description = ext.moduleMetadata.description - - publishing { - publications { - "$project.name"(MavenPublication) { - pom { - name = "$project.name" - description = "$project.description" - if (moduleMetadata.author) { - developers { - for (String developerId : moduleMetadata.author.split(',')) { - developer { - id = developerId.trim() - } - } - } - } - } - } - } - } - - dependencies { - moduleMetadata.dependencies.each { dependency -> - if (!dependency.optional) { - //noinspection DependencyNotationArgument - gestaltModule (group: gestaltExtension.modulesPackage.get(), name: dependency.id) { - if (dependency.minVersion && !dependency.maxVersion) { - version { - strictly "[${dependency.minVersion},)" - } - } - - if (!dependency.minVersion && dependency.maxVersion) { - version { - strictly "(,${dependency.maxVersion}[" - } - } - - if (dependency.minVersion && dependency.maxVersion) { - version { - strictly "[${dependency.minVersion}, ${dependency.maxVersion}[" - } - } - } - } - } - } -} \ No newline at end of file diff --git a/build-logic/src/main/groovy/terasology-metrics.gradle b/build-logic/src/main/groovy/terasology-metrics.gradle deleted file mode 100644 index 72fba40ad..000000000 --- a/build-logic/src/main/groovy/terasology-metrics.gradle +++ /dev/null @@ -1,108 +0,0 @@ -import com.github.spotbugs.snom.SpotBugsTask - -plugins { - //noinspection JavaPluginLanguageLevel - id 'java' - id 'project-report' - id 'checkstyle' - id 'pmd' - id 'com.github.spotbugs' - id 'jacoco' - id 'ru.vyarus.animalsniffer' -} - -configurations { - codeMetrics -} - -dependencies { - // Config for our code analytics lives in a centralized repo: https://github.com/MovingBlocks/TeraConfig - codeMetrics(group: 'org.terasology.config', name: 'codemetrics', version: '2.2.0', ext: 'zip') - - pmd("net.sourceforge.pmd:pmd-ant:7.26.0") - pmd("net.sourceforge.pmd:pmd-core:7.26.0") - pmd("net.sourceforge.pmd:pmd-java:7.26.0") - - signature('com.toasttab.android:gummy-bears-api-24:0.15.0:coreLib2@signature') -} - -animalsniffer { - // java.nio.* APIs can be desugared by D8. java.io.File.toPath() also needs to be excluded. - ignore = ["java.nio.file.*", "java.io.File"] -} - -jacoco { - toolVersion = "0.8.15" -} - -jacocoTestReport { - dependsOn test // Despite doc saying this should be automatic we need to explicitly add it anyway :-( - reports { - // We only use the .exec report for display in Jenkins and such. More could be enabled if desired. - xml.required = false - csv.required = false - html.required = false - } -} - -checkstyle { - ignoreFailures = true - configFile = new File(rootDir, 'config/metrics/checkstyle/checkstyle.xml') - toolVersion = "10.2" - configDirectory.set(configFile.parentFile) - configProperties.samedir = checkstyle.configFile.parentFile -} - -pmd { - ignoreFailures = true - ruleSetFiles = files("$rootDir/config/metrics/pmd/pmd.xml") - // By default, gradle uses both ruleset file AND the rulesets. Override the ruleSets to use only those from the file - ruleSets = [] -} - -spotbugs { - toolVersion = '4.8.1' - ignoreFailures = true - excludeFilter = new File(rootDir, "config/metrics/findbugs/findbugs-exclude.xml") -} -tasks.spotbugsMain { - reports.create('xml') { - enabled = true - outputLocation = file("$buildDir/reports/spotbugs/main/spotbugs.xml") - } -} - -var extractMetricsConfig = rootProject.tasks.findByName("extractMetricsConfig") -if (extractMetricsConfig != null) { - // Task already present, so no need to re-register. -} else { - extractMetricsConfig = rootProject.tasks.register("extractMetricsConfig", Copy) { - description = "Extracts our configuration files from the zip we fetched as a dependency" - from { - configurations.codeMetrics.collect { - zipTree(it) - } - } - into "$rootDir/config/metrics" - } -} - -spotbugsMain.dependsOn extractMetricsConfig -pmdMain.dependsOn extractMetricsConfig - -tasks.withType(Checkstyle).configureEach { - group = 'Reporting' - dependsOn extractMetricsConfig -} - -tasks.withType(Pmd).configureEach { - dependsOn extractMetricsConfig - group = 'Reporting' -} - -tasks.withType(SpotBugsTask).configureEach { - dependsOn extractMetricsConfig - group = 'Reporting' -} - -check.dependsOn extractMetricsConfig \ No newline at end of file diff --git a/build-logic/src/main/groovy/terasology-publish-common.gradle b/build-logic/src/main/groovy/terasology-publish-common.gradle deleted file mode 100644 index 2c0c8c1eb..000000000 --- a/build-logic/src/main/groovy/terasology-publish-common.gradle +++ /dev/null @@ -1,53 +0,0 @@ -plugins { - id 'maven-publish' -} - -publishing { - publications { - "$project.name"(MavenPublication) { - from components.java - pom { - name = "$project.name" - } - } - } - - repositories { - maven { - name = 'TerasologyOrg' - if (rootProject.hasProperty("publishRepo")) { - // This first option is good for local testing, you can set a full explicit target repo in gradle.properties - url = "https://artifactory.terasology.io/artifactory/$publishRepo" - - logger.info("Changing PUBLISH repoKey set via Gradle property to {}", publishRepo) - } else { - // Support override from the environment to use a different target publish org - String deducedPublishRepo = System.getenv()["PUBLISH_ORG"] - if (deducedPublishRepo == null || deducedPublishRepo == "") { - // If not then default - deducedPublishRepo = "libs" - } - - // Base final publish repo on whether we're building a snapshot or a release - if (project.version.endsWith('SNAPSHOT')) { - deducedPublishRepo += "-snapshot-local" - } else { - deducedPublishRepo += "-release-local" - } - - logger.info("The final deduced publish repo is {}", deducedPublishRepo) - url = "https://artifactory.terasology.io/artifactory/$deducedPublishRepo" - } - - if (rootProject.hasProperty("mavenUser") && rootProject.hasProperty("mavenPass")) { - credentials { - username = "$mavenUser" - password = "$mavenPass" - } - authentication { - basic(BasicAuthentication) - } - } - } - } -} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/GestaltExtension.kt b/build-logic/src/main/kotlin/GestaltExtension.kt new file mode 100644 index 000000000..e2312f4de --- /dev/null +++ b/build-logic/src/main/kotlin/GestaltExtension.kt @@ -0,0 +1,6 @@ +import org.gradle.api.provider.Property + +interface GestaltExtension { + val modulesPackage: Property + val moduleMetadataFileName: Property +} diff --git a/build-logic/src/main/groovy/destination-sol-common.gradle b/build-logic/src/main/kotlin/destination-sol-common.gradle.kts similarity index 59% rename from build-logic/src/main/groovy/destination-sol-common.gradle rename to build-logic/src/main/kotlin/destination-sol-common.gradle.kts index c694dfee2..c02c11632 100644 --- a/build-logic/src/main/groovy/destination-sol-common.gradle +++ b/build-logic/src/main/kotlin/destination-sol-common.gradle.kts @@ -1,18 +1,18 @@ plugins { - id 'destination-sol-constants' - id 'gestalt-repositories' - id 'destination-sol-repositories' - id 'destination-sol-java' - id 'terasology-metrics' - id 'destination-sol-ide' + id("destination-sol-constants") + id("gestalt-repositories") + id("destination-sol-repositories") + id("destination-sol-java") + id("terasology-metrics") + id("destination-sol-ide") } // TODO: Temporary until javadoc has been fixed for Java 8 everywhere -javadoc { - failOnError = false +tasks.javadoc { + isFailOnError = false } -test { +tasks.test { // ignoreFailures: Specifies whether the build should break when the verifications performed by this task fail. ignoreFailures = true @@ -20,5 +20,5 @@ test { testLogging.showStandardStreams = true // Arguments to include while running tests - jvmArgs '-Xms512m', '-Xmx1024m' + jvmArgs("-Xms512m", "-Xmx1024m") } diff --git a/build-logic/src/main/kotlin/destination-sol-constants.gradle.kts b/build-logic/src/main/kotlin/destination-sol-constants.gradle.kts new file mode 100644 index 000000000..2761c4f7c --- /dev/null +++ b/build-logic/src/main/kotlin/destination-sol-constants.gradle.kts @@ -0,0 +1,9 @@ +extra["appName"] = "DestinationSol" + +extra["engineVersion"] = "2.1.0" +extra["gestaltVersion"] = "8.0.2-SNAPSHOT" +extra["gdxVersion"] = "1.12.1" +// The LibGDX controllers library is versioned differently to the main LibGDX versions. +// See https://github.com/libgdx/gdx-controllers/wiki/Compatibility for compatible versions. +extra["gdxControllersVersion"] = "2.2.3" +extra["nuiVersion"] = "4.0.0-SNAPSHOT" diff --git a/build-logic/src/main/kotlin/destination-sol-ide.gradle.kts b/build-logic/src/main/kotlin/destination-sol-ide.gradle.kts new file mode 100644 index 000000000..3a377e565 --- /dev/null +++ b/build-logic/src/main/kotlin/destination-sol-ide.gradle.kts @@ -0,0 +1,97 @@ +import groovy.util.Node +import groovy.util.NodeList +import groovy.xml.XmlNodePrinter +import groovy.xml.XmlParser +import org.gradle.api.XmlProvider + +plugins { + eclipse + idea + id("org.jetbrains.gradle.plugin.idea-ext") +} + +@Suppress("UNCHECKED_CAST") +fun Node.child(name: String): List = (get(name) as? NodeList)?.filterIsInstance() ?: emptyList() + +fun Node.attr(name: String): Any? = attribute(name) + +val ideaPatchAnnotationProcessors = Action { + val profile = asNode().child("component") + .first { it.attr("name") == "CompilerConfiguration" } + .child("annotationProcessing").first() + .child("profile") + for (profileNode in profile) { + val moduleName = profileNode.child("module").first().attr("name") as String + + val sourceOutputDir: String + val sourceTestOutputDir: String + + if (moduleName.startsWith("DestinationSol.modules.")) { + // Modules output to a unified build directory. + sourceOutputDir = "../generated/sources/annotationProcessor/java/main" + sourceTestOutputDir = "../generated/sources/annotationProcessor/java/main" + } else { + // Normal libraries use separated build directories instead. + sourceOutputDir = "../../../generated/sources/annotationProcessor/java/main" + sourceTestOutputDir = "../../../generated/sources/annotationProcessor/java/test" + } + + if (profileNode.child("sourceOutputDir").isEmpty()) { + profileNode.appendNode("sourceOutputDir", mapOf("name" to sourceOutputDir)) + } else { + profileNode.child("sourceOutputDir").first().attributes()["name"] = sourceOutputDir + } + if (profileNode.child("sourceTestOutputDir").isEmpty()) { + profileNode.appendNode("sourceTestOutputDir", mapOf("name" to sourceTestOutputDir)) + } else { + profileNode.child("sourceTestOutputDir").first().attributes()["name"] = sourceTestOutputDir + } + if (profileNode.child("outputRelativeToContentRoot").isEmpty()) { + profileNode.appendNode("outputRelativeToContentRoot", mapOf("value" to false)) + } else { + profileNode.child("outputRelativeToContentRoot").first().attributes()["value"] = false + } + } +} + +val ideaPatchEntryPoints = Action { + val component = asNode().child("component") + .firstOrNull { it.attr("name") == "EntryPointsManager" } ?: return@Action + + component.child("list").forEach { component.remove(it) } + val entryPointsList = component.appendNode("list", mapOf("size" to 5)) + entryPointsList.appendNode("item", mapOf("index" to 0, "class" to "java.lang.String", "itemvalue" to "org.destinationsol.game.attributes.RegisterUpdateSystem")) + entryPointsList.appendNode("item", mapOf("index" to 1, "class" to "java.lang.String", "itemvalue" to "org.destinationsol.game.console.annotations.Command")) + entryPointsList.appendNode("item", mapOf("index" to 2, "class" to "java.lang.String", "itemvalue" to "org.terasology.gestalt.assets.module.annotations.RegisterAssetFileFormat")) + entryPointsList.appendNode("item", mapOf("index" to 3, "class" to "java.lang.String", "itemvalue" to "org.terasology.gestalt.assets.module.annotations.RegisterAssetType")) + entryPointsList.appendNode("item", mapOf("index" to 4, "class" to "java.lang.String", "itemvalue" to "org.terasology.context.annotation.Service")) + + component.child("writeAnnotations").forEach { component.remove(it) } + val writeAnnotations = component.appendNode("writeAnnotations", mapOf()) + writeAnnotations.appendNode("writeAnnotation", mapOf("name" to "javax.inject.Inject")) + writeAnnotations.appendNode("writeAnnotation", mapOf("name" to "org.destinationsol.common.In")) +} + +val ideaPatchCheckstyle = Action { + val checkstyleConfigs = asNode().child("component") + .first { it.attr("name") == "CheckStyle-IDEA" } + .child("option").first { it.attr("name") == "locations" } + .child("list").first() + var terasologyConfig = checkstyleConfigs.child("ConfigurationLocation") + .firstOrNull { it.attr("id") == "terasology-style" } + if (terasologyConfig != null) { + println(terasologyConfig) + checkstyleConfigs.remove(terasologyConfig) + } + terasologyConfig = checkstyleConfigs.appendNode( + "ConfigurationLocation", + mapOf("id" to "terasology-style", "type" to "LOCAL_FILE", "scope" to "All", "description" to "Terasology Style") + ) + terasologyConfig.setValue("\$PROJECT_DIR\$/config/metrics/checkstyle/checkstyle.xml") + val terasologyConfigProperties = terasologyConfig.appendNode("option", mapOf("name" to "properties")).appendNode("map", mapOf()) + terasologyConfigProperties.appendNode("entry", mapOf("key" to "sameDir", "value" to "\$PROJECT_DIR\$/config/metrics/checkstyle")) +} + +extra["ideaPatchAnnotationProcessors"] = ideaPatchAnnotationProcessors +extra["ideaPatchEntryPoints"] = ideaPatchEntryPoints +extra["ideaPatchCheckstyle"] = ideaPatchCheckstyle diff --git a/build-logic/src/main/kotlin/destination-sol-java.gradle.kts b/build-logic/src/main/kotlin/destination-sol-java.gradle.kts new file mode 100644 index 000000000..2e8c60ccc --- /dev/null +++ b/build-logic/src/main/kotlin/destination-sol-java.gradle.kts @@ -0,0 +1,33 @@ +plugins { + java +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + + withJavadocJar() + withSourcesJar() +} + +tasks.compileJava { + options.encoding = "UTF-8" + sourceCompatibility = JavaVersion.VERSION_17.toString() + targetCompatibility = JavaVersion.VERSION_17.toString() + + // The release flag is required for Java 9+. + if (JavaVersion.current().isJava9Compatible) { + options.release = 17 + } +} + +tasks.compileTestJava { + options.encoding = "UTF-8" + sourceCompatibility = JavaVersion.VERSION_17.toString() + targetCompatibility = JavaVersion.VERSION_17.toString() + + // The release flag is required for Java 9+. + if (JavaVersion.current().isJava9Compatible) { + options.release = 17 + } +} diff --git a/build-logic/src/main/kotlin/destination-sol-jre.gradle.kts b/build-logic/src/main/kotlin/destination-sol-jre.gradle.kts new file mode 100644 index 000000000..3fa39a303 --- /dev/null +++ b/build-logic/src/main/kotlin/destination-sol-jre.gradle.kts @@ -0,0 +1,82 @@ +/* + * Copyright 2022 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import de.undercouch.gradle.tasks.download.DownloadAction +import org.gradle.api.plugins.BasePluginExtension + +plugins { + id("de.undercouch.download") +} + +// The `base` plugin (bringing BasePluginExtension) is applied transitively by whatever +// project consumes this script (e.g. via destination-sol-java), not by this script itself, +// so the type-safe `base { }` accessor isn't available here - look it up explicitly instead. +val distsDirectory = the().distsDirectory + +// Uses Bellsoft Liberica JRE. Must stay on a Java 17+ build - the engine compiles with +// options.release = 17 (see destination-sol-java.gradle.kts), and a Java 11 runtime can't +// load Java 17 class files (UnsupportedClassVersionError). +val jreVersion = "17.0.12+10" +val jreUrlBase = "https://download.bell-sw.com/java/$jreVersion/bellsoft-jre$jreVersion" +val jreUrlFilenames = mapOf( + "lwjreLinux64" to "linux-amd64.tar.gz", + // 32-bit Windows dropped from JDK 12+ builds; windows-amd64 is the closest equivalent to + // the old windows-i586 key. + "lwjre" to "windows-amd64.zip", + "lwjreOSX" to "macos-amd64.zip", + "lwjreOSXArm" to "macos-aarch64.zip" +) + +val downloadJreAll by tasks.registering { + group = "Download" + description = "Downloads JRE for all platforms" +} + +jreUrlFilenames.forEach { (os, file) -> + val downloadTask = tasks.register("downloadJre$os") { + group = "Download" + description = "Downloads JRE for $os" + + val packedJre = File("$rootDir/jre/$jreVersion/$file") + val unpackedJre = distsDirectory.dir("app/$os").get().asFile + + doFirst { + DownloadAction(project).apply { + src("$jreUrlBase-$file") + dest(packedJre) + overwrite(false) + }.execute() + } + + doLast { + // Unpack the JRE + if (!unpackedJre.exists()) { + unpackedJre.mkdirs() + copy { + from(if (file.endsWith("zip")) zipTree(packedJre) else tarTree(packedJre)) { + eachFile { + relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray()) + } + includeEmptyDirs = false + } + into(unpackedJre) + } + } + } + } + + downloadJreAll.configure { dependsOn(downloadTask) } +} diff --git a/build-logic/src/main/kotlin/destination-sol-module.gradle.kts b/build-logic/src/main/kotlin/destination-sol-module.gradle.kts new file mode 100644 index 000000000..6f792785f --- /dev/null +++ b/build-logic/src/main/kotlin/destination-sol-module.gradle.kts @@ -0,0 +1,87 @@ +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.plugins.ide.eclipse.model.EclipseModel +import org.gradle.plugins.ide.idea.model.IdeaModel + +plugins { + id("destination-sol-constants") + id("gestalt-8-module") + id("destination-sol-common") +} + +group = "org.destinationsol.modules" + +val engineVersion = extra["engineVersion"] as String + +dependencies { + "api"(rootProject.findProject("engine") ?: "org.destinationsol.engine:engine:$engineVersion") +} + +configure { + modulesPackage.set("org.destinationsol.modules") + moduleMetadataFileName.set("module.json") +} + +publishing { + publications { + // maybeCreate: gestalt-module and terasology-publish-common also configure the + // project-name-keyed publication, in whichever order their plugins get applied. + maybeCreate(project.name, MavenPublication::class.java).pom { + url.set("https://github.com/DestinationSol/${project.name}") + licenses { + license { + name.set("Apache-2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + distribution.set("repo") + } + } + issueManagement { + system.set("GitHub") + url.set("https://github.com/DestinationSol/${project.name}/issues") + } + scm { + connection.set("scm:git:https://github.com/DestinationSol/${project.name}.git") + developerConnection.set("scm:git:ssh://github.com/DestinationSol/${project.name}.git") + url.set("scm:git:https://github.com/DestinationSol/${project.name}.git") + } + } + } +} + +// Generate the module directory structure if missing +tasks.register("createSkeleton") { + doLast { + mkdir("assets") + mkdir("assets/music") + mkdir("assets/sounds") + mkdir("assets/textures") + mkdir("assets/configs") + mkdir("assets/asteroids") + mkdir("assets/schemas") + mkdir("assets/prefabs") + mkdir("assets/ui") + mkdir("assets/skins") + mkdir("overrides") + mkdir("deltas") + mkdir("src/main/java") + mkdir("src/test/java") + } +} + +// idea/eclipse are applied transitively (via destination-sol-common -> destination-sol-ide), +// not by this script's own plugins{} block, so the type-safe idea{}/eclipse{} accessors +// aren't available here - look the extensions up explicitly instead. +configure { + module { + inheritOutputDirs = false + outputDir = file("build/classes") + testOutputDir = file("build/testClasses") + isDownloadSources = true + } +} + +// For Eclipse just make sure the classpath is right +configure { + classpath { + defaultOutputDir = file("build/classes") + } +} diff --git a/build-logic/src/main/kotlin/destination-sol-repositories.gradle.kts b/build-logic/src/main/kotlin/destination-sol-repositories.gradle.kts new file mode 100644 index 000000000..64a4800d0 --- /dev/null +++ b/build-logic/src/main/kotlin/destination-sol-repositories.gradle.kts @@ -0,0 +1,45 @@ +repositories { + mavenCentral { + content { + // Terasology's libraries/modules aren't on maven central, so don't bother looking there. + excludeGroupByRegex("""org\.terasology(\..+)?""") + // Same for Destination Sol. + excludeGroupByRegex("""org\.destinationsol(\..+)?""") + } + } + + // Repos for LibGDX + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots/") + content { + includeGroupByRegex("""com\.badlogicgames.gdx(\..+)?""") + } + } + maven { + url = uri("https://oss.sonatype.org/content/repositories/releases/") + content { + includeGroupByRegex("""com\.badlogicgames.gdx(\..+)?""") + } + } + + // everit-org JSON schema dependency + maven { + url = uri("https://jitpack.io") + content { + includeModule("com.github.everit-org.json-schema", "org.everit.json.schema") + } + } + + // Terasology Artifactory for any shared libs + maven { + url = uri("https://artifactory.terasology.io/artifactory/virtual-repo-live") + content { + includeGroupByRegex("""org\.terasology(\..+)?""") + includeGroupByRegex("""org\.destinationsol(\..+)?""") + // A copy of jpastebin is hosted here + includeModule("brianbb", "jpastebin") + } + } + + google() +} diff --git a/build-logic/src/main/kotlin/gestalt-8-module.gradle.kts b/build-logic/src/main/kotlin/gestalt-8-module.gradle.kts new file mode 100644 index 000000000..f321cb073 --- /dev/null +++ b/build-logic/src/main/kotlin/gestalt-8-module.gradle.kts @@ -0,0 +1,22 @@ +plugins { + id("gestalt-module") +} + +val gestaltVersion = extra["gestaltVersion"] as String + +tasks.compileJava { + // Specify the module's assets as pre-requisites for compilation, so that they force re-compilation when they change. + inputs.files(sourceSets.main.get().resources.srcDirs) + // Asset lists are cached by gestalt-di's annotation processors, which run just before compilation begins. + // Without these manifests, or with outdated manifests, the game won't know what assets are present. + options.compilerArgs = listOf("-Aresource=${sourceSets.main.get().resources.srcDirs.joinToString(File.pathSeparator)}") +} +tasks.compileTestJava { + // See above comments. + inputs.files(sourceSets.test.get().resources.srcDirs) + options.compilerArgs = listOf("-Aresource=${sourceSets.test.get().resources.srcDirs.joinToString(File.pathSeparator)}") +} + +dependencies { + "annotationProcessor"("org.terasology.gestalt:gestalt-inject-java:$gestaltVersion") +} diff --git a/build-logic/src/main/kotlin/gestalt-module.gradle.kts b/build-logic/src/main/kotlin/gestalt-module.gradle.kts new file mode 100644 index 000000000..7fea689c8 --- /dev/null +++ b/build-logic/src/main/kotlin/gestalt-module.gradle.kts @@ -0,0 +1,108 @@ +import com.google.gson.JsonParser +import org.gradle.api.publish.maven.MavenPublication + +plugins { + `java-library` + id("gestalt-repositories") + id("terasology-publish-common") +} + +val gestaltExtension = extensions.create("gestalt") + +gestaltExtension.modulesPackage.set("org.terasology.gestalt.modules") +gestaltExtension.moduleMetadataFileName.set("module.txt") + +// Change the output dir of each module +sourceSets { + main { + java.destinationDirectory.set(File("$buildDir/classes")) + } + test { + java.destinationDirectory.set(File("$buildDir/testClasses")) + } +} + +tasks.jar { + from("module.json") { + into("") + } + from("assets") { + into("assets") + } + from("overrides") { + into("overrides") + } + from("deltas") { + into("deltas") + } +} + +val gestaltModule: Configuration by configurations.creating +configurations.named("api") { + extendsFrom(gestaltModule) +} + +afterEvaluate { + val moduleMetadataFile = file(gestaltExtension.moduleMetadataFileName.get()) + if (!moduleMetadataFile.exists()) { + println("${gestaltExtension.moduleMetadataFileName.get()} does not exist!") + throw GradleException("Failed to find ${gestaltExtension.moduleMetadataFileName.get()} for module ${project.name}") + } + + val moduleMetadata = moduleMetadataFile.reader().use { JsonParser.parseReader(it) }.asJsonObject + extra["moduleMetadata"] = moduleMetadata + extra["modulesRoot"] = if (project.hasProperty("modulesRoot")) property("modulesRoot") else "$rootProject/modules" + + moduleMetadata.get("version")?.asString?.let { version = it } + moduleMetadata.get("description")?.asString?.let { description = it } + + publishing { + publications { + // Both this plugin and destination-sol-module configure the same, project-name-keyed + // publication; maybeCreate lets either one run first without the other clobbering it. + maybeCreate(project.name, MavenPublication::class.java).pom { + name.set(project.name) + description.set(project.description) + val author = moduleMetadata.get("author")?.asString + if (!author.isNullOrEmpty()) { + developers { + for (developerId in author.split(",")) { + developer { + id.set(developerId.trim()) + } + } + } + } + } + } + } + + dependencies { + moduleMetadata.getAsJsonArray("dependencies")?.forEach { element -> + val dependency = element.asJsonObject + val optional = dependency.get("optional")?.asBoolean ?: false + if (!optional) { + val depId = dependency.get("id").asString + val minVersion = dependency.get("minVersion")?.asString + val maxVersion = dependency.get("maxVersion")?.asString + gestaltModule(group = gestaltExtension.modulesPackage.get(), name = depId) { + if (minVersion != null && maxVersion == null) { + version { + strictly("[$minVersion,)") + } + } + if (minVersion == null && maxVersion != null) { + version { + strictly("(,$maxVersion[") + } + } + if (minVersion != null && maxVersion != null) { + version { + strictly("[$minVersion, $maxVersion[") + } + } + } + } + } + } +} diff --git a/build-logic/src/main/groovy/gestalt-repositories.gradle b/build-logic/src/main/kotlin/gestalt-repositories.gradle.kts similarity index 54% rename from build-logic/src/main/groovy/gestalt-repositories.gradle rename to build-logic/src/main/kotlin/gestalt-repositories.gradle.kts index 327fbdfcb..66fe2af8f 100644 --- a/build-logic/src/main/groovy/gestalt-repositories.gradle +++ b/build-logic/src/main/kotlin/gestalt-repositories.gradle.kts @@ -1,25 +1,25 @@ repositories { - mavenCentral() { + mavenCentral { content { // Terasology's libraries/modules aren't on maven central, so don't bother looking there. - excludeGroupByRegex('org\\.terasology(\\..+)?') + excludeGroupByRegex("""org\.terasology(\..+)?""") } } - google() { + google { content { // Terasology's libraries/modules aren't on maven central, so don't bother looking there. - excludeGroupByRegex('org\\.terasology(\\..+)?') + excludeGroupByRegex("""org\.terasology(\..+)?""") } } // Terasology Artifactory for any shared libs maven { - url "https://artifactory.terasology.io/artifactory/virtual-repo-live" + url = uri("https://artifactory.terasology.io/artifactory/virtual-repo-live") content { - includeGroupByRegex('org\\.terasology.gestalt(\\..+)?') + includeGroupByRegex("""org\.terasology.gestalt(\..+)?""") // A copy of Java-semver is hosted here too - includeModule('com.github.zafarkhaja', 'java-semver') + includeModule("com.github.zafarkhaja", "java-semver") } } -} \ No newline at end of file +} diff --git a/build-logic/src/main/kotlin/terasology-metrics.gradle.kts b/build-logic/src/main/kotlin/terasology-metrics.gradle.kts new file mode 100644 index 000000000..5f8d549d6 --- /dev/null +++ b/build-logic/src/main/kotlin/terasology-metrics.gradle.kts @@ -0,0 +1,99 @@ +import com.github.spotbugs.snom.SpotBugsTask + +plugins { + java + id("project-report") + checkstyle + pmd + id("com.github.spotbugs") + jacoco + id("ru.vyarus.animalsniffer") +} + +val codeMetrics: Configuration by configurations.creating + +dependencies { + // Config for our code analytics lives in a centralized repo: https://github.com/MovingBlocks/TeraConfig + codeMetrics(group = "org.terasology.config", name = "codemetrics", version = "2.2.0", ext = "zip") + + "pmd"("net.sourceforge.pmd:pmd-ant:7.26.0") + "pmd"("net.sourceforge.pmd:pmd-core:7.26.0") + "pmd"("net.sourceforge.pmd:pmd-java:7.26.0") + + "signature"("com.toasttab.android:gummy-bears-api-24:0.15.0:coreLib2@signature") +} + +animalsniffer { + // java.nio.* APIs can be desugared by D8. java.io.File.toPath() also needs to be excluded. + ignore = setOf("java.nio.file.*", "java.io.File") +} + +jacoco { + toolVersion = "0.8.15" +} + +tasks.jacocoTestReport { + dependsOn(tasks.test) // Despite doc saying this should be automatic we need to explicitly add it anyway :-( + reports { + // We only use the .exec report for display in Jenkins and such. More could be enabled if desired. + xml.required = false + csv.required = false + html.required = false + } +} + +checkstyle { + isIgnoreFailures = true + configFile = File(rootDir, "config/metrics/checkstyle/checkstyle.xml") + toolVersion = "10.2" + configDirectory.set(configFile.parentFile) + configProperties["samedir"] = configFile.parentFile +} + +pmd { + isIgnoreFailures = true + ruleSetFiles = files("$rootDir/config/metrics/pmd/pmd.xml") + // By default, gradle uses both ruleset file AND the rulesets. Override the ruleSets to use only those from the file + ruleSets = listOf() +} + +spotbugs { + toolVersion.set("4.8.1") + ignoreFailures.set(true) + excludeFilter.set(File(rootDir, "config/metrics/findbugs/findbugs-exclude.xml")) +} +tasks.named("spotbugsMain") { + reports.create("xml") { + enabled = true + outputLocation.set(file("$buildDir/reports/spotbugs/main/spotbugs.xml")) + } +} + +val extractMetricsConfig = rootProject.tasks.findByName("extractMetricsConfig") + ?: rootProject.tasks.register("extractMetricsConfig", Copy::class) { + description = "Extracts our configuration files from the zip we fetched as a dependency" + from({ codeMetrics.map { zipTree(it) } }) + into("$rootDir/config/metrics") + }.get() + +tasks.named("spotbugsMain") { dependsOn(extractMetricsConfig) } +tasks.named("pmdMain") { dependsOn(extractMetricsConfig) } + +tasks.withType().configureEach { + group = "Reporting" + dependsOn(extractMetricsConfig) +} + +tasks.withType().configureEach { + dependsOn(extractMetricsConfig) + group = "Reporting" +} + +tasks.withType().configureEach { + dependsOn(extractMetricsConfig) + group = "Reporting" +} + +tasks.check { + dependsOn(extractMetricsConfig) +} diff --git a/build-logic/src/main/kotlin/terasology-publish-common.gradle.kts b/build-logic/src/main/kotlin/terasology-publish-common.gradle.kts new file mode 100644 index 000000000..fb6b0100c --- /dev/null +++ b/build-logic/src/main/kotlin/terasology-publish-common.gradle.kts @@ -0,0 +1,59 @@ +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.authentication.http.BasicAuthentication + +plugins { + `maven-publish` +} + +publishing { + publications { + // maybeCreate: gestalt-module and destination-sol-module also configure the + // project-name-keyed publication, in whichever order their plugins get applied. + maybeCreate(project.name, MavenPublication::class.java).apply { + from(components["java"]) + pom { + name.set(project.name) + } + } + } + + repositories { + maven { + name = "TerasologyOrg" + if (rootProject.hasProperty("publishRepo")) { + // This first option is good for local testing, you can set a full explicit target repo in gradle.properties + val publishRepo = rootProject.property("publishRepo") as String + url = uri("https://artifactory.terasology.io/artifactory/$publishRepo") + + logger.info("Changing PUBLISH repoKey set via Gradle property to {}", publishRepo) + } else { + // Support override from the environment to use a different target publish org + var deducedPublishRepo = System.getenv()["PUBLISH_ORG"] + if (deducedPublishRepo.isNullOrEmpty()) { + // If not then default + deducedPublishRepo = "libs" + } + + // Base final publish repo on whether we're building a snapshot or a release + deducedPublishRepo += if (project.version.toString().endsWith("SNAPSHOT")) { + "-snapshot-local" + } else { + "-release-local" + } + + logger.info("The final deduced publish repo is {}", deducedPublishRepo) + url = uri("https://artifactory.terasology.io/artifactory/$deducedPublishRepo") + } + + if (rootProject.hasProperty("mavenUser") && rootProject.hasProperty("mavenPass")) { + credentials { + username = rootProject.property("mavenUser") as String + password = rootProject.property("mavenPass") as String + } + authentication { + create("basic") + } + } + } + } +} diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 4e9e5daae..000000000 --- a/build.gradle +++ /dev/null @@ -1,177 +0,0 @@ -// Git plugin details at https://github.com/ajoberstar/gradle-git -import org.ajoberstar.gradle.git.tasks.* - -plugins { - id 'destination-sol-ide' - id 'destination-sol-repositories' - id 'terasology-metrics' - id 'org.ajoberstar.grgit' version '5.0.0' apply false -} - -repositories { - // Good ole Maven central - mavenCentral() - - // Repos for LibGDX - maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } - maven { url "https://oss.sonatype.org/content/repositories/releases/" } - - // Terasology Artifactory for any shared libs - maven { - url "https://artifactory.terasology.io/artifactory/virtual-repo-live" - } - - maven { url "https://maven.google.com" } -} - -// Helper that returns a list of all local Destination Sol module projects -def destinationSolModules() { - subprojects.findAll {it.parent.name == 'modules'} -} - -destinationSolModules().forEach { destSolModule -> - destSolModule.configurations.configureEach { - resolutionStrategy.dependencySubstitution.all { dependency -> - if (dependency.requested instanceof ModuleComponentSelector && dependency.requested.group == "org.destinationsol.modules") { - destinationSolModules().forEach { otherModule -> - if (otherModule.name == dependency.requested.module) { - dependency.useTarget otherModule - } - } - } - } - } -} - -tasks.eclipse.doLast { - delete ".project" -} -tasks.eclipse.dependsOn extractMetricsConfig - -import org.jetbrains.gradle.ext.Application -idea { - project { - // Set JDK - jdkName = '1.8' - wildcards -= '!?*.groovy' - - settings { - compiler { - enableAutomake = true - } - - runConfigurations { - "Desktop"(Application) { - mainClass = 'org.destinationsol.desktop.SolDesktop' - moduleName 'DestinationSol.desktop.main' - workingDirectory = rootDir - jvmArgs = '-splash:engine/src/main/resources/assets/textures/mainMenu/mainMenuLogo.png -Xms256m -Xmx1024m -Dlog4j.configuration=log4j-debug.properties' - programParameters = '-noSplash -noCrashReport' - } - } - - copyright { - useDefault = 'DestinationSolCopyright' - profiles { - DestinationSolCopyright { - notice = 'Copyright 2023 The Terasology Foundation\n\nLicensed under the Apache License, Version 2.0 (the "License");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an "AS IS" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.' - keyword = 'Copyright' - allowReplaceRegexp = '' - } - } - } - - taskTriggers { - afterSync tasks.named('extractMetricsConfig') - } - - generateImlFiles = true - withIDEAFileXml("compiler.xml", ideaPatchAnnotationProcessors) - withIDEAFileXml("misc.xml", ideaPatchEntryPoints) - withIDEAFileXml("checkstyle-idea.xml", ideaPatchCheckstyle) - } - - ipr { - withXml { xmlProvider -> - // Apply a bunch of tweaks to IntelliJ config - all defined in ide.gradle - // Part reason for separate file was in case a module needs to define something it cannot do so in a project block - def iprNode = xmlProvider.asNode() - ideaActivateCheckstyle(iprNode) - ideaActivateCopyright(iprNode) - ideaActivateAnnotations(iprNode) - ideaActivateGit(iprNode) - ideaActivateGradle(iprNode) - } - - // Sets sourceCompatibility within IntelliJ (without this root build having the Java plugin applied) - whenMerged { project -> - project.jdk.languageLevel = 'JDK_1_8' - } - } - } - - // Tweaks to the .iws - workspace.iws.withXml { xmlProvider -> - def iwsNode = xmlProvider.asNode() - ideaMakeAutomatically(iwsNode) - ideaRunConfig(iwsNode) - } -} - -cleanIdea.doLast { - new File('DestinationSol.iws').delete() - new File('config/metrics').deleteDir() - println "Cleaned root - don't forget to re-extract code metrics config! 'gradlew extractConfig' will do so, or 'gradlew idea' (or eclipse)" -} - -import org.ajoberstar.grgit.Grgit -tasks.register('fetchAndroid') { - description = 'Git clones the Android facade source from GitHub' - - // Repo name is the dynamic part of the task name - def repo = 'DestSolAndroid' - - // Default GitHub account to use. Supply with -PgithubAccount="TargetAccountName" or via gradle.properties - def githubHome = 'MovingBlocks' - - def destination = file('android') - - // Don't clone this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) - enabled = !destination.exists() - //println "fetchAndroid requested for $repo from Github under $githubHome - exists already? " + !enabled - - doLast { - Grgit.clone( - // Do the actual clone if we don't have the directory already - uri: "https://github.com/$githubHome/" + repo + ".git", - //println "Fetching $repo from $uri" - dir: destination, - bare: false - ) - } -} - -tasks.register('fetchSteam') { - description = 'Git clones the Steam facade source from GitHub' - - // Repo name is the dynamic part of the task name - def repo = 'DestSolSteam' - - // Default GitHub account to use. Supply with -PgithubAccount="TargetAccountName" or via gradle.properties - def githubHome = findProperty('githubAccount') ?: 'MovingBlocks' - - def destination = file('steam') - - // Don't clone this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) - enabled = !destination.exists() - - doLast { - Grgit.clone( - // Do the actual clone if we don't have the directory already - uri: "https://github.com/$githubHome/" + repo + ".git", - //println "Fetching $repo from $uri" - dir: destination, - bare: false - ) - } -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 000000000..a99e11f67 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,176 @@ +// Git plugin details at https://github.com/ajoberstar/gradle-git +import org.ajoberstar.grgit.Grgit +import org.gradle.api.XmlProvider +import org.jetbrains.gradle.ext.Application +import org.jetbrains.gradle.ext.compiler +import org.jetbrains.gradle.ext.copyright +import org.jetbrains.gradle.ext.runConfigurations +import org.jetbrains.gradle.ext.settings +import org.jetbrains.gradle.ext.taskTriggers + +plugins { + id("destination-sol-ide") + id("destination-sol-repositories") + id("terasology-metrics") + id("org.ajoberstar.grgit") version "5.0.0" apply false +} + +repositories { + // Good ole Maven central + mavenCentral() + + // Repos for LibGDX + maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } + maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } + + // Terasology Artifactory for any shared libs + maven { + url = uri("https://artifactory.terasology.io/artifactory/virtual-repo-live") + } + + maven { url = uri("https://maven.google.com") } +} + +// Helper that returns a list of all local Destination Sol module projects +fun destinationSolModules() = subprojects.filter { it.parent?.name == "modules" } + +extra["destinationSolModules"] = ::destinationSolModules + +destinationSolModules().forEach { destSolModule -> + destSolModule.configurations.configureEach { + resolutionStrategy.dependencySubstitution.all { + val requestedSelector = requested + if (requestedSelector is ModuleComponentSelector && requestedSelector.group == "org.destinationsol.modules") { + destinationSolModules().forEach { otherModule -> + if (otherModule.name == requestedSelector.module) { + useTarget(otherModule) + } + } + } + } + } +} + +tasks.named("eclipse") { + doLast { + delete(".project") + } + dependsOn("extractMetricsConfig") +} + +@Suppress("UNCHECKED_CAST") +val ideaPatchAnnotationProcessors = extra["ideaPatchAnnotationProcessors"] as Action +@Suppress("UNCHECKED_CAST") +val ideaPatchEntryPoints = extra["ideaPatchEntryPoints"] as Action +@Suppress("UNCHECKED_CAST") +val ideaPatchCheckstyle = extra["ideaPatchCheckstyle"] as Action + +idea { + project { + // Set JDK + jdkName = "1.8" + wildcards.remove("!?*.groovy") + + settings { + compiler { + enableAutomake = true + } + + runConfigurations { + create("Desktop") { + mainClass = "org.destinationsol.desktop.SolDesktop" + moduleName = "DestinationSol.desktop.main" + workingDirectory = rootDir.toString() + jvmArgs = "-splash:engine/src/main/resources/assets/textures/mainMenu/mainMenuLogo.png -Xms256m -Xmx1024m -Dlog4j.configuration=log4j-debug.properties" + programParameters = "-noSplash -noCrashReport" + } + } + + copyright { + useDefault = "DestinationSolCopyright" + profiles { + create("DestinationSolCopyright") { + notice = "Copyright 2023 The Terasology Foundation\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + keyword = "Copyright" + allowReplaceRegexp = "" + } + } + } + + taskTriggers { + afterSync(tasks.named("extractMetricsConfig")) + } + + generateImlFiles = true + withIDEAFileXml("compiler.xml", ideaPatchAnnotationProcessors) + withIDEAFileXml("misc.xml", ideaPatchEntryPoints) + withIDEAFileXml("checkstyle-idea.xml", ideaPatchCheckstyle) + } + + // NOTE: the previous ipr.withXml{}/workspace.iws.withXml{} blocks here called + // ideaActivateCheckstyle/Copyright/Annotations/Git/Gradle, ideaMakeAutomatically and + // ideaRunConfig - none of which have existed since config/gradle/ide.gradle was deleted + // in 68193863 (2022). Groovy only ever failed on that at runtime, when `gradle idea`/ + // `ipr`/`iws` actually ran, which nothing does - so it's been silently dead ever since. + // Dropped rather than carried forward as newly-hard-failing Kotlin. + } +} + +tasks.named("cleanIdea") { + doLast { + rootDir.resolve("DestinationSol.iws").delete() + rootDir.resolve("config/metrics").deleteRecursively() + println("Cleaned root - don't forget to re-extract code metrics config! 'gradlew extractMetricsConfig' will do so, or 'gradlew idea' (or eclipse)") + } +} + +tasks.register("fetchAndroid") { + description = "Git clones the Android facade source from GitHub" + + // Repo name is the dynamic part of the task name + val repo = "DestSolAndroid" + + // Default GitHub account to use. Supply with -PgithubAccount="TargetAccountName" or via gradle.properties + val githubHome = findProperty("githubAccount") as String? ?: "MovingBlocks" + + val destination = file("android") + + // Don't clone this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) + enabled = !destination.exists() + //println("fetchAndroid requested for $repo from Github under $githubHome - exists already? " + !enabled) + + doLast { + Grgit.clone(mapOf( + // Do the actual clone if we don't have the directory already + "uri" to "https://github.com/$githubHome/$repo.git", + //println("Fetching $repo from $uri") + "dir" to destination, + "bare" to false + )) + } +} + +tasks.register("fetchSteam") { + description = "Git clones the Steam facade source from GitHub" + + // Repo name is the dynamic part of the task name + val repo = "DestSolSteam" + + // Default GitHub account to use. Supply with -PgithubAccount="TargetAccountName" or via gradle.properties + val githubHome = findProperty("githubAccount") as String? ?: "MovingBlocks" + + val destination = file("steam") + + // Don't clone this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) + enabled = !destination.exists() + + doLast { + Grgit.clone(mapOf( + // Do the actual clone if we don't have the directory already + "uri" to "https://github.com/$githubHome/$repo.git", + //println("Fetching $repo from $uri") + "dir" to destination, + "bare" to false + )) + } +} diff --git a/desktop/build.gradle b/desktop/build.gradle deleted file mode 100644 index 9aeb7185c..000000000 --- a/desktop/build.gradle +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2022 The Terasology Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -plugins { - id 'destination-sol-common' - id 'destination-sol-jre' -} - -import groovy.xml.XmlParser -import groovy.xml.XmlNodePrinter - -project.ext.mainClassName = "org.destinationsol.desktop.SolDesktop" - -dependencies { - implementation project(":engine") - implementation "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion" - implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" - implementation "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop" - implementation "com.badlogicgames.gdx-controllers:gdx-controllers-desktop:$gdxControllersVersion" - - implementation group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.7.25' - implementation group: 'org.terasology.crashreporter', name: 'cr-destsol', version: '4.0.0' - annotationProcessor "org.terasology.gestalt:gestalt-inject-java:$gestaltVersion" -} - -tasks.withType(ru.vyarus.gradle.plugin.animalsniffer.AnimalSniffer) { - // The desktop facade does not run on Android, so we do not have to fulfil its constraints. - exclude("**/*") -} - -tasks.register('run', JavaExec) { - dependsOn classes - //TODO: Remove extra args when the splash screen works on Macs again - see https://github.com/MovingBlocks/DestinationSol/issues/414 - if (System.properties["os.name"].toLowerCase().contains("mac")) { - jvmArgs = ["-splash:../engine/src/main/resources/assets/textures/mainMenu/mainMenuLogo.png", "-XstartOnFirstThread", "-Dlog4j.configuration=log4j-debug.properties"] - String[] runArgs = ["-noSplash"] - args runArgs - } else { - jvmArgs = ["-splash:../engine/src/main/resources/assets/textures/mainMenu/mainMenuLogo.png", "-Dlog4j.configuration=log4j-debug.properties"] - } - mainClass = project.mainClassName - classpath = sourceSets.main.runtimeClasspath - standardInput = System.in - workingDir = rootProject.projectDir - ignoreExitValue true -} - -jar { - archiveFileName = "solDesktop.jar" - - manifest { - def manifestClasspath = configurations.runtimeClasspath.collect { it.getName() }.join(" ") - attributes 'Main-Class': project.mainClassName - attributes("Class-Path": manifestClasspath) - attributes "SplashScreen-Image": "mainMenuLogo.png" - } -} - -tasks.register('moduleDist', Copy) { - into("${base.distsDirectory.getAsFile().get()}/app/modules") - rootProject.destinationSolModules().each { module -> - dependsOn ":modules:$module.name" + ":jar" - from("$rootDir/modules/${module.name}/build/libs") { - include "*.jar" - } - } -} - -tasks.register('copyLaunchers', Copy) { - description = "Copy launchers into the distribution folder." - - from("$rootDir/launcher") - include("*.sh", "*.exe") - into("${base.distsDirectory.getAsFile().get()}/app") -} - -tasks.register('libsDist', Copy) { - description = "Copy libs directory into the distribution folder." - - dependsOn jar - - from jar - from configurations.runtimeClasspath - into("${base.distsDirectory.getAsFile().get()}/app/libs") -} - -tasks.register('distUnbundledJRE') { - description = "Creates an application package without any bundled JRE." - group 'Distribution' - - dependsOn libsDist - dependsOn moduleDist - dependsOn copyLaunchers -} - -tasks.register('distZipUnbundledJRE', Zip) { - description = "Creates an application package and zip archive without any bundled JRE." - group 'Distribution' - - dependsOn distUnbundledJRE - from "${base.distsDirectory.getAsFile().get()}/app" - archiveFileName = "DestinationSol.zip" -} - -tasks.register('distBundleJREs') { - description = "Creates an application package with a bundled JRE." - group 'Distribution' - - dependsOn distUnbundledJRE - dependsOn downloadJreAll -} - -tasks.register('distZipBundleJREs', Zip) { - description = "Creates an application package and zip archive with a bundled JRE." - group 'Distribution' - - dependsOn distBundleJREs - from "${base.distsDirectory.getAsFile().get()}/app" - archiveFileName = "DestinationSol.zip" -} - -// TODO: LibGDX Generated config for Eclipse. Needs adjustment for assets not being in the Android facade -eclipse { - project { - name = appName + "-desktop" - linkedResource name: 'assets', type: '2', location: 'PARENT-1-PROJECT_LOC/android/assets' - file { - whenMerged { project -> - def destinationSolRunConfig = new XmlParser().parseText(''' - - - - - - - - - - - - - - - - ''') - def writer = new FileWriter(file("DestinationSol.launch")) - def printer = new XmlNodePrinter(new PrintWriter(writer)) - printer.setPreserveWhitespace(true) - printer.print(destinationSolRunConfig) - } - } - } -} - -tasks.register('afterEclipseImport') { - description 'Post processing after project generation' - group 'IDE' - - doLast { - def classpath = new XmlParser().parse(file(".classpath")) - new Node(classpath, "classpathentry", [kind: 'src', path: 'assets']); - def writer = new FileWriter(file(".classpath")) - def printer = new XmlNodePrinter(new PrintWriter(writer)) - printer.setPreserveWhitespace(true) - printer.print(classpath) - } -} - -tasks.withType(JavaExec).configureEach { - if (System.getProperty('DEBUG', 'false') == 'true') { - jvmArgs '-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9099', '-Dlog4j.configuration=log4j-debug.properties' - } -} diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts new file mode 100644 index 000000000..2457a1ac5 --- /dev/null +++ b/desktop/build.gradle.kts @@ -0,0 +1,206 @@ +/* + * Copyright 2022 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import groovy.xml.XmlNodePrinter +import groovy.xml.XmlParser +import org.gradle.api.plugins.BasePluginExtension +import org.gradle.plugins.ide.eclipse.model.EclipseModel +import java.io.FileWriter +import java.io.PrintWriter + +plugins { + id("destination-sol-common") + id("destination-sol-jre") +} + +val gdxVersion = extra["gdxVersion"] as String +val gestaltVersion = extra["gestaltVersion"] as String +val gdxControllersVersion = extra["gdxControllersVersion"] as String +val appName = extra["appName"] as String + +val mainClassName = "org.destinationsol.desktop.SolDesktop" +extra["mainClassName"] = mainClassName + +dependencies { + implementation(project(":engine")) + implementation("com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion") + implementation("com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop") + implementation("com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop") + implementation("com.badlogicgames.gdx-controllers:gdx-controllers-desktop:$gdxControllersVersion") + + implementation(group = "org.slf4j", name = "slf4j-log4j12", version = "1.7.25") + implementation(group = "org.terasology.crashreporter", name = "cr-destsol", version = "4.0.0") + annotationProcessor("org.terasology.gestalt:gestalt-inject-java:$gestaltVersion") +} + +tasks.withType().configureEach { + // The desktop facade does not run on Android, so we do not have to fulfil its constraints. + exclude("**/*") +} + +val distsDirectory = the().distsDirectory + +tasks.register("run") { + dependsOn(tasks.classes) + //TODO: Remove extra args when the splash screen works on Macs again - see https://github.com/MovingBlocks/DestinationSol/issues/414 + if (System.getProperty("os.name").lowercase().contains("mac")) { + jvmArgs = listOf("-splash:../engine/src/main/resources/assets/textures/mainMenu/mainMenuLogo.png", "-XstartOnFirstThread", "-Dlog4j.configuration=log4j-debug.properties") + args("-noSplash") + } else { + jvmArgs = listOf("-splash:../engine/src/main/resources/assets/textures/mainMenu/mainMenuLogo.png", "-Dlog4j.configuration=log4j-debug.properties") + } + mainClass.set(mainClassName) + classpath = sourceSets.main.get().runtimeClasspath + standardInput = System.`in` + workingDir = rootProject.projectDir + isIgnoreExitValue = true +} + +tasks.jar { + archiveFileName.set("solDesktop.jar") + + manifest { + val manifestClasspath = configurations.runtimeClasspath.get().joinToString(" ") { it.name } + attributes("Main-Class" to mainClassName) + attributes("Class-Path" to manifestClasspath) + attributes("SplashScreen-Image" to "mainMenuLogo.png") + } +} + +tasks.register("moduleDist") { + into("${distsDirectory.get().asFile}/app/modules") + @Suppress("UNCHECKED_CAST") + val destinationSolModules = rootProject.extra["destinationSolModules"] as () -> List + destinationSolModules().forEach { module -> + dependsOn(":modules:${module.name}:jar") + from("$rootDir/modules/${module.name}/build/libs") { + include("*.jar") + } + } +} + +tasks.register("copyLaunchers") { + description = "Copy launchers into the distribution folder." + + from("$rootDir/launcher") + include("*.sh", "*.exe") + into("${distsDirectory.get().asFile}/app") +} + +tasks.register("libsDist") { + description = "Copy libs directory into the distribution folder." + + dependsOn(tasks.jar) + + from(tasks.jar) + from(configurations.runtimeClasspath) + into("${distsDirectory.get().asFile}/app/libs") +} + +tasks.register("distUnbundledJRE") { + description = "Creates an application package without any bundled JRE." + group = "Distribution" + + dependsOn("libsDist") + dependsOn("moduleDist") + dependsOn("copyLaunchers") +} + +tasks.register("distZipUnbundledJRE") { + description = "Creates an application package and zip archive without any bundled JRE." + group = "Distribution" + + dependsOn("distUnbundledJRE") + from("${distsDirectory.get().asFile}/app") + archiveFileName.set("DestinationSol.zip") +} + +tasks.register("distBundleJREs") { + description = "Creates an application package with a bundled JRE." + group = "Distribution" + + dependsOn("distUnbundledJRE") + dependsOn("downloadJreAll") +} + +tasks.register("distZipBundleJREs") { + description = "Creates an application package and zip archive with a bundled JRE." + group = "Distribution" + + dependsOn("distBundleJREs") + from("${distsDirectory.get().asFile}/app") + archiveFileName.set("DestinationSol.zip") +} + +// TODO: LibGDX Generated config for Eclipse. Needs adjustment for assets not being in the Android facade +// eclipse is applied transitively (via destination-sol-common -> destination-sol-ide), not by +// this script's own plugins{} block, so the type-safe eclipse{} accessor isn't available here. +configure { + project { + name = "$appName-desktop" + linkedResource(mapOf("name" to "assets", "type" to "2", "location" to "PARENT-1-PROJECT_LOC/android/assets")) + file { + whenMerged { + val destinationSolRunConfig = XmlParser().parseText(""" + + + + + + + + + + + + + + + + """) + val writer = FileWriter(file("DestinationSol.launch")) + val printer = XmlNodePrinter(PrintWriter(writer)) + printer.isPreserveWhitespace = true + printer.print(destinationSolRunConfig) + } + } + } +} + +tasks.register("afterEclipseImport") { + description = "Post processing after project generation" + group = "IDE" + + doLast { + val classpath = XmlParser().parse(file(".classpath")) + groovy.util.Node(classpath, "classpathentry", mapOf("kind" to "src", "path" to "assets")) + val writer = FileWriter(file(".classpath")) + val printer = XmlNodePrinter(PrintWriter(writer)) + printer.isPreserveWhitespace = true + printer.print(classpath) + } +} + +// Registering the task above doesn't make :desktop:eclipse run it. finalizedBy, not +// synchronizationTasks: the latter only fires on a Buildship-driven IDE sync, not on a plain +// `gradlew eclipse` - and .classpath (which this patches) doesn't exist until eclipse has run. +tasks.named("eclipse") { finalizedBy(tasks.named("afterEclipseImport")) } + +tasks.withType().configureEach { + if (System.getProperty("DEBUG", "false") == "true") { + jvmArgs("-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9099", "-Dlog4j.configuration=log4j-debug.properties") + } +} diff --git a/engine/build.gradle b/engine/build.gradle deleted file mode 100644 index 83287e243..000000000 --- a/engine/build.gradle +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2022 The Terasology Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( -buildscript { - repositories { - mavenCentral() - - google() - maven { - url "https://artifactory.terasology.io/artifactory/virtual-repo-live" - } - // Needed for Jsemver, which is a gestalt dependency - maven { url = 'https://heisluft.de/maven/' } - } - - dependencies { - classpath 'dom4j:dom4j:1.6.1' - } -} - -plugins { - id 'java-library' - id 'destination-sol-common' - id 'terasology-publish-common' -} - -group = "org.destinationsol.engine" -version = engineVersion - -dependencies { - api group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' - api group: 'com.google.code.gson', name: 'gson', version: '2.6.2' - api group: 'com.google.guava', name: 'guava', version: '30.1-jre' - - api "com.badlogicgames.gdx:gdx:$gdxVersion" - api "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" - api "com.badlogicgames.gdx-controllers:gdx-controllers-core:$gdxControllersVersion" - - api "org.terasology.gestalt:gestalt-asset-core:$gestaltVersion" - api "org.terasology.gestalt:gestalt-entity-system:$gestaltVersion" - api "org.terasology.gestalt:gestalt-module:$gestaltVersion" - api "org.terasology.gestalt:gestalt-util:$gestaltVersion" - - api "org.terasology.gestalt:gestalt-di:$gestaltVersion" - api "org.terasology.gestalt:gestalt-inject:$gestaltVersion" - annotationProcessor "org.terasology.gestalt:gestalt-inject-java:$gestaltVersion" - - implementation "net.jcip:jcip-annotations:1.0" - - api "org.terasology.nui:nui:$nuiVersion" - api "org.terasology.nui:nui-libgdx:$nuiVersion" - api "org.terasology.nui:nui-gestalt:$nuiVersion" - api "org.terasology.nui:nui-reflect:$nuiVersion" - - implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.4.0' - - implementation "com.github.zafarkhaja:java-semver:0.10.0" // gestalt lost this... - api "com.github.everit-org.json-schema:org.everit.json.schema:1.11.1" - implementation "com.github.marschall:zipfilesystem-standalone:1.0.1" - implementation 'dom4j:dom4j:1.6.1' - - //TODO inserted by someone who has no idea how the gradle thing works. Inspect whether necessary (for tests). Copied rom desktop build.gradle. May break things with android (maybe) - testImplementation "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion" - testImplementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" - testImplementation "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop" - testImplementation "com.badlogicgames.gdx-controllers:gdx-controllers-desktop:$gdxControllersVersion" - - // Test lib dependencies - testImplementation("org.junit.jupiter:junit-jupiter-api:6.1.2") - testImplementation("org.junit.jupiter:junit-jupiter-params:6.1.2") - testImplementation "org.mockito:mockito-junit-jupiter:5.23.0" - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:6.1.2") - testRuntimeOnly('org.junit.platform:junit-platform-launcher:6.1.2') - - testImplementation "org.jboss.shrinkwrap:shrinkwrap-depchain-java7:1.1.3" - testImplementation "org.assertj:assertj-core:3.27.7" -} - -// Adds Resources as parameter for AnnotationProcessor (gather ResourceIndex, -// also add resource as input for compilejava, for re-gathering ResourceIndex, when resource was changed. -compileJava { - inputs.files sourceSets.main.resources.srcDirs - options.compilerArgs = ["-Aresource=${sourceSets.main.resources.srcDirs.join(File.pathSeparator)}"] -} -compileTestJava { - inputs.files sourceSets.test.resources.srcDirs - options.compilerArgs = ["-Aresource=${sourceSets.test.resources.srcDirs.join(File.pathSeparator)}"] -} - -jar { - archiveFileName = "sol.jar" - - doFirst { - copy { - from 'src/SolAppListener.gwt.xml' - into 'build/classes/main' - } - } -} - -eclipse.project { - name = appName + "-engine" -} - -// Extra details provided for unit tests -test { - // ignoreFailures: Specifies whether the build should break when the verifications performed by this task fail. - ignoreFailures = true - useJUnitPlatform() - // showStandardStreams: makes the standard streams (err and out) visible at console when running tests - testLogging.showStandardStreams = true - workingDir = rootProject.projectDir -} diff --git a/engine/build.gradle.kts b/engine/build.gradle.kts new file mode 100644 index 000000000..02bab6ac5 --- /dev/null +++ b/engine/build.gradle.kts @@ -0,0 +1,139 @@ +/* + * Copyright 2022 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( +buildscript { + repositories { + mavenCentral() + + google() + maven { + url = uri("https://artifactory.terasology.io/artifactory/virtual-repo-live") + } + // Needed for Jsemver, which is a gestalt dependency + maven { url = uri("https://heisluft.de/maven/") } + } + + dependencies { + classpath("dom4j:dom4j:1.6.1") + } +} + +plugins { + `java-library` + id("destination-sol-common") + id("terasology-publish-common") +} + +group = "org.destinationsol.engine" + +val engineVersion = extra["engineVersion"] as String +val gdxVersion = extra["gdxVersion"] as String +val gdxControllersVersion = extra["gdxControllersVersion"] as String +val gestaltVersion = extra["gestaltVersion"] as String +val nuiVersion = extra["nuiVersion"] as String +val appName = extra["appName"] as String + +version = engineVersion + +dependencies { + api(group = "org.slf4j", name = "slf4j-api", version = "1.7.25") + api(group = "com.google.code.gson", name = "gson", version = "2.6.2") + api(group = "com.google.guava", name = "guava", version = "30.1-jre") + + api("com.badlogicgames.gdx:gdx:$gdxVersion") + api("com.badlogicgames.gdx:gdx-box2d:$gdxVersion") + api("com.badlogicgames.gdx-controllers:gdx-controllers-core:$gdxControllersVersion") + + api("org.terasology.gestalt:gestalt-asset-core:$gestaltVersion") + api("org.terasology.gestalt:gestalt-entity-system:$gestaltVersion") + api("org.terasology.gestalt:gestalt-module:$gestaltVersion") + api("org.terasology.gestalt:gestalt-util:$gestaltVersion") + + api("org.terasology.gestalt:gestalt-di:$gestaltVersion") + api("org.terasology.gestalt:gestalt-inject:$gestaltVersion") + annotationProcessor("org.terasology.gestalt:gestalt-inject-java:$gestaltVersion") + + implementation("net.jcip:jcip-annotations:1.0") + + api("org.terasology.nui:nui:$nuiVersion") + api("org.terasology.nui:nui-libgdx:$nuiVersion") + api("org.terasology.nui:nui-gestalt:$nuiVersion") + api("org.terasology.nui:nui-reflect:$nuiVersion") + + implementation(group = "com.google.protobuf", name = "protobuf-java", version = "3.4.0") + + implementation("com.github.zafarkhaja:java-semver:0.10.0") // gestalt lost this... + api("com.github.everit-org.json-schema:org.everit.json.schema:1.11.1") + implementation("com.github.marschall:zipfilesystem-standalone:1.0.1") + implementation("dom4j:dom4j:1.6.1") + + //TODO inserted by someone who has no idea how the gradle thing works. Inspect whether necessary (for tests). Copied rom desktop build.gradle. May break things with android (maybe) + testImplementation("com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion") + testImplementation("com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop") + testImplementation("com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop") + testImplementation("com.badlogicgames.gdx-controllers:gdx-controllers-desktop:$gdxControllersVersion") + + // Test lib dependencies + testImplementation("org.junit.jupiter:junit-jupiter-api:6.1.2") + testImplementation("org.junit.jupiter:junit-jupiter-params:6.1.2") + testImplementation("org.mockito:mockito-junit-jupiter:5.23.0") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:6.1.2") + testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.1.2") + + testImplementation("org.jboss.shrinkwrap:shrinkwrap-depchain-java7:1.1.3") + testImplementation("org.assertj:assertj-core:3.27.7") +} + +// Adds Resources as parameter for AnnotationProcessor (gather ResourceIndex, +// also add resource as input for compilejava, for re-gathering ResourceIndex, when resource was changed. +tasks.compileJava { + inputs.files(sourceSets.main.get().resources.srcDirs) + options.compilerArgs = listOf("-Aresource=${sourceSets.main.get().resources.srcDirs.joinToString(File.pathSeparator)}") +} +tasks.compileTestJava { + inputs.files(sourceSets.test.get().resources.srcDirs) + options.compilerArgs = listOf("-Aresource=${sourceSets.test.get().resources.srcDirs.joinToString(File.pathSeparator)}") +} + +tasks.jar { + archiveFileName.set("sol.jar") + + doFirst { + copy { + from("src/SolAppListener.gwt.xml") + into("build/classes/main") + } + } +} + +// eclipse is applied transitively (via destination-sol-common -> destination-sol-ide), not by +// this script's own plugins{} block, so the type-safe eclipse{} accessor isn't available here. +configure { + project { + name = "$appName-engine" + } +} + +// Extra details provided for unit tests +tasks.test { + // ignoreFailures: Specifies whether the build should break when the verifications performed by this task fail. + ignoreFailures = true + useJUnitPlatform() + // showStandardStreams: makes the standard streams (err and out) visible at console when running tests + testLogging.showStandardStreams = true + workingDir = rootProject.projectDir +} diff --git a/libs/subprojects.gradle b/libs/subprojects.gradle deleted file mode 100644 index 07598e541..000000000 --- a/libs/subprojects.gradle +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -// This magically allows subdirs to become included builds -// https://docs.gradle.org/6.4.1/userguide/composite_builds.html -file(".").eachDir { possibleIncludedBuildDirectory -> - File buildFile = new File(possibleIncludedBuildDirectory, "build.gradle") - File buildFileKts = new File(possibleIncludedBuildDirectory, "build.gradle.kts") - File settingsFile = new File(possibleIncludedBuildDirectory, "settings.gradle") - File settingsFileKts = new File(possibleIncludedBuildDirectory, "settings.gradle.kts") - - if ((buildFile.exists() || buildFileKts.exists()) && (settingsFile.exists() || settingsFileKts.exists())) { - logger.info("{} will be included in the composite build.", - rootDir.relativePath(possibleIncludedBuildDirectory)) - includeBuild(possibleIncludedBuildDirectory) - } else { - logger.warn("{} REJECTED as an included build. build.gradle: {}, settings.gradle: {}", - rootDir.relativePath(possibleIncludedBuildDirectory), - buildFile.exists() ? "present" : "MISSING", - settingsFile.exists() ? "present" : "MISSING" - ) - } -} \ No newline at end of file diff --git a/libs/subprojects.gradle.kts b/libs/subprojects.gradle.kts new file mode 100644 index 000000000..917a05b38 --- /dev/null +++ b/libs/subprojects.gradle.kts @@ -0,0 +1,26 @@ +// Copyright 2020 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +// This magically allows subdirs to become included builds +// https://docs.gradle.org/6.4.1/userguide/composite_builds.html +// This script is apply(from = ...)'d from settings.gradle.kts, so file(".") here would resolve +// against settingsDir (the repo root), not against libs/ where this script itself lives - scan +// libs/ explicitly instead. +File(rootDir, "libs").listFiles { f -> f.isDirectory }?.forEach { possibleIncludedBuildDirectory -> + val buildFile = File(possibleIncludedBuildDirectory, "build.gradle") + val buildFileKts = File(possibleIncludedBuildDirectory, "build.gradle.kts") + val settingsFile = File(possibleIncludedBuildDirectory, "settings.gradle") + val settingsFileKts = File(possibleIncludedBuildDirectory, "settings.gradle.kts") + + if ((buildFile.exists() || buildFileKts.exists()) && (settingsFile.exists() || settingsFileKts.exists())) { + logger.info("{} will be included in the composite build.", + rootDir.toPath().relativize(possibleIncludedBuildDirectory.toPath())) + includeBuild(possibleIncludedBuildDirectory) + } else { + logger.warn("{} REJECTED as an included build. build.gradle(.kts): {}, settings.gradle(.kts): {}", + rootDir.toPath().relativize(possibleIncludedBuildDirectory.toPath()), + if (buildFile.exists() || buildFileKts.exists()) "present" else "MISSING", + if (settingsFile.exists() || settingsFileKts.exists()) "present" else "MISSING" + ) + } +} diff --git a/modules/subprojects.gradle b/modules/subprojects.gradle deleted file mode 100644 index a009734ef..000000000 --- a/modules/subprojects.gradle +++ /dev/null @@ -1,25 +0,0 @@ -// This magically allows subdirs in this subproject to themselves become sub-subprojects in a proper tree structure -new File(rootDir, 'modules').eachDir { possibleSubprojectDir -> - if (!possibleSubprojectDir.name.startsWith(".")) { - def subprojectName = 'modules:' + possibleSubprojectDir.name - //println "Gradle is reviewing module $subprojectName for inclusion as a sub-project" - File buildFile = new File(possibleSubprojectDir, "build.gradle") - if (buildFile.exists()) { - println "Module $subprojectName has a build file so counting it complete and including it" - } else { - println "***** WARNING: Found a module without a build.gradle, Adding a build.gradle to $subprojectName. *****" - copy { - from("$rootProject.projectDir/templates") - into("$possibleSubprojectDir") - include("build.gradle") - } - } - - include subprojectName - def subprojectPath = ':' + subprojectName - def subproject = project(subprojectPath) - subproject.projectDir = possibleSubprojectDir - } else { - println "Ignoring path for module consideration: " + possibleSubprojectDir - } -} diff --git a/modules/subprojects.gradle.kts b/modules/subprojects.gradle.kts new file mode 100644 index 000000000..0095ba589 --- /dev/null +++ b/modules/subprojects.gradle.kts @@ -0,0 +1,25 @@ +// This magically allows subdirs in this subproject to themselves become sub-subprojects in a proper tree structure +File(rootDir, "modules").listFiles { file -> file.isDirectory }?.forEach { possibleSubprojectDir -> + if (!possibleSubprojectDir.name.startsWith(".")) { + val subprojectName = "modules:" + possibleSubprojectDir.name + //println("Gradle is reviewing module $subprojectName for inclusion as a sub-project") + val buildFile = File(possibleSubprojectDir, "build.gradle") + val buildFileKts = File(possibleSubprojectDir, "build.gradle.kts") + if (buildFile.exists() || buildFileKts.exists()) { + println("Module $subprojectName has a build file so counting it complete and including it") + } else { + println("***** WARNING: Found a module without a build.gradle.kts, Adding a build.gradle.kts to $subprojectName. *****") + copy { + from("${rootProject.projectDir}/templates") + into(possibleSubprojectDir) + include("build.gradle.kts") + } + } + + include(subprojectName) + val subprojectPath = ":$subprojectName" + project(subprojectPath).projectDir = possibleSubprojectDir + } else { + println("Ignoring path for module consideration: $possibleSubprojectDir") + } +} diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index fe49b896d..000000000 --- a/settings.gradle +++ /dev/null @@ -1,32 +0,0 @@ -includeBuild 'build-logic' - -include 'desktop', 'engine', 'modules' -import groovy.io.FileType - -File steamGradle = new File(rootDir, 'steam/build.gradle') -if (steamGradle.exists()) { - include 'steam' -} - -File gwtGradle = new File(rootDir, 'gwt/build.gradle') -if (gwtGradle.exists()) { - include 'gwt' -} - -rootProject.name = 'DestinationSol' - -// Handy little snippet found online that'll "fake" having nested settings.gradle files under /modules, /libs, etc -rootDir.eachDir { possibleSubprojectDir -> - - // First scan through all subdirs that has a subprojects.gradle in it and apply that script (recursive search!) - possibleSubprojectDir.eachFileMatch FileType.FILES, ~/subprojects\.gradle/, { subprojectsSpecificationScript -> - //println "Magic is happening, applying from " + subprojectsSpecificationScript - apply from: subprojectsSpecificationScript - } -} - -// This is put last to ensure that Android can detect the modules -File androidGradle = new File(rootDir, 'android/build.gradle') -if (androidGradle.exists()) { - include 'android' -} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 000000000..69300c24a --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,31 @@ +includeBuild("build-logic") + +include("desktop", "engine", "modules") + +if (File(rootDir, "steam/build.gradle").exists() || File(rootDir, "steam/build.gradle.kts").exists()) { + include("steam") +} + +if (File(rootDir, "gwt/build.gradle").exists() || File(rootDir, "gwt/build.gradle.kts").exists()) { + include("gwt") +} + +rootProject.name = "DestinationSol" + +// Handy little snippet found online that'll "fake" having nested settings.gradle files under /modules, /libs, etc +rootDir.listFiles { file -> file.isDirectory }?.forEach { possibleSubprojectDir -> + + // First scan through all subdirs that has a subprojects.gradle.kts in it and apply that script (recursive search!) + possibleSubprojectDir.listFiles { file -> file.isFile && file.name == "subprojects.gradle.kts" } + ?.forEach { subprojectsSpecificationScript -> + //println("Magic is happening, applying from $subprojectsSpecificationScript") + apply(from = subprojectsSpecificationScript) + } +} + +// This is put last to ensure that Android can detect the modules +// Checks both names: Jenkins checks out DestSolAndroid's own develop branch here, which may or +// may not have been converted to Kotlin DSL independently of this repo. +if (File(rootDir, "android/build.gradle").exists() || File(rootDir, "android/build.gradle.kts").exists()) { + include("android") +} diff --git a/templates/build.gradle b/templates/build.gradle deleted file mode 100644 index 5b0372d86..000000000 --- a/templates/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id 'destination-sol-module' -} diff --git a/templates/build.gradle.kts b/templates/build.gradle.kts new file mode 100644 index 000000000..1a1d3ae69 --- /dev/null +++ b/templates/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("destination-sol-module") +}