diff --git a/.gitignore b/.gitignore index 10904360e31..4105e5171cd 100644 --- a/.gitignore +++ b/.gitignore @@ -19,19 +19,19 @@ extensions !/modules/Core !/modules/CoreSampleGameplay !/modules/BuilderSampleGameplay -!/modules/subprojects.gradle +!/modules/*.gradle /meta/* !/meta/subprojects.gradle /libs/* !/libs/subprojects.gradle -# Ignore Gradle and some transient project config files (template versions available under /templates) +# Ignore Gradle and some transient project config files (template versions available under /gradle/templates) build/ /gradle.properties /override.cfg # Ignore IntelliJ - could use "/**/*.iml" to catch all .iml files, but that requires a somewhat recent Git version -/out/ +out/ modules/Core/Core.iml modules/CoreSampleGameplay/CoreSampleGameplay.iml modules/BuilderSampleGameplay/BuilderSampleGameplay.iml @@ -90,7 +90,7 @@ entityDump.txt # Ignore stuff we extract for use with Terasology /natives/ -config/metrics/ +gradle/metrics/ # Ignore weird stuff that might be obsolete diff --git a/build.gradle b/build.gradle index 7a8fd99b57d..41b941a4c4c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,18 +1,7 @@ -// Separate build file for structure heavy stuff like using Git to fetch other repos to embed within the project -apply from: 'config/gradle/utility.gradle' -apply from: 'config/gradle/ide.gradle' - // Needed for extending the "clean" task to also delete custom stuff defined here like natives apply plugin: 'base' -// For generating IntelliJ project files -apply plugin: 'idea' - -// The root project should not be an eclipse project. It keeps eclipse (4.2) from finding the sub-projects. -//apply plugin: 'eclipse' - -// Git plugin details at https://github.com/ajoberstar/gradle-git -import org.ajoberstar.gradle.git.tasks.* +apply plugin: 'project-report' // Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( buildscript { @@ -41,8 +30,8 @@ assert org.gradle.api.JavaVersion.current().isJava8Compatible() // Declare "extra properties" (variables) for the project (and subs) - a Gradle thing that makes them special. ext { dirNatives = 'natives' - dirConfigMetrics = 'config/metrics' - templatesDir = 'templates' + dirConfigMetrics = 'gradle/metrics' + templatesDir = 'gradle/templates' // Lib dir for use in manifest entries etc (like in :engine). A separate "libsDir" exists, auto-created by Gradle subDirLibs = 'libs' @@ -58,7 +47,12 @@ repositories { // MovingBlocks Artifactory instance for libs not readily available elsewhere plus our own libs maven { - url "http://artifactory.terasology.org/artifactory/virtual-repo-live" + if (rootProject.hasProperty("artifactoryResolveRepo")) { + url "http://artifactory.terasology.org/artifactory/$artifactoryResolveRepo" + println "Changing root RESOLVE repo to $artifactoryResolveRepo" + } else { + url "http://artifactory.terasology.org/artifactory/virtual-repo-live" + } } } @@ -130,38 +124,9 @@ task extractConfig (type: Copy) { into "$rootDir/$dirConfigMetrics" } -// Helper that returns a list of all local Terasology module projects -def terasologyModules() { - subprojects.findAll {it.parent.name == 'modules'} -} - -// Helper that replaces the build.gradle under every module with a fresh copy from the Core module -task refreshModuleGradle << { - File replacementGradle = new File(rootDir, 'modules/Core/build.gradle') - terasologyModules().each { - if (it.name != 'Core') { - File targetFile = new File(rootDir, "modules/" + it.name + "/build.gradle") - targetFile.delete() - targetFile << replacementGradle.text - } - } -} - -// Helpers that do magic things after having dependencies attached below -task moduleClasses -task moduleJars - -// This magically makes everything work - without this the desired module projects returned have no tasks :-( -gradle.projectsEvaluated { - // Note how "classes" may indirectly trigger "jar" for module dependencies of modules (module compile dependency) - moduleClasses.dependsOn(terasologyModules().classes) - - // This makes it work for a full jar task - moduleJars.dependsOn(terasologyModules().jar) -} - // This is a TEMPORARY tweak to make "changing" dependencies always ('0') check for newer snapshots available // TODO: Remove this when versioning and promotion works fully, then we shouldn't care about snapshots normally anyway +// For some reason this can stay here, yet had to be removed from everywhere else for Gradle 3.3 configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'seconds' } @@ -183,12 +148,17 @@ task protobufCompileLinux(type:Exec) { commandLine 'protobuf/compiler/protoc', '--proto_path=engine/src/main/protobuf', '--java_out', 'engine/src/main/java', "engine/src/main/protobuf/EntityData.proto", "engine/src/main/protobuf/NetMessage.proto" } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// General IDE customization // -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// These tasks are written in a way that makes them execute always, outside Gradle's regular workflow of calculating what needs to be done // +// If they were written as Copy tasks and such they'd only work during the execution phase for a run that included them on the task graph // +// That's how it was done in the frequent 'gradlew idea' days where any change impacting project structure needed a rerun + IDE restart // +// With Gradle import / linking enabled instead we can use IDE integration to avoid a restart but don't have an explicit task to chain to // +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// TODO: Convert the natives, config, and IDE setup to be tasks like this one task copyInMissingTemplates { - description = "Copies in placeholders from the /templates dir to project root if not present yet" + description = "Copies in placeholders from the /gradle/templates dir to project root if not present yet" File gradlePropsFile = new File(rootDir, 'gradle.properties') File OverrideCfgFile = new File(rootDir, 'override.cfg') if (!gradlePropsFile.exists()) { @@ -199,64 +169,48 @@ task copyInMissingTemplates { } } -// Make sure the IDE prep includes extraction of natives -ideaModule.dependsOn extractNatives -ideaModule.dependsOn copyInMissingTemplates - -// For IntelliJ add a bunch of excluded directories -idea { - - // Exclude Eclipse dirs - // TODO: Update this as Eclipse bin dirs now generate in several deeper spots rather than at top-level - module.excludeDirs += file('bin') - module.excludeDirs += file('.settings') - // TODO: Add a single file exclude for facades/PC/Terasology.launch ? - - // Exclude special dirs - module.excludeDirs += file('natives') - module.excludeDirs += file('protobuf') - - // Exclude output dirs - module.excludeDirs += file('logs') - module.excludeDirs += file('saves') - module.excludeDirs += file('screenshots') - module.excludeDirs += file('terasology-server') - module.excludeDirs += file('terasology-2ndclient') - - module.downloadSources = true - - project { - // Set JDK - jdkName = '1.8' - wildcards -= '!?*.groovy' - - 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' - } - } +// TODO: Consider making this stuff excluded from clean, then offer a fullClean for the rare time that may be needed +// That would allow a `gradlew clean` without having to rerun this at all, everything would still work fine (with rebuild) +task prepareStuff { + dependsOn extractNatives + dependsOn extractConfig + dependsOn copyInMissingTemplates +} + +task harnessTheBuild (type: Zip) { + description = "Prepares all build files and supporting stuff needed to build modules solo" + dependsOn extractNatives + dependsOn extractConfig + + from ('modules') { + include 'build.gradle' } - // Tweaks to the .iws - workspace.iws.withXml { xmlProvider -> - def iwsNode = xmlProvider.asNode() - ideaMakeAutomatically(iwsNode) - ideaRunConfig(iwsNode) + from ('gradle') { + include '*.gradle' + include 'wrapper/**/*' + include 'metrics/**/*' + include 'templates/**/*' + into 'gradle' } + + from 'gradlew' + from 'gradlew.bat' + from 'groovyw' + from 'groovyw.bat' + from 'module.groovy' + + archiveName = "TerasologyHarness.zip" } -cleanIdea.doLast { - new File('Terasology.iws').delete() -} \ No newline at end of file +afterEvaluate { + // This pops up way too early - after this particular file has been evaluated? Module config logging follows + // Really need a hook to trigger a task's execution phase on any project evaluation + // Difficulty: IntelliJ uses a special entry point (likely Eclipse as well) that just evaluates somehow + //println 'After evaluate (TODO: Trying to find a good point to hook in a prep task)' +} + +gradle.afterProject { + // This triggers *a lot* - after every project is evaluated. So similar to allProjects.afterEvaluate? + //println 'After project (TODO: Trying to find a good point to hook in a prep task)' +} diff --git a/config/gradle/utility.gradle b/config/gradle/utility.gradle deleted file mode 100644 index 5fff457c4c4..00000000000 --- a/config/gradle/utility.gradle +++ /dev/null @@ -1,381 +0,0 @@ -// This extra build file is mainly for structural setup of the project using Git to import other repos from GitHub - -// Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( -buildscript { - repositories { - // External libs - jcenter is Bintray and is supposed to be a superset of Maven Central, but do both just in case - jcenter() - mavenCentral() - } - - dependencies { - // Git plugin for Gradle - classpath 'org.ajoberstar:gradle-git:0.6.3' - } -} - -// Git plugin details at https://github.com/ajoberstar/gradle-git -import org.ajoberstar.gradle.git.tasks.* - -ext { - // TODO: Huh, why can't this be resolved from the main build.gradle anymore? It used to be accessible - templatesDir = 'templates' -} - -// Dynamic task definition for cloning an existing module (with the module name embedded in the task name as ) -// Sample command: gradlew fetchModulePortals -tasks.addRule("Pattern: fetchModule") { String taskName -> - if (taskName.startsWith("fetchModule")) { - - // Here's the actual task definition for each dynamically created task of name taskName - task (taskName, type: GitClone) { - // TODO: This task description does not get listed under the "Rules" section on "gradlew tasks" :-( - description = 'Fetches source for a given module to the local project, cloned from GitHub' - - // Repo name is the dynamic part of the task name - def repo = (taskName - 'fetchModule') - def parentDir = 'modules' - - // Default GitHub account to use. Supply with -PgithubAccount="TargetAccountName" or via gradle.properties - def githubHome = 'Terasology' - - // Command line parameter has precedence - if it is set we do not check gradle.properties - if (project.hasProperty('githubAccount')) { - githubHome = githubAccount - } else if (project.hasProperty('alternativeGithubHome')) { - githubHome =alternativeGithubHome - } - - def destination = file("$parentDir/$repo") - - // Don't clone this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) - enabled = !destination.exists() - println "fetchModule requested for $repo from Github under $githubHome - exists already? " + !enabled - - // Do the actual clone if we don't have the directory already - if (enabled) { - uri = "https://github.com/$githubHome/" + repo + ".git" - println "Fetching $repo from $uri" - destinationPath = destination - bare = false - } - - // Copy in (replace if existing for some reason) a build.gradle from template, plus module.txt if missing - doLast { - File targetBuildGradle = new File(destination, 'build.gradle') - targetBuildGradle.delete() - targetBuildGradle << new File(rootDir, 'modules/Core/build.gradle').text - - File moduleManifest = new File (destination, 'module.txt') - if (!moduleManifest.exists()) { - def moduleText = new File(templatesDir, 'module.txt').text - moduleManifest << moduleText.replaceAll('MODULENAME', repo) - println "WARNING: Module $repo did not have a module.txt! One was created, please review and submit to GitHub" - } - } - } - } -} - -// Dynamic task definition for creating a new module (from scratch, rather than cloning from GitHub) -// Sample command: gradlew createModuleMyStuff -tasks.addRule("Pattern: createModule") { String taskName -> - if (taskName.startsWith("createModule")) { - - // Here's the actual task definition for each dynamically created task of name taskName - task (taskName, type: GitInit) { - description = 'Creates template source for a given module to the local project and preps a local Git repo' - - // Repo name is the dynamic part of the task name - def repo = (taskName - 'createModule') - def parentDir = 'modules' - - // Default GitHub account to use. Supply with -PgithubAccount="TargetAccountName" or via gradle.properties - def githubHome = 'Terasology' - - // Command line parameter has precedence - if it is set we do not check gradle.properties - if (project.hasProperty('githubAccount')) { - githubHome = githubAccount - } else if (project.hasProperty('alternativeGithubHome')) { - githubHome =alternativeGithubHome - } - - def destination = file("$parentDir/$repo") - - // Don't create this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) - enabled = !destination.exists() - println "createModule requested for $repo linked to $githubHome on GitHub - exists already? " + !enabled - - // Do the actual creation if we don't have the directory already - if (enabled) { - - println "Creating $repo locally" - destination.mkdir() - destinationPath = destination - - // TODO: Add in the local mapping to a remote ref definition. Needs support in the Gradle-Git plugin - //uri = "https://github.com/$githubAccount/" + repo + ".git" - bare = false - - // Copy in some template stuff for the new module - doLast { - new File(destination, 'build.gradle') << new File(rootDir, 'modules/Core/build.gradle').text - // TODO : Add in the logback.groovy from engine\src\test\resources\logback.groovy ? Local dev only, Jenkins will use the one inside engine-tests.jar. Also add to .gitignore - def moduleManifest = new File (destination, 'module.txt') - def moduleText = new File(templatesDir, 'module.txt').text - moduleManifest << moduleText.replaceAll('MODULENAME', repo) - new File(destination, '.gitignore') << new File(templatesDir, '.gitignore').text - } - } - } - } -} - -// Dynamic task definition for cloning an existing meta-module (with the meta-module name embedded in the task name as ) -// Note how this one uses a different override name - modules & meta modules share names, so cannot co-exist in same Org -// Sample command: gradlew fetchMetaStructuralResources -tasks.addRule("Pattern: fetchMeta") { String taskName -> - if (taskName.startsWith("fetchMeta")) { - - // Here's the actual task definition for each dynamically created task of name taskName - task (taskName, type: GitClone) { - // TODO: This task description does not get listed under the "Rules" section on "gradlew tasks" :-( - description = 'Fetches source for a given meta-module to the local project, cloned from GitHub' - - // Repo name is the dynamic part of the task name - def repo = (taskName - 'fetchMeta') - def parentDir = 'meta' - - // Default GitHub account to use. Supply with -PgithubMetaAccount="TargetAccountName" or via gradle.properties - def githubHome = 'MetaTerasology' - - // Command line parameter has precedence - if it is set we do not check gradle.properties - if (project.hasProperty('githubMetaAccount')) { - githubHome = githubAccount - } else if (project.hasProperty('alternativeGithubMetaHome')) { - githubHome =alternativeGithubHome - } - - // Since meta-modules only accompany normal modules they need a unique name - def destination = file("$parentDir/$repo" + "Meta") - - // Don't clone this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) - enabled = !destination.exists() - println "fetchMeta requested for $repo from Github under $githubHome - exists already? " + !enabled - - // Do the actual clone if we don't have the directory already - if (enabled) { - uri = "https://github.com/$githubHome/" + repo + ".git" - println "Fetching $repo from $uri" - destinationPath = destination - bare = false - } - } - } -} - -// Dynamic task definition for creating a new meta-module (from scratch, rather than cloning from GitHub) -// Sample command: gradlew createMetaStructuralResources -tasks.addRule("Pattern: createMeta") { String taskName -> - if (taskName.startsWith("createMeta")) { - - // Here's the actual task definition for each dynamically created task of name taskName - task (taskName, type: GitInit) { - description = 'Creates template source for a given meta-module to the local project and preps a local Git repo' - - // Repo name is the dynamic part of the task name + "Meta" appended in the case of meta-modules - def repo = (taskName - 'createMeta') - def parentDir = 'meta' - - // Default GitHub account to use. Supply with -PgithubMetaAccount="TargetAccountName" or via gradle.properties - def githubHome = 'MetaTerasology' - - // Command line parameter has precedence - if it is set we do not check gradle.properties - if (project.hasProperty('githubMetaAccount')) { - githubHome = githubAccount - } else if (project.hasProperty('alternativeGithubMetaHome')) { - githubHome =alternativeGithubHome - } - - // Since meta-modules only accompany normal modules they need a unique name - def destination = file("$parentDir/$repo" + "Meta") - - // Don't create this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) - enabled = !destination.exists() - println "createMeta requested for $repo linked to $githubHome on GitHub - exists already? " + !enabled - - // Do the actual creation if we don't have the directory already - if (enabled) { - - println "Creating $repo locally" - destination.mkdir() - destinationPath = destination - - // TODO: Add in the local mapping to a remote ref definition. Needs support in the Gradle-Git plugin - //uri = "https://github.com/$githubAccount/" + repo + ".git" - bare = false - - // Copy in some template stuff for the new meta-module - doLast { - new File(destination, 'README.markdown') << new File(templatesDir, 'metaREADME.markdown').text.replaceAll('MODULENAME', repo) - new File(destination, '.gitignore') << new File(templatesDir, '.gitignore').text - } - } - } - } -} - -// Dynamic task definition for cloning an existing facade (with the facade name embedded in the task name as ) -// Sample command: gradlew fetchFacadeAndroid -tasks.addRule("Pattern: fetchFacade") { String taskName -> - if (taskName.startsWith("fetchFacade")) { - - // Here's the actual task definition for each dynamically created task of name taskName - task (taskName, type: GitClone) { - description = 'Fetches source for a given facade to the local project, cloned from GitHub' - - // Repo name is the dynamic part of the task name. Facade repos are prefixed with "Facade" so we do "math" - def repo = (taskName - 'fetch') - def localName = (repo - 'Facade') - def parentDir = 'facades' - - println "Repo is $repo - localName is $localName" - - // Default GitHub account to use. Supply with -PgithubAccount="TargetAccountName" or via gradle.properties - def githubHome = 'MovingBlocks' - - // Command line parameter has precedence - if it is set we do not check gradle.properties - if (project.hasProperty('githubAccount')) { - githubHome = githubAccount - } else if (project.hasProperty('alternativeGithubHome')) { - githubHome =alternativeGithubHome - } - - def destination = file("$parentDir/$localName") - - // Don't clone this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) - enabled = !destination.exists() - println "fetchFacade requested for $localName from Github under $githubHome - exists already? " + !enabled - - // Do the actual clone if we don't have the directory already - if (enabled) { - uri = "https://github.com/$githubHome/" + repo + ".git" - println "Fetching $repo from $uri" - destinationPath = destination - bare = false - } - } - } -} - -// Dynamic task definition for creating a new facade (with the facade name embedded in the task name as ) -// Sample command: gradlew createFacadeConsoleX -tasks.addRule("Pattern: createFacade") { String taskName -> - if (taskName.startsWith("createFacade")) { - - // Here's the actual task definition for each dynamically created task of name taskName - task (taskName, type: GitInit) { - description = 'Creates template source for a given facade to the local project and preps a local Git repo' - - // Repo name is the dynamic part of the task name. Facade repos are prefixed with "Facade" so we do "math" - def repo = (taskName - 'create') - def localName = (repo - 'Facade') - def parentDir = 'facades' - - println "Repo is $repo - localName is $localName" - - // Default GitHub account to use. Supply with -PgithubAccount="TargetAccountName" or via gradle.properties - def githubHome = 'MovingBlocks' - - // Command line parameter has precedence - if it is set we do not check gradle.properties - if (project.hasProperty('githubAccount')) { - githubHome = githubAccount - } else if (project.hasProperty('alternativeGithubHome')) { - githubHome =alternativeGithubHome - } - - def destination = file("$parentDir/$localName") - - // Don't create this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) - enabled = !destination.exists() - println "createFacade requested for $localName linked to Github under $githubHome - exists already? " + !enabled - - // Do the actual creation if we don't have the directory already TODO: Doesn't enable the GitHub mapping yet - if (enabled) { - //uri = "https://github.com/$githubHome/" + repo + ".git" - println "Creating $repo locally" // pointed at $uri" - destination.mkdir() - destinationPath = destination - bare = false - - // Copy in some template stuff for the new facade. TODO: This doesn't do a whole lot yet... - doLast { - new File(destination, 'build.gradle') << new File(templatesDir, 'facades.gradle').text - def moduleManifest = new File (destination, 'module.txt') - def moduleText = new File(templatesDir, 'module.txt').text - moduleManifest << moduleText.replaceAll('MODULENAME', repo) - new File(destination, '.gitignore') << '*.iml' - println "*** New facade created but this template is pretty raw! Please review well before using" - } - } - } - } -} - -// Dynamic task for fetching project-related libs -// Sample command: gradlew fetchLibCrashReporter -tasks.addRule("Pattern: fetchLib") { String taskName -> - if (taskName.startsWith("fetchLib")) { - - // Task for fetching our various library projects like TeraBullet - task (taskName, type: GitClone) { - description = 'Fetches source for a given Terasology-related library to the local project, pulled from GitHub' - - // Repo name is the dynamic part of the task name - def repo = (taskName - 'fetchLib') - def parentDir = 'libs' - - // Default to MovingBlocks but set some stuff for known libs - def githubHome = 'MovingBlocks' - - switch (repo) { - case 'TeraBullet' : - // TeraBullet currently uses the "develop" branch - branch = 'develop' - break; - - // Hmm, what to do with casing issues. Camel case gradlew fetchLibJitter is so pretty! But .. jitter - case 'Jitter' : - case 'jitter' : - // Jitter is hosted in the "openleap" org and everything is in lower case, whoops - githubHome = 'openleap' - repo = 'jitter' - break; - case 'Index' : - githubHome = 'Terasology' - break; - } - - // Allow user to override the GitHub account to pull from. Supply with -PgithubAccount="TargetAccountName" - if (project.hasProperty('githubAccount')) { - githubHome = githubAccount - } else if (project.hasProperty('alternativeGithubHome')) { - githubHome =alternativeGithubHome - } - - def destination = file("$parentDir/$repo") - - // Don't clone this repo if we already have a directory by that name (also determines Gradle UP-TO-DATE) - enabled = !destination.exists() - println "fetchLib requested for $repo from Github under $githubHome - exists already? " + !enabled - - // Do the actual clone if we don't have the directory already - if (enabled) { - uri = "https://github.com/$githubHome/" + repo + ".git" - println "Fetching $repo from $uri" - destinationPath = destination - bare = false - } - } - } -} diff --git a/engine-tests/build.gradle b/engine-tests/build.gradle index 9672e2305d0..f2ddacbd75a 100644 --- a/engine-tests/build.gradle +++ b/engine-tests/build.gradle @@ -1,7 +1,7 @@ // Engine tests are split out due to otherwise quirky project dependency issues with module tests extending engine tests // Grab all the common stuff like plugins to use, artifact repositories, code analysis config -apply from: "$rootDir/config/gradle/artifactory.gradle" +apply from: "$rootDir/gradle/artifactory.gradle" import groovy.json.JsonSlurper @@ -50,4 +50,3 @@ dependencies { compile group: 'org.mockito', name: 'mockito-all', version: '1.10.19' compile group: 'org.jboss.shrinkwrap', name: 'shrinkwrap-depchain-java7', version: '1.1.3' } - diff --git a/engine/build.gradle b/engine/build.gradle index d0ae86290c3..6bde7de8f6c 100644 --- a/engine/build.gradle +++ b/engine/build.gradle @@ -6,7 +6,7 @@ plugins { } // Grab all the common stuff like plugins to use, artifact repositories, code analysis config, etc -apply from: "$rootDir/config/gradle/artifactory.gradle" +apply from: "$rootDir/gradle/artifactory.gradle" import groovy.json.JsonSlurper import org.reflections.Reflections; @@ -23,7 +23,8 @@ ext { // Read environment variables, including variables passed by jenkins continuous integration server env = System.getenv() - templatesDir = new File(rootDir, 'templates') + // TODO: Use project.file() instead? Good practice / warning. Can be used as plain file(). But may need one-arg approach? + templatesDir = new File(rootDir, 'gradle/templates') // Stuff for our automatic version file setup startDateTimeString = dateTimeFormat.format(new Date()) @@ -80,10 +81,6 @@ configurations { devCompile.extendsFrom compile } -// TODO: Remove when we don't need to rely on snapshots. Wonder why modules respected this set in root project, engine not so much -configurations.all { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' -} // Primary dependencies definition dependencies { @@ -118,6 +115,17 @@ dependencies { compile group: 'ec.util', name: 'MersenneTwister', version: '20' compile group: 'org.eaxy', name: 'eaxy', version: '0.1' + /* Gradle / project structure quirk: modules sometimes will hook on to an engine snapshot rather than local source + off in the Nanoware fork GSOC happened in parallel, but was merged and published to Artifactory + a Nanoware workspace would then have a module catch on to the upstream engine, lacking telemetry (diff repo) + adding said repo here resolves the resolution issue, but having the actual dependencies clashes with the code + // telemetry + compile (group: 'com.snowplowanalytics', name: 'snowplow-java-tracker', version :'0.8.0'){ + exclude group:'org.slf4j',module:'slf4j-simple' + } + compile group: 'net.logstash.logback', name: 'logstash-logback-encoder', version: '4.10' + */ + // Our developed libs compile group: 'org.terasology', name: 'gestalt-module', version: '5.1.2' compile group: 'org.terasology', name: 'gestalt-asset-core', version: '5.1.2' @@ -150,16 +158,17 @@ task cacheReflections { description = 'Caches reflection output to make regular startup faster. May go stale and need cleanup at times.' inputs.files project.classes.outputs.files dependsOn classes - + doLast { // Without the .mkdirs() we might hit a scenario where the classes dir doesn't exist yet + // TODO: This could be Gradlefied further by declaring inputs/outputs better, maybe a plugin? file(sourceSets.main.output.classesDir.toString()).mkdirs() Reflections reflections = new org.reflections.Reflections(new org.reflections.util.ConfigurationBuilder() .addUrls(configurations.compile.collect { it.toURI().toURL()} + sourceSets.main.output.classesDir.toURI().toURL()) .setScanners(new TypeAnnotationsScanner(), new SubTypesScanner())) reflections.save(sourceSets.main.output.classesDir.toString() + "/reflections.cache") } - + } task cleanReflections(type: Delete) { @@ -220,20 +229,5 @@ jar.finalizedBy cleanReflections // General IDE customization // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -idea { - module { - // Add development "dev" dir - sourceDirs += sourceSets.dev.allJava.srcDirs - - // Change around the output a bit - inheritOutputDirs = false - outputDir = file('build/classes') - testOutputDir = file('build/testClasses') - downloadSources = true - } -} - // Make sure our config file for code analytics get extracted (vulnerability: non-IDE execution of single analytic) -ideaModule.dependsOn rootProject.extractConfig -tasks.eclipse.dependsOn rootProject.extractConfig check.dependsOn rootProject.extractConfig diff --git a/engine/src/main/java/org/terasology/engine/paths/PathManager.java b/engine/src/main/java/org/terasology/engine/paths/PathManager.java index cb5b6cc6ef8..38dd8a5a8df 100644 --- a/engine/src/main/java/org/terasology/engine/paths/PathManager.java +++ b/engine/src/main/java/org/terasology/engine/paths/PathManager.java @@ -65,7 +65,15 @@ private PathManager() { URL urlToSource = PathManager.class.getProtectionDomain().getCodeSource().getLocation(); Path codeLocation = Paths.get(urlToSource.toURI()); - System.out.println("codeLocation: " + codeLocation); + System.out.println("PathManager being initialized. Initial code location is " + codeLocation.toAbsolutePath()); + + // In the case of unit testing in a module the engine jar may be somewhere totally different. We don't want that + if (codeLocation.toString().contains(".gradle") || codeLocation.toString().contains(".m2")) { + System.out.println("PathManager is being initialized out of a jar in the .gradle or .m2 cache. Need better working dir!"); + codeLocation = Paths.get(""); + System.out.println("Hopefully changed it to the right working dir: " + codeLocation.toAbsolutePath()); + } + if (Files.isRegularFile(codeLocation)) { installPath = findNativesHome(codeLocation.getParent(), 5); if (installPath == null) { diff --git a/facades/PC/build.gradle b/facades/PC/build.gradle index 07f216f5787..11d57535184 100644 --- a/facades/PC/build.gradle +++ b/facades/PC/build.gradle @@ -1,7 +1,7 @@ // The PC facade is responsible for the primary distribution - a plain Java application runnable on PCs // Grab all the common stuff like plugins to use, artifact repositories, code analysis config -apply from: "$rootDir/config/gradle/artifactory.gradle" +apply from: "$rootDir/gradle/artifactory.gradle" import org.apache.tools.ant.filters.FixCrLfFilter import java.text.SimpleDateFormat; @@ -17,7 +17,7 @@ ext { // General props mainClassName = 'org.terasology.engine.Terasology' subDirLibs = 'libs' - templatesDir = new File(rootDir, 'templates') + templatesDir = new File("$rootDir/gradle/templates") rootDirDist = new File(rootDir, 'build/distributions') // Read environment variables, including variables passed by jenkins continuous integration server @@ -83,7 +83,7 @@ task game(type:JavaExec) { // Dependencies: natives + all modules & the PC facade itself (which will trigger the engine) dependsOn rootProject.extractNatives - dependsOn rootProject.moduleClasses + // dependsOn rootProject.moduleClasses // TODO: Review? dependsOn classes // Run arguments @@ -104,7 +104,7 @@ task debug(type:JavaExec) { // Dependencies: natives + all modules & the PC facade itself (which will trigger the engine) dependsOn rootProject.extractNatives - dependsOn rootProject.moduleClasses + // dependsOn rootProject.moduleClasses // TODO: Review? dependsOn classes // Run arguments @@ -121,59 +121,63 @@ task debug(type:JavaExec) { classpath project(':engine').configurations.runtime } -// By delaying this task to doLast (the << bit) we don't get the headless server dir set up unless actually wanting it + // TODO: This is not the Gradle Way. Needs more declared output-fu to determine up-to-date instead of the if -task setupServerConfig() << { +task setupServerConfig() { description "Parses parameters passed via Gradle and writes them to the local run-from-source server dir's config.cfg" - def json = new JsonBuilder() + doLast { + def json = new JsonBuilder() - def serverRoot = rootProject.file(localServerDataPath); - def config = new File(serverRoot, 'config.cfg') + def serverRoot = rootProject.file(localServerDataPath); + def config = new File(serverRoot, 'config.cfg') - if (!config.exists()) { + if (!config.exists()) { - serverRoot.mkdir() - logger.lifecycle("Creating config file $config") + serverRoot.mkdir() + logger.lifecycle("Creating config file $config") - json { - worldGeneration { - if (project.hasProperty('seed')) { - logger.lifecycle(" Seed value: $seed"); - defaultSeed seed - } - if (project.hasProperty('worldGen')) { - logger.lifecycle(" World Generator: $worldGen"); - defaultGenerator worldGen + json { + worldGeneration { + if (project.hasProperty('seed')) { + logger.lifecycle(" Seed value: $seed"); + defaultSeed seed + } + if (project.hasProperty('worldGen')) { + logger.lifecycle(" World Generator: $worldGen"); + defaultGenerator worldGen + } } - } - defaultModSelection { - if (project.hasProperty('extraModules')) { - logger.lifecycle(" Enabling modules: $extraModules"); - modules extraModules.tokenize(" ,") + defaultModSelection { + if (project.hasProperty('extraModules')) { + logger.lifecycle(" Enabling modules: $extraModules"); + modules extraModules.tokenize(" ,") + } } } + config.text = json.toPrettyString() } - config.text = json.toPrettyString() } } // TODO: Seems to always be up to date so no modules get copied -task setupServerModules(type: Sync) << { +task setupServerModules(type: Sync) { description 'Parses "extraModules" - a comma-separated list of modules and puts them into ' + localServerDataPath - if (project.hasProperty('extraModules')) { - // Grab modules from Artifactory - cheats by declaring them as dependencies - extraModules.tokenize(' ,').each { String module -> - println "Extra module: " + module - dependencies { - modules group: 'org.terasology.modules', name: module, version: '+', changing: 'true' + doLast { + if (project.hasProperty('extraModules')) { + // Grab modules from Artifactory - cheats by declaring them as dependencies + extraModules.tokenize(' ,').each { String module -> + println "Extra module: " + module + dependencies { + modules group: 'org.terasology.modules', name: module, version: '+', changing: 'true' + } } } - } - from(configurations.modules) - into(new File(rootProject.file(localServerDataPath), "modules")) + from(configurations.modules) + into(new File(rootProject.file(localServerDataPath), "modules")) + } } // TODO: Make a task to reset server / game data @@ -182,7 +186,7 @@ task server(type:JavaExec) { // Dependencies: natives + all modules & the PC facade itself (which will trigger the engine) dependsOn rootProject.extractNatives - dependsOn rootProject.moduleClasses + // dependsOn rootProject.moduleClasses // TODO: Review? dependsOn classes dependsOn setupServerConfig dependsOn setupServerModules @@ -251,24 +255,19 @@ task distApp (type: Sync) { } } -// This is a TEMPORARY tweak to make "changing" dependencies always ('0') check for newer snapshots available -// TODO: Remove this when versioning and promotion works fully, then we shouldn't care about snapshots normally anyway -configurations.all { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' -} - // Distribute modules - only grabs Core in Jenkins but locally will grab any present. "Distros" now handle Jenkins packs task distModules (type: Sync) { description = "Prepares local modules for distribution" dependsOn distApp - dependsOn rootProject.moduleJars - + // dependsOn rootProject.moduleJars // TODO: Review. Doesn't seem to work anymore anyway +/* // So this is probably a hack, but it works ;-) It does not work if it is in distApp, default "into" quirk ? into("$distsDir/app/modules") rootProject.terasologyModules().each { from "$rootDir/modules/${it.name}/build/libs" include "*.jar" } + */ } task distPCZip (type: Zip) { @@ -289,28 +288,3 @@ task distForLauncher (type: Sync) { } } } - -// Prep an IntelliJ module for the facade -idea { - module { - // Change around the output a bit - inheritOutputDirs = false - outputDir = file('build/classes') - testOutputDir = file('build/testClasses') - } -} - -task copyEclipseLauncher(type: Copy) { - from "$rootDir/config/eclipse" - into projectDir - include("Terasology.launch") -} - -tasks.eclipse { - dependsOn copyEclipseLauncher - dependsOn rootProject.extractNatives -} - -cleanEclipse.doLast { - new File(projectDir, "Terasology.launch").delete() -} diff --git a/facades/TeraEd/build.gradle b/facades/TeraEd/build.gradle index c7e0e5e99c5..04ec4fcd61d 100644 --- a/facades/TeraEd/build.gradle +++ b/facades/TeraEd/build.gradle @@ -1,7 +1,7 @@ // The Editor facade is responsible for the (shader) editor - a plain Java application runnable on PCs // Grab all the common stuff like plugins to use, artifact repositories, code analysis config -apply from: "$rootDir/config/gradle/artifactory.gradle" +apply from: "$rootDir/gradle/artifactory.gradle" // Base the engine tests on the same version number as the engine version = project(':engine').version @@ -19,7 +19,7 @@ task editor(type:JavaExec) { // Dependencies: natives + all modules & the PC facade itself (which will trigger the engine) dependsOn rootProject.extractNatives - dependsOn rootProject.moduleClasses + // dependsOn rootProject.moduleClasses // TODO: Review? dependsOn classes // Run arguments @@ -34,13 +34,3 @@ task editor(type:JavaExec) { classpath project(':engine').sourceSets.main.output.classesDir classpath project(':engine').configurations.runtime } - -// Prep an IntelliJ module for the facade -idea { - module { - // Change around the output a bit - inheritOutputDirs = false - outputDir = file('build/classes') - testOutputDir = file('build/testClasses') - } -} diff --git a/config/gradle/artifactory.gradle b/gradle/artifactory.gradle similarity index 85% rename from config/gradle/artifactory.gradle rename to gradle/artifactory.gradle index 66e5d1f6354..4f026f65d71 100644 --- a/config/gradle/artifactory.gradle +++ b/gradle/artifactory.gradle @@ -1,12 +1,29 @@ // This include file applies our Artifactory related settings to places that need them (root project + modules) // It is a superset of the the stuff common to Java sub projects so we include the common.gradle here -apply from: "$rootDir/config/gradle/common.gradle" +apply from: "$rootDir/gradle/common.gradle" // Artifactory publishing requires these (without maven we'd get no .pom with dependency info published) apply plugin: 'maven' apply plugin: 'com.jfrog.artifactory' apply plugin: 'maven-publish' +// Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( +buildscript { + repositories { + // External libs - jcenter is Bintray and is supposed to be a superset of Maven Central, but do both just in case + jcenter() + } + + dependencies { + // Artifactory plugin + classpath(group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '4.0.0') + + // Needed for caching reflected data during builds + classpath 'org.reflections:reflections:0.9.10' + classpath 'dom4j:dom4j:1.6.1' + } +} + // This configures what we need to interact with the MovingBlocks Artifactory instance. It is primarily for local use. // In Jenkins the Artifactory plugin can supply overrides for the following values artifactory { @@ -90,4 +107,4 @@ artifactoryPublish { dependsOn jar, sourceJar, javadocJar // TODO: After Gradle 2.3 to 2.10 upgrade have to use `gradlew generatePomFileForMavenJavaPublication artifactoryPublish` // The generation task doesn't happen on its own yet can't be set as a dependsOn as it isn't available yet? -} \ No newline at end of file +} diff --git a/config/gradle/common.gradle b/gradle/common.gradle similarity index 89% rename from config/gradle/common.gradle rename to gradle/common.gradle index a20cb1e06c7..4be54ff63c1 100644 --- a/config/gradle/common.gradle +++ b/gradle/common.gradle @@ -1,8 +1,6 @@ // This include file is meant for each Java project/module and contains setup for artifact repos, code analysis, etc apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' apply plugin: 'project-report' apply plugin: 'checkstyle' apply plugin: 'pmd' @@ -34,11 +32,18 @@ repositories { if (rootProject.hasProperty("alternativeResolutionRepo")) { // If the user supplies an alternative repo via gradle.properties then use that url alternativeResolutionRepo + println "common.gradle picked up a custom resolution repo: $alternativeResolutionRepo" } else { // Our default is the main virtual repo containing everything except repos for testing Artifactory itself url "http://artifactory.terasology.org/artifactory/virtual-repo-live" + println "common.gradle is set to use the standard resolution (virtual-repo-live)" } } + + // snowplow for telemetry + maven { + url "http://maven.snplow.com/releases" + } } dependencies { @@ -102,13 +107,13 @@ jacocoTestReport { // TODO: Maybe update other projects like modules to pull the zipped dependency so fewer quirks are needed in Jenkins checkstyle { ignoreFailures = true - configFile = new File(rootDir, 'config/metrics/checkstyle/checkstyle.xml') + configFile = new File(rootDir, 'gradle/metrics/checkstyle/checkstyle.xml') configProperties.samedir = checkstyle.configFile.parentFile } pmd { ignoreFailures = true - ruleSetFiles = files("$rootDir/config/metrics/pmd/pmd.xml") + ruleSetFiles = files("$rootDir/gradle/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 = [] } @@ -116,10 +121,10 @@ pmd { findbugs { ignoreFailures = true toolVersion = '3.0.1' - excludeFilter = new File(rootDir, "config/metrics/findbugs/findbugs-exclude.xml") + excludeFilter = new File(rootDir, "gradle/metrics/findbugs/findbugs-exclude.xml") } // TODO: Temporary until javadoc has been fixed for Java 8 everywhere javadoc { failOnError = false -} \ No newline at end of file +} diff --git a/config/eclipse/.importorder b/gradle/eclipse/.importorder similarity index 100% rename from config/eclipse/.importorder rename to gradle/eclipse/.importorder diff --git a/config/eclipse/Terasology.launch b/gradle/eclipse/Terasology.launch similarity index 100% rename from config/eclipse/Terasology.launch rename to gradle/eclipse/Terasology.launch diff --git a/config/eclipse/TerasologyAWT.launch b/gradle/eclipse/TerasologyAWT.launch similarity index 100% rename from config/eclipse/TerasologyAWT.launch rename to gradle/eclipse/TerasologyAWT.launch diff --git a/config/eclipse/codetemplates.xml b/gradle/eclipse/codetemplates.xml similarity index 100% rename from config/eclipse/codetemplates.xml rename to gradle/eclipse/codetemplates.xml diff --git a/config/eclipse/formatting.xml b/gradle/eclipse/formatting.xml similarity index 100% rename from config/eclipse/formatting.xml rename to gradle/eclipse/formatting.xml diff --git a/config/eclipse/templates.xml b/gradle/eclipse/templates.xml similarity index 100% rename from config/eclipse/templates.xml rename to gradle/eclipse/templates.xml diff --git a/config/gradle/ide.gradle b/gradle/ide.gradle similarity index 98% rename from config/gradle/ide.gradle rename to gradle/ide.gradle index 31032e9db72..7c94357e869 100644 --- a/config/gradle/ide.gradle +++ b/gradle/ide.gradle @@ -12,14 +12,14 @@ ext { def option = builder.option(name: 'configuration') { map { entry(key: 'active-configuration', - value: 'PROJECT_RELATIVE:$PROJECT_DIR$/config/metrics/checkstyle/checkstyle.xml:Terasology CheckStyle') + value: 'PROJECT_RELATIVE:$PROJECT_DIR$/gradle/metrics/checkstyle/checkstyle.xml:Terasology CheckStyle') entry(key: 'check-nonjava-files', value: false) entry(key: 'check-test-classes', value: true) entry(key: 'location-0', value: 'CLASSPATH:/sun_checks.xml:The default CheckStyle rules') entry(key: 'location-1', - value: 'PROJECT_RELATIVE:$PROJECT_DIR$/config/metrics/checkstyle/checkstyle.xml:Terasology CheckStyle') - entry(key: 'property-1.samedir', value: 'config/metrics/checkstyle') + value: 'PROJECT_RELATIVE:$PROJECT_DIR$/gradle/metrics/checkstyle/checkstyle.xml:Terasology CheckStyle') + entry(key: 'property-1.samedir', value: 'gradle/metrics/checkstyle') entry(key: 'suppress-errors', value: false) entry(key: 'thirdparty-classpath', value: '') } diff --git a/gradle/java.gradle b/gradle/java.gradle new file mode 100644 index 00000000000..01c05479480 --- /dev/null +++ b/gradle/java.gradle @@ -0,0 +1,61 @@ +// This Gradle file is an include file for modules wanting to run in plain old Java + +// Grab all the common stuff like plugins to use, artifact repositories, code analysis config, Artifactory settings, Git magic +apply from: "$rootDir/gradle/artifactory.gradle" + +// Grab the common stuff specific to modules builds (allows it to live in a module specific but language agnostic file) +apply from: "$rootDir/gradle/module.gradle" + +// Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( +buildscript { + repositories { + // External libs - jcenter is Bintray and is supposed to be a superset of Maven Central, but do both just in case + jcenter() + } + + dependencies { + // Artifactory plugin + classpath(group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '4.0.0') + + // Needed for caching reflected data during builds + classpath 'org.reflections:reflections:0.9.10' + classpath 'dom4j:dom4j:1.6.1' + } +} + +ext { + // For the Java modules we add these two additional dirs to the module directory structure + moduleSkeletonDirs << 'src/main/java' << 'src/test/java' +} + +sourceSets { + main { + java { + output.classesDir 'build/classes' + } + } +} + +// Generate the module directory structure if missing (by declaring the task here we also get the language specific extras) +task createSkeleton() { + moduleSkeletonDirs.each { + mkdir it + } +} + +jar { + // Make sure the assets directory is included + dependsOn cacheReflections + dependsOn syncAssets + dependsOn syncOverrides + dependsOn syncDeltas + + // Jarring needs to copy module.txt and all the assets into the output + doFirst { + copy { + from 'module.txt' + into 'build/classes' + } + } +} +jar.finalizedBy cleanReflections diff --git a/config/gradle/jenkins.groovy b/gradle/jenkins.groovy similarity index 100% rename from config/gradle/jenkins.groovy rename to gradle/jenkins.groovy diff --git a/gradle/kotlin.gradle b/gradle/kotlin.gradle new file mode 100644 index 00000000000..039a5eb6ba5 --- /dev/null +++ b/gradle/kotlin.gradle @@ -0,0 +1,87 @@ +// This Gradle file is an include file for modules wanting to run in Kotlin +apply plugin: "kotlin" + +// Grab all the common stuff like plugins to use, artifact repositories, code analysis config, etc +apply from: "$rootDir/gradle/artifactory.gradle" + +// Grab the module specific build logic that's language agnostic +apply from: "$rootDir/gradle/module.gradle" + +// Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( +buildscript { + repositories { + jcenter() + } + + dependencies { + // Artifactory plugin + classpath(group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '4.0.0') + + // Needed for caching reflected data during builds + classpath 'org.reflections:reflections:0.9.10' + classpath 'dom4j:dom4j:1.6.1' + + // Needed for modules in Kotlin + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.6" + } +} + +ext { + // For the Kotlin modules we add these two additional dirs to the module directory structure + moduleSkeletonDirs << 'src/main/kotlin' << 'src/test/kotlin' +} + +sourceSets { + main { + kotlin { + output.classesDir 'build/classes' + } + } +} + +configurations { + kotlinConf.extendsFrom compile + kotlinTestConf.extendsFrom testCompile + kotlinEmbedded +} + +dependencies { + // Since the main compile and compileTest configs are defined in the generic module.gradle we can't modify them here - so extend instead + kotlinConf group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: '1.0.6' + kotlinConf group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: '1.0.6' + + kotlinTestConf group: 'org.jetbrains.kotlin', name: 'kotlin-test:1.0.6' + kotlinTestConf group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit:1.0.6' + + // To make this module bundle Kotlin's runtime jar we put it in a separate config by itself + // TODO: As noted below as well this could for space reasons only be done for the KotlinLib module + // Could also consider skipping the KotlinLib dependency set in module.txt for others (the lang field forces it) + // In that case we may need to keep in mind the versioning of Kotlin - should modules be allowed to pick? + kotlinEmbedded group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: '1.0.6' +} + +// Generate the module directory structure if missing (by declaring the task here we also get the language specific extras) +task createSkeleton() { + moduleSkeletonDirs.each { + mkdir it + } +} + +// TODO: Only embed Kotlin for the KotlinLib module - otherwise rely on a dependency declared on KotlinLib +jar { + // Make sure the assets directory is included + dependsOn cacheReflections + dependsOn syncAssets + dependsOn syncOverrides + dependsOn syncDeltas + + // Jarring needs to copy module.txt and all the assets into the output + doFirst { + copy { + from 'module.txt' + from configurations.kotlinEmbedded + into 'build/classes' + } + } +} +jar.finalizedBy cleanReflections diff --git a/modules/BuilderSampleGameplay/build.gradle b/gradle/module.gradle similarity index 61% rename from modules/BuilderSampleGameplay/build.gradle rename to gradle/module.gradle index db7ab6bcf3d..de8b46bc7a6 100644 --- a/modules/BuilderSampleGameplay/build.gradle +++ b/gradle/module.gradle @@ -1,54 +1,32 @@ -// Simple build file for modules - the one under the Core module is the template, will be copied as needed to modules - -// Grab all the common stuff like plugins to use, artifact repositories, code analysis config, Artifactory settings, Git magic -apply from: "$rootDir/config/gradle/artifactory.gradle" +// This file holds language-generic Gradle stuff for modules. It is meant to be included by the language specific Gradle files import groovy.json.JsonSlurper -import java.text.SimpleDateFormat; - -// Git plugin details at https://github.com/ajoberstar/gradle-git -import org.ajoberstar.gradle.git.tasks.* -import org.reflections.Reflections; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; -import org.reflections.util.ConfigurationBuilder; -// Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( buildscript { repositories { // External libs - jcenter is Bintray and is supposed to be a superset of Maven Central, but do both just in case jcenter() - mavenCentral() } dependencies { // Artifactory plugin classpath(group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '4.0.0') - // Git plugin for Gradle - classpath 'org.ajoberstar:gradle-git:0.6.3' - // Needed for caching reflected data during builds classpath 'org.reflections:reflections:0.9.10' classpath 'dom4j:dom4j:1.6.1' } } -ext { - // Read environment variables, including variables passed by jenkins continuous integration server - env = System.getenv() -} +import org.reflections.Reflections; +import org.reflections.scanners.SubTypesScanner; +import org.reflections.scanners.TypeAnnotationsScanner; +import org.reflections.util.ConfigurationBuilder; def moduleDepends = []; def moduleFile = file('module.txt') -// The module file should always exist if the module was correctly created or cloned using Gradle -if (!moduleFile.exists()) { - println "Y U NO EXIST MODULE.TXT!" - throw new GradleException("Failed to find module.txt for " + project.name) -} - -//println "Scanning for dependencies in module.txt for " + project.name +println "Scanning for dependencies in module.txt for " + project.name def slurper = new JsonSlurper() def moduleConfig = slurper.parseText(moduleFile.text) for (dependency in moduleConfig.dependencies) { @@ -56,6 +34,7 @@ for (dependency in moduleConfig.dependencies) { moduleDepends += dependency.id } } +println "Dependencies: $moduleDepends" // Gradle uses the magic version variable when creating the jar name (unless explicitly set somewhere else I guess) version = moduleConfig.version @@ -65,16 +44,41 @@ group = 'org.terasology.modules' println "Version for $project.name loaded as $version for group $group" -// TODO: Remove when we don't need to rely on snapshots. Needed here for solo builds in Jenkins -configurations.all { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' +ext { + // The base set of directories potentially needed in a module. Language Gradle files add their unique extras + moduleSkeletonDirs = ['assets', + 'assets/animations', + 'assets/atlas', + 'assets/behaviors', + 'assets/blocks', + 'assets/blockSounds', + 'assets/blockTiles', + 'assets/fonts', + 'assets/i18n', + 'assets/materials', + 'assets/mesh', + 'assets/music', + 'assets/prefabs', + 'assets/shaders', + 'assets/shapes', + 'assets/skeletalMesh', + 'assets/skins', + 'assets/sounds', + 'assets/textures', + 'assets/ui', + 'overrides', + 'deltas'] } // Set dependencies. Note that the dependency information from module.txt is used for other Terasology modules dependencies { + // TODO: Add a new Gradle plugin that itself is responsible for scanning module.txt (actually module.info.json or so) + // It should contain a method that can be triggered here that will apply the appropriate dependencies for the given module + // Ideally it could also handle the source vs binary plus outputting nice build info + // Check to see if this module is not the root Gradle project - if so we are in a multi-project workspace if (project.name != project(':').name) { - println "\nProcessing module '$project.name' in a multi-project workspace" + println "Processing module '$project.name' in a multi-project workspace" // Dependency on the engine itself (actually its built jar file) compile project(':engine') @@ -112,8 +116,14 @@ dependencies { } } + // This step resolves artifacts early, after which project config CANNOT be altered again! - configurations.compile.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact -> + //TODO: This is what causes the nested task slowdown, due to the resolve at config time issue + //configurations.compile.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact -> + // TODO: This updated version seems to do the trick without a slowdown! + // However, this exact variant causes an error on Gradle refresh within IntelliJ ... the testCompile doesn't? IntelliJ bug? + configurations.compile.incoming.afterResolve { ResolvedArtifact artifact -> + def id = artifact.moduleVersion.id // Check for any needed module dependencies on other modules that we need at runtime @@ -135,6 +145,9 @@ dependencies { } } } + + + } else { println "We're in a single-project non-Core module workspace (Jenkins) so will look elsewhere for dependencies" @@ -155,52 +168,18 @@ dependencies { // TODO: parse and apply external lib dependencies if any are present // TODO: Consider / keep an eye on whether resolving artifacts early at this point causes any trouble (is only for logging) // This step resolves artifacts (both compile & testCompile) and prints out some interesting versions - configurations.testCompile.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact -> + //configurations.testCompile.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact -> + configurations.testCompile.incoming.afterResolve { ResolvedArtifact artifact -> // Fixed version def id = artifact.moduleVersion.id // Print out module (and engine stuff) dependencies and the exact versions they resolved at if (id.group.startsWith('org.terasology')) { println "*** $project.name remotely resolved $id.group - $id.name - version $id.version" } } - } -} -// Change the output dir of each module -sourceSets { - main { - java { - output.classesDir 'build/classes' - } } } -// Generate the module directory structure if missing -task createSkeleton() { - mkdir('assets') - mkdir('assets/animations') - mkdir('assets/atlas') - mkdir('assets/behaviors') - mkdir('assets/blocks') - mkdir('assets/blockSounds') - mkdir('assets/blockTiles') - mkdir('assets/fonts') - mkdir('assets/materials') - mkdir('assets/mesh') - mkdir('assets/music') - mkdir('assets/prefabs') - mkdir('assets/shaders') - mkdir('assets/shapes') - mkdir('assets/skeletalMesh') - mkdir('assets/skins') - mkdir('assets/sounds') - mkdir('assets/textures') - mkdir('assets/ui') - mkdir('overrides') - mkdir('deltas') - mkdir('src/main/java') - mkdir('src/test/java') -} - task cacheReflections { description = 'Caches reflection output to make regular startup faster. May go stale and need cleanup at times.' // TODO: The extra "org" qualifier excludes test classes otherwise sucked up in Jenkins, causing issues. Redo later @@ -238,64 +217,3 @@ task syncDeltas(type: Sync) { from 'deltas' into 'build/classes/deltas' } - -// Instructions for packaging a jar file - is a manifest even needed for modules? -jar { - // Make sure the assets directory is included - dependsOn cacheReflections - dependsOn syncAssets - dependsOn syncOverrides - dependsOn syncDeltas - - // Jarring needs to copy module.txt and all the assets into the output - doFirst { - copy { - from 'module.txt' - into 'build/classes' - } - } -} - -jar.finalizedBy cleanReflections - -// Prep an IntelliJ module for the Terasology module - yes, might want to read that twice :D -idea { - module { - // Change around the output a bit - 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') - } -} - -// Utility task to update the module (except Core) via Git - not tested with local changes present, may cause trouble -task (updateModule, type: GitPull) { - description = 'Updates source for the module from its home (most likely GitHub)' - - // Base whether the task executes on two things - // 1 - we actually asked for it ("gradlew updateModule") - otherwise we don't want it to run TODO: Test for abbreviations? - // 2 - this is not the Core module, which lives with the engine and needs no updates - boolean enabled = "updateModule" in project.gradle.startParameter.taskNames && !project.name.equals("Core") - // TODO: Used to cheat with declaring tasks using << but that defers everything (including config) to execution phase - // Some tasks do not work that way, this one would ALWAYS go with default (root git repo) in that case - // Probably should go through other stuff and use this strategy instead of << - - // Only if we asked for it (and not Core) do we actually configure the repo path and log that we're updating - if (enabled) { - - // This is the Git repo we're actually using - projectDir is specific to the executing project, a.k.a. module whatever - // If not enabled the default is the root project's Git dir, which is a valid Git dir - // However in the case of Core we'd be setting ain invalid Git dir which causes fail - so this avoids that - repoPath = projectDir - - println "Pulling updates via Git to " + getRepoDir() + ", if dependencies change run Gradle again (like 'gradlew idea')" - } -} diff --git a/gradle/scala.gradle b/gradle/scala.gradle new file mode 100644 index 00000000000..43d739f5558 --- /dev/null +++ b/gradle/scala.gradle @@ -0,0 +1,82 @@ +// This Gradle file is an include file for modules wanting to run in Scala +apply plugin: "scala" + +// Grab all the common stuff like plugins to use, artifact repositories, code analysis config, etc +apply from: "$rootDir/gradle/artifactory.gradle" + +// Grab the module specific build logic that's language agnostic +apply from: "$rootDir/gradle/module.gradle" + +// Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( +buildscript { + repositories { + jcenter() + } + + dependencies { + // Artifactory plugin + classpath(group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '4.0.0') + + // Needed for caching reflected data during builds + classpath 'org.reflections:reflections:0.9.10' + classpath 'dom4j:dom4j:1.6.1' + } +} + +ext { + // For the Scala modules we add these two additional dirs to the module directory structure + moduleSkeletonDirs << 'src/main/scala' << 'src/test/scala' +} + +sourceSets { + main { + scala { + output.classesDir 'build/classes' + } + } +} + +configurations { + scalaConf.extendsFrom compile + scalaEmbedded +} + +dependencies { + // Since the main compile and compileTest configs are defined in the generic module.gradle we can't modify them here - so extend instead + scalaConf group: 'org.scala-lang', name: 'scala-reflect', version: '2.11.11' + scalaConf group: 'org.scala-lang', name: 'scala-compiler', version: '2.11.11' + + // To make this module bundle Scala's runtime jar we put it in a separate config by itself + scalaEmbedded group: 'org.scala-lang', name: 'scala-library', version: '2.11.11' +} + +// Generate the module directory structure if missing (by declaring the task here we also get the language specific extras) +task createSkeleton() { + moduleSkeletonDirs.each { + mkdir it + } +} + +// TODO: Only embed Scala for the ScalaLib module - otherwise rely on a dependency declared on ScalaLib +jar { + // Make sure the assets directory is included + dependsOn cacheReflections + dependsOn syncAssets + dependsOn syncOverrides + dependsOn syncDeltas + + // Jarring needs to copy module.txt and all the assets into the output + doFirst { + copy { + from 'module.txt' + from configurations.scalaEmbedded + into 'build/classes' + } + } +} +jar.finalizedBy cleanReflections + +// Needed for Scala for some reason - Kotlin seems to pick up on the custom configurations on its own but here we have to be explicit +tasks.withType(ScalaCompile) { + scalaClasspath = configurations.scalaConf +} diff --git a/gradle/templates/.gitignore b/gradle/templates/.gitignore new file mode 100644 index 00000000000..d9bd3a40c52 --- /dev/null +++ b/gradle/templates/.gitignore @@ -0,0 +1,15 @@ +# Modules do not need and are not allowed a build file +build.gradle + +# IntelliJ +*.iml + +# Eclipse +.checkstyle +.classpath +.project +.settings +bin/ +build/ + +#TODO: Add new entries expected when the build harness has been added to a module dir diff --git a/templates/VERSION b/gradle/templates/VERSION similarity index 100% rename from templates/VERSION rename to gradle/templates/VERSION diff --git a/templates/facades.gradle b/gradle/templates/facades.gradle similarity index 74% rename from templates/facades.gradle rename to gradle/templates/facades.gradle index a3bc72d6206..1d92a1bef84 100644 --- a/templates/facades.gradle +++ b/gradle/templates/facades.gradle @@ -13,6 +13,7 @@ repositories { // MovingBlocks Artifactory instance for libs not readily available elsewhere plus our own libs maven { url "http://artifactory.terasology.org/artifactory/repo" + println "facades.gradle (the template? huh) was reached with standard 'repo' for resolution" } } @@ -22,11 +23,11 @@ dependencies { checkstyle { ignoreFailures = true - configFile = new File(rootDir, 'config/checkstyle/checkstyle.xml') + configFile = new File(rootDir, 'gradle/metrics/checkstyle/checkstyle.xml') configProperties.samedir = checkstyle.configFile.parentFile } pmd { ignoreFailures = true - ruleSetFiles = files("$rootDir/config/pmd/pmd.xml") + ruleSetFiles = files("$rootDir/gradle/metrics/pmd/pmd.xml") } diff --git a/templates/gradle.properties b/gradle/templates/gradle.properties similarity index 92% rename from templates/gradle.properties rename to gradle/templates/gradle.properties index ecfaed08739..3cfb08eb3a1 100644 --- a/templates/gradle.properties +++ b/gradle/templates/gradle.properties @@ -1,7 +1,9 @@ # These settings can speed up the execution of Gradle (and some may be on by default anyway in newer Gradle versions) org.gradle.daemon=true -org.gradle.parallel = true -org.gradle.configureondemand=true +org.gradle.parallel = false +org.gradle.configureondemand=false +org.gradle.console=verbose + # Alternative resolution repo to use in general (not an Artifactory setting, but a more general Maven repo definition) # alternativeResolutionRepo=http://artifactory.terasology.org/artifactory/virtual-nanoware-and-remote @@ -34,4 +36,4 @@ org.gradle.configureondemand=true # If you want to work under a different GitHub account/org you can set it here # There are two properties split out as modules & meta modules have the same name, so they can't share user/org # alternativeGithubHome=Nanoware -# alternativeGithubMetaHome=Nanoware \ No newline at end of file +# alternativeGithubMetaHome=Nanoware diff --git a/templates/metaREADME.markdown b/gradle/templates/metaREADME.markdown similarity index 100% rename from templates/metaREADME.markdown rename to gradle/templates/metaREADME.markdown diff --git a/templates/module.txt b/gradle/templates/module.txt similarity index 57% rename from templates/module.txt rename to gradle/templates/module.txt index 29d2f183f1c..18b2de98bdd 100644 --- a/templates/module.txt +++ b/gradle/templates/module.txt @@ -1,9 +1,9 @@ { "id" : "MODULENAME", - "version" : "0.1.0-SNAPSHOT", - "author" : "", + "version" : "VERSION", + "author" : "AUTHOR", "displayName" : "MODULENAME", - "description" : "", + "description" : "DESCRIPTION", "dependencies" : [], "serverSideOnly" : false } \ No newline at end of file diff --git a/templates/override.cfg b/gradle/templates/override.cfg similarity index 100% rename from templates/override.cfg rename to gradle/templates/override.cfg diff --git a/templates/version.txt b/gradle/templates/version.txt similarity index 100% rename from templates/version.txt rename to gradle/templates/version.txt diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 941144813d2..31b06f23403 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ce781878fbc..001744d9136 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Dec 31 10:21:27 CET 2015 +#Fri Jan 27 18:22:28 EST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip diff --git a/gradle/wrapper/groovy-wrapper.jar b/gradle/wrapper/groovy-wrapper.jar new file mode 100644 index 00000000000..dc694676103 Binary files /dev/null and b/gradle/wrapper/groovy-wrapper.jar differ diff --git a/gradlew b/gradlew index 9d82f789151..4453ccea33d 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282aa68..f9553162f12 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/groovyw b/groovyw new file mode 100644 index 00000000000..757080f7b48 --- /dev/null +++ b/groovyw @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar:$APP_HOME/gradle/wrapper/groovy-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GroovyWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/groovyw.bat b/groovyw.bat new file mode 100644 index 00000000000..c437ccabc53 --- /dev/null +++ b/groovyw.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Groovy Wrapper version of the Gradle Wrapper +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar;%APP_HOME%\gradle\wrapper\groovy-wrapper.jar; + +@rem Execute Groovy via the Gradle Wrapper +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GroovyWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/module.groovy b/module.groovy new file mode 100644 index 00000000000..1bd94b5a7e2 --- /dev/null +++ b/module.groovy @@ -0,0 +1,284 @@ +// We use GrGit for interacting with Git. This gets a hold of it as a dependency like Gradle would +// TODO: Consider if we should do something to fix/suppress the SLF4J warning that gets logged on first usage? +@GrabResolver(name = 'jcenter', root = 'http://jcenter.bintray.com/') +@Grab(group='org.ajoberstar', module='grgit', version='1.9.3') +import org.ajoberstar.grgit.Grgit + +import groovy.json.JsonSlurper + +// Grab override properties from the gradle.properties file (shared with various Gradle commands) +Properties properties = new Properties() +new File("gradle.properties").withInputStream { + properties.load(it) +} +//println "Properties: " + properties + +// Groovy Elvis operator woo! Defaults to "Terasology" if an override isn't set +githubHome = properties.alternativeGithubHome ?: "Terasology" + +//println "githubHome is: $githubHome" + +// For keeping a list of modules retrieved so far +modulesRetrieved = [] + +// Module dependencies we don't want to retrieve as they live in the main Terasology repo +excludedDependencies = ["engine", "Core", "CoreSampleGameplay", "BuilderSampleGameplay"] + +/** + * Primary entry point for retrieving modules, kicks off recursively if needed. + * @param modules the modules we want to retrieve + * @param recurse whether to also retrieve dependencies of the desired modules + */ +def retrieve(String[] modules, boolean recurse) { + println "Now inside retrieve, user (recursively? $recurse) wants: $modules" + for (String module : modules) { + println "Starting loop for module $module, are we recursing? $recurse" + println "Modules retrieved so far: $modulesRetrieved" + retrieveModule(module, recurse) + //println "Modules retrieved after recent addition(s): modulesRetrieved" + } +} + +/** + * Retrieves a single module via Git Clone. Considers whether it exists locally first or if it has already been retrieved this execution. + * @param module the target module to retrieve + * @param recurse whether to also retrieve its dependencies (if so then recurse back into retrieve) + */ +def retrieveModule(String module, boolean recurse) { + File targetDir = new File("modules/$module") + println "Request to retrieve module $module would store it at $targetDir - exists? " + targetDir.exists() + + if (targetDir.exists()) { + println "That module already had an existing directory locally. If something is wrong with it please delete and try again" + modulesRetrieved << module + } else if (modulesRetrieved.contains(module)) { + println "We already retrieved $module - skipping" + } else { + println "Retrieving module $module - if it doesn't appear to exist (typo for instance) you'll get an auth prompt (in case it is private)" + //noinspection GroovyAssignabilityCheck - GrGit has its own .clone but a warning gets issued for Object.clone + Grgit.clone dir: targetDir, uri: "https://github.com/$githubHome/${module}.git" + modulesRetrieved << module + + File moduleManifest = new File(targetDir, 'module.txt') + if (!moduleManifest.exists()) { + def moduleText = new File("gradle/templates/module.txt").text + moduleManifest << moduleText.replaceAll('MODULENAME', module) + println "WARNING: Module $module did not have a module.txt! One was created, please review and submit to GitHub" + } + + // Recurse deeper if we are retrieving dependencies as well + if (recurse) { + def foundDependencies = readModuleDependencies(new File(targetDir, "module.txt")) + if (foundDependencies.length == 0) { + println "Module $module did not appear to have any dependencies we need to worry about" + } else { + println "Module $module has the following module dependencies we care about: $foundDependencies" + String[] uniqueDependencies = foundDependencies - modulesRetrieved + println "After removing dupes already retrieved we have the remaining dependencies left: $uniqueDependencies" + if (uniqueDependencies.length > 0) { + retrieve(uniqueDependencies, true) + } + } + } + } +} + +/** + * Reads a given module info file to figure out which if any dependencies it has. Filters out any already retrieved. + * @param targetModuleInfo the target file to check (a module.txt file or similar) + * @return a String[] containing the next level of dependencies, if any + */ +String[] readModuleDependencies(File targetModuleInfo) { + def qualifiedDependencies = [] + if (!targetModuleInfo.exists()) { + println "The module info file did not appear to exist - can't calculate dependencies" + return qualifiedDependencies + } + + def slurper = new JsonSlurper() + def moduleConfig = slurper.parseText(targetModuleInfo.text) + for (dependency in moduleConfig.dependencies) { + if (excludedDependencies.contains(dependency.id)) { + println "Skipping listed dependency $dependency as it is in the exclude list (shipped with primary project)" + } else { + println "Accepting listed dependency $dependency" + qualifiedDependencies << dependency.id + } + } + return qualifiedDependencies +} + +/** + * Creates a new module with the given name and adds the necessary .gitignore and module.txt files. + * @param name the name of the module to be created + */ +def createModule(String name) { + // Check if the module already exists. If not, create the module directory + File targetDir = new File("modules/$name") + if (targetDir.exists()) { + println "Target directory already exists. Aborting." + return + } + println "Creating target directory" + targetDir.mkdir() + + // Add gitignore + println "Creating .gitignore" + File gitignore = new File(targetDir, ".gitignore") + def gitignoreText = new File("gradle/templates/.gitignore").text + gitignore << gitignoreText + + // Add module.txt + println "Creating module.txt" + File moduleManifest = new File(targetDir, "module.txt") + def moduleText = new File("gradle/templates/module.txt").text + moduleManifest << moduleText.replaceAll('MODULENAME', name) + + // Initialize git + Grgit.init dir: targetDir, bare: false +} + +/** + * Update a given module. + * @param name the name of the module to update + */ +def updateModule(String name) { + println "Attempting to update module $name" + File targetDir = new File("modules/$name") + if (!targetDir.exists()) { + println "Module \"$name\" not found" + return + } + + def moduleGit = Grgit.open(dir: targetDir) + def clean = moduleGit.status().clean + println "Is \"$name\" clean? $clean" + if (!clean) { + println "Module has uncommitted changes. Aborting." + return + } + + println "Updating module $name" + moduleGit.pull remote: "origin" +} + +/** + * Accepts input from the user, showing a descriptive prompt. + * @param prompt the prompt to show the user + */ +def getUserString (String prompt) { + println ('\n*** ' + prompt + '\n') + + def reader = new BufferedReader(new InputStreamReader(System.in)) // Note: Do not close reader, it will close System.in (Big no-no) + + return reader.readLine() +} + +/** + * Simply prints usage information. + */ +def printUsage() { + println "" + println "Utility script for interacting with modules. Available sub commands:" + println "- 'get' - retrieves one or more modules in source form (separate with spaces)" + println "- 'recurse' - retrieves the given module(s) *and* their dependencies in source form" + println "- 'create' - creates a new module" + println "" + println "Example: 'groovyw module recurse GooeysQuests Sample' - would retrieve those modules plus their dependencies" + println "*NOTE*: Module names are case sensitive" + println "" + println "If you omit further arguments beyond the sub command you'll be prompted for details" + println "After changing modules available in your workspace rerun 'gradlew idea' and/or refresh your IDE" + println "" + println "For advanced usage see project documentation. For instance you can provide an alternative GitHub home" + println "A gradle.properties file (one exists under '/gradle/templates' in an engine workspace) can provide such overrides" + println "" +} + +// Main bit of logic handling the entry points to this script - defers actual work to dedicated methods +//println "Args: $args" +if (args.length == 0) { + printUsage() +} else { + def recurse = false + switch (args[0]) { + case 'usage': + printUsage() + break + case "recurse": + recurse = true + println "We're retrieving recursively (all the things depended on too)" + // We just fall through here to the get logic after setting a boolean + //noinspection GroovyFallthrough + case "get": + println "Preparing to get one or more modules" + if (args.length == 1) { + // User hasn't supplied any module names, so ask + def moduleString = getUserString('Enter Module Name(s - separate multiple with spaces, CapiTaliZation MatterS): ') + println "User wants: $moduleString" + // Split it on whitespace + String[] moduleList = moduleString.split("\\s+") + println "Now in an array: $moduleList" + retrieve moduleList, recurse + } else { + // User has supplied one or more module names, so pass them forward (skipping the "get" arg) + def adjustedArgs = args.drop(1) + println "Adjusted args: $adjustedArgs" + retrieve adjustedArgs, recurse + } + println "All done retrieving requested modules: $modulesRetrieved" + break + case "create": + println "We're doing a create" + String name = "" + + // Get new module's name + if (args.length > 2) { + println "Received more than one argument. Aborting." + break + } else if (args.length == 2) { + name = args[1] + } else { + name = getUserString("Enter module name: ") + } + println "User wants to create a module named: $name" + + createModule(name) + + println "Created module named $name" + break + case "update": + println "We're updating modules" + String[] moduleList = [] + if (args.length == 1) { + // User hasn't supplied any module names, so ask + def moduleString = getUserString('Enter Module Name(s - separate multiple with spaces, CapiTaliZation MatterS): ') + // Split it on whitespace + moduleList = moduleString.split("\\s+") + } else { + // User has supplied one or more module names, so pass them forward (skipping the "get" arg) + moduleList = args.drop(1) + } + println "List of modules to update: $moduleList" + for (String module: moduleList) { + updateModule(module) + } + break + case "update-all": + println "We're updating all modules" + println "List of modules:" + new File("modules").eachDir() { dir -> + String moduleName = dir.getPath().substring(8) + if (!excludedDependencies.contains(moduleName)) { + updateModule(moduleName) + } + } + break + case "init" + // TODO: Special case for using the build harness in a solo module workspace. + // Should work akin to create but using the workspace root, not modules/$name + break + default: + println "UNRECOGNIZED COMMAND - please try again or use 'groovyw module usage' for help" + } +} diff --git a/modules/Core/build.gradle b/modules/Core/build.gradle deleted file mode 100644 index db7ab6bcf3d..00000000000 --- a/modules/Core/build.gradle +++ /dev/null @@ -1,301 +0,0 @@ -// Simple build file for modules - the one under the Core module is the template, will be copied as needed to modules - -// Grab all the common stuff like plugins to use, artifact repositories, code analysis config, Artifactory settings, Git magic -apply from: "$rootDir/config/gradle/artifactory.gradle" - -import groovy.json.JsonSlurper -import java.text.SimpleDateFormat; - -// Git plugin details at https://github.com/ajoberstar/gradle-git -import org.ajoberstar.gradle.git.tasks.* -import org.reflections.Reflections; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; -import org.reflections.util.ConfigurationBuilder; - -// Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( -buildscript { - repositories { - // External libs - jcenter is Bintray and is supposed to be a superset of Maven Central, but do both just in case - jcenter() - mavenCentral() - } - - dependencies { - // Artifactory plugin - classpath(group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '4.0.0') - - // Git plugin for Gradle - classpath 'org.ajoberstar:gradle-git:0.6.3' - - // Needed for caching reflected data during builds - classpath 'org.reflections:reflections:0.9.10' - classpath 'dom4j:dom4j:1.6.1' - } -} - -ext { - // Read environment variables, including variables passed by jenkins continuous integration server - env = System.getenv() -} - -def moduleDepends = []; -def moduleFile = file('module.txt') - -// The module file should always exist if the module was correctly created or cloned using Gradle -if (!moduleFile.exists()) { - println "Y U NO EXIST MODULE.TXT!" - throw new GradleException("Failed to find module.txt for " + project.name) -} - -//println "Scanning for dependencies in module.txt for " + project.name -def slurper = new JsonSlurper() -def moduleConfig = slurper.parseText(moduleFile.text) -for (dependency in moduleConfig.dependencies) { - if (dependency.id != 'engine') { - moduleDepends += dependency.id - } -} - -// Gradle uses the magic version variable when creating the jar name (unless explicitly set somewhere else I guess) -version = moduleConfig.version - -// Jenkins-Artifactory integration catches on to this as part of the Maven-type descriptor -group = 'org.terasology.modules' - -println "Version for $project.name loaded as $version for group $group" - -// TODO: Remove when we don't need to rely on snapshots. Needed here for solo builds in Jenkins -configurations.all { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' -} - -// Set dependencies. Note that the dependency information from module.txt is used for other Terasology modules -dependencies { - // Check to see if this module is not the root Gradle project - if so we are in a multi-project workspace - if (project.name != project(':').name) { - println "\nProcessing module '$project.name' in a multi-project workspace" - - // Dependency on the engine itself (actually its built jar file) - compile project(':engine') - - // Engine unit tests contain classes that module unit tests can extend, so need to be compile, not testCompile - compile project(':engine-tests') - - if (moduleDepends.size() > 0) { - println "* $project.name has extra dependencies:" - moduleDepends.each { - println "** $it" - } - } else { - println "* No extra dependencies" - } - - // If the module has dependencies on other modules we look for either a source version or a remote binary - for (dependency in moduleDepends) { - File wouldBeSrcPath = new File(rootDir, 'modules/' + dependency) - //println "Scanning for source module at: " + wouldBeSrcPath.getAbsolutePath() - - // First see if we have an actual source module project in the Gradle project tree (user fetchModule'ed it) - if (wouldBeSrcPath.exists()) { - //TODO: This could hit problems with corrupt module directories? - - println "*** Identified source: " + dependency - // Note: if artifactoryPublish is used in a multi-project workspace including modules the .pom gets hard version refs - // Normally they're expected to run in Jenkins standalone where they'll instead match the else and get version '+' - compile project(':modules:' + dependency) - } else { - println "*** Seeking as binary: " + dependency - // The '+' is satisfied by any version. "changing" triggers better checking for updated snapshots - // TODO: When version handling and promotion is in then we can probably ignore snapshots in normal cases - compile(group: 'org.terasology.modules', name: dependency, version: '+', changing: true) - } - } - - // This step resolves artifacts early, after which project config CANNOT be altered again! - configurations.compile.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact -> - def id = artifact.moduleVersion.id - - // Check for any needed module dependencies on other modules that we need at runtime - if (id.group == 'org.terasology.modules' && id.name != "Core") { - File moduleSrcPath = new File(rootDir, 'modules/' + id.name) - File moduleJarPath = new File(rootDir, 'modules/' + id.name + '-' + id.version + '.jar') - - if (moduleSrcPath.exists()) { - println "*** Found module dependency $id.name in source form, not copying a runtime jar from Gradle" - } else { - println "$project.name resolved binary $id.group - $id.name at version $id.version" - - // This copies the jar from the Gradle cache to the game's module dir for runtime usage, if needed - if (!moduleJarPath.exists()) { - println "* Writing a runtime jar to /modules: " + moduleJarPath.name - moduleJarPath.createNewFile() - moduleJarPath << artifact.file.bytes - } - } - } - } - } else { - println "We're in a single-project non-Core module workspace (Jenkins) so will look elsewhere for dependencies" - - // TODO: While this is easy it would prevent modules declaring an engine dependency of a specific version - // TODO: Look for a provided engine jar in the workspace and use that if present - // TODO: Only use engine, engine-tests, and maybe core for compilation, but remove when publishing? - compile(group: 'org.terasology.engine', name: 'engine', version: '+', changing: true) - compile(group: 'org.terasology.engine', name: 'engine-tests', version: '+', changing: true) - - // To get Terasology module dependencies we simply declare them against Artifactory - moduleDepends.each { - println "*** Attempting to fetch dependency module from Artifactory for " + project.name + ": " + it - // The '+' is satisfied by any version - compile(group: 'org.terasology.modules', name: it, version: '+', changing: true) - - } - - // TODO: parse and apply external lib dependencies if any are present - // TODO: Consider / keep an eye on whether resolving artifacts early at this point causes any trouble (is only for logging) - // This step resolves artifacts (both compile & testCompile) and prints out some interesting versions - configurations.testCompile.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact -> - def id = artifact.moduleVersion.id - // Print out module (and engine stuff) dependencies and the exact versions they resolved at - if (id.group.startsWith('org.terasology')) { - println "*** $project.name remotely resolved $id.group - $id.name - version $id.version" - } - } - } -} - -// Change the output dir of each module -sourceSets { - main { - java { - output.classesDir 'build/classes' - } - } -} - -// Generate the module directory structure if missing -task createSkeleton() { - mkdir('assets') - mkdir('assets/animations') - mkdir('assets/atlas') - mkdir('assets/behaviors') - mkdir('assets/blocks') - mkdir('assets/blockSounds') - mkdir('assets/blockTiles') - mkdir('assets/fonts') - mkdir('assets/materials') - mkdir('assets/mesh') - mkdir('assets/music') - mkdir('assets/prefabs') - mkdir('assets/shaders') - mkdir('assets/shapes') - mkdir('assets/skeletalMesh') - mkdir('assets/skins') - mkdir('assets/sounds') - mkdir('assets/textures') - mkdir('assets/ui') - mkdir('overrides') - mkdir('deltas') - mkdir('src/main/java') - mkdir('src/test/java') -} - -task cacheReflections { - description = 'Caches reflection output to make regular startup faster. May go stale and need cleanup at times.' - // TODO: The extra "org" qualifier excludes test classes otherwise sucked up in Jenkins, causing issues. Redo later - File dirToReflect = new File(sourceSets.main.output.classesDir, "org") - inputs.files dirToReflect - outputs.file file(sourceSets.main.output.classesDir.toString() + "/reflections.cache") - dependsOn classes - doFirst { - // Without the .mkdirs() we might hit a scenario where the classes dir doesn't exist yet - dirToReflect.mkdirs() - Reflections reflections = new Reflections(new ConfigurationBuilder() - .addUrls(dirToReflect.toURI().toURL()) - .setScanners(new TypeAnnotationsScanner(), new SubTypesScanner())) - reflections.save(sourceSets.main.output.classesDir.toString() + "/reflections.cache") - } -} - -task cleanReflections(type: Delete) { - description = 'Cleans the reflection cache. Useful in cases where it has gone stale and needs regeneration.' - delete sourceSets.main.output.classesDir.toString() + "/reflections.cache" -} - -// This task syncs everything in the assets dir into the output dir, used when jarring the module -task syncAssets(type: Sync) { - from 'assets' - into 'build/classes/assets' -} - -task syncOverrides(type: Sync) { - from 'overrides' - into 'build/classes/overrides' -} - -task syncDeltas(type: Sync) { - from 'deltas' - into 'build/classes/deltas' -} - -// Instructions for packaging a jar file - is a manifest even needed for modules? -jar { - // Make sure the assets directory is included - dependsOn cacheReflections - dependsOn syncAssets - dependsOn syncOverrides - dependsOn syncDeltas - - // Jarring needs to copy module.txt and all the assets into the output - doFirst { - copy { - from 'module.txt' - into 'build/classes' - } - } -} - -jar.finalizedBy cleanReflections - -// Prep an IntelliJ module for the Terasology module - yes, might want to read that twice :D -idea { - module { - // Change around the output a bit - 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') - } -} - -// Utility task to update the module (except Core) via Git - not tested with local changes present, may cause trouble -task (updateModule, type: GitPull) { - description = 'Updates source for the module from its home (most likely GitHub)' - - // Base whether the task executes on two things - // 1 - we actually asked for it ("gradlew updateModule") - otherwise we don't want it to run TODO: Test for abbreviations? - // 2 - this is not the Core module, which lives with the engine and needs no updates - boolean enabled = "updateModule" in project.gradle.startParameter.taskNames && !project.name.equals("Core") - // TODO: Used to cheat with declaring tasks using << but that defers everything (including config) to execution phase - // Some tasks do not work that way, this one would ALWAYS go with default (root git repo) in that case - // Probably should go through other stuff and use this strategy instead of << - - // Only if we asked for it (and not Core) do we actually configure the repo path and log that we're updating - if (enabled) { - - // This is the Git repo we're actually using - projectDir is specific to the executing project, a.k.a. module whatever - // If not enabled the default is the root project's Git dir, which is a valid Git dir - // However in the case of Core we'd be setting ain invalid Git dir which causes fail - so this avoids that - repoPath = projectDir - - println "Pulling updates via Git to " + getRepoDir() + ", if dependencies change run Gradle again (like 'gradlew idea')" - } -} diff --git a/modules/CoreSampleGameplay/build.gradle b/modules/CoreSampleGameplay/build.gradle deleted file mode 100644 index db7ab6bcf3d..00000000000 --- a/modules/CoreSampleGameplay/build.gradle +++ /dev/null @@ -1,301 +0,0 @@ -// Simple build file for modules - the one under the Core module is the template, will be copied as needed to modules - -// Grab all the common stuff like plugins to use, artifact repositories, code analysis config, Artifactory settings, Git magic -apply from: "$rootDir/config/gradle/artifactory.gradle" - -import groovy.json.JsonSlurper -import java.text.SimpleDateFormat; - -// Git plugin details at https://github.com/ajoberstar/gradle-git -import org.ajoberstar.gradle.git.tasks.* -import org.reflections.Reflections; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; -import org.reflections.util.ConfigurationBuilder; - -// Dependencies needed for what our Gradle scripts themselves use. It cannot be included via an external Gradle file :-( -buildscript { - repositories { - // External libs - jcenter is Bintray and is supposed to be a superset of Maven Central, but do both just in case - jcenter() - mavenCentral() - } - - dependencies { - // Artifactory plugin - classpath(group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '4.0.0') - - // Git plugin for Gradle - classpath 'org.ajoberstar:gradle-git:0.6.3' - - // Needed for caching reflected data during builds - classpath 'org.reflections:reflections:0.9.10' - classpath 'dom4j:dom4j:1.6.1' - } -} - -ext { - // Read environment variables, including variables passed by jenkins continuous integration server - env = System.getenv() -} - -def moduleDepends = []; -def moduleFile = file('module.txt') - -// The module file should always exist if the module was correctly created or cloned using Gradle -if (!moduleFile.exists()) { - println "Y U NO EXIST MODULE.TXT!" - throw new GradleException("Failed to find module.txt for " + project.name) -} - -//println "Scanning for dependencies in module.txt for " + project.name -def slurper = new JsonSlurper() -def moduleConfig = slurper.parseText(moduleFile.text) -for (dependency in moduleConfig.dependencies) { - if (dependency.id != 'engine') { - moduleDepends += dependency.id - } -} - -// Gradle uses the magic version variable when creating the jar name (unless explicitly set somewhere else I guess) -version = moduleConfig.version - -// Jenkins-Artifactory integration catches on to this as part of the Maven-type descriptor -group = 'org.terasology.modules' - -println "Version for $project.name loaded as $version for group $group" - -// TODO: Remove when we don't need to rely on snapshots. Needed here for solo builds in Jenkins -configurations.all { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' -} - -// Set dependencies. Note that the dependency information from module.txt is used for other Terasology modules -dependencies { - // Check to see if this module is not the root Gradle project - if so we are in a multi-project workspace - if (project.name != project(':').name) { - println "\nProcessing module '$project.name' in a multi-project workspace" - - // Dependency on the engine itself (actually its built jar file) - compile project(':engine') - - // Engine unit tests contain classes that module unit tests can extend, so need to be compile, not testCompile - compile project(':engine-tests') - - if (moduleDepends.size() > 0) { - println "* $project.name has extra dependencies:" - moduleDepends.each { - println "** $it" - } - } else { - println "* No extra dependencies" - } - - // If the module has dependencies on other modules we look for either a source version or a remote binary - for (dependency in moduleDepends) { - File wouldBeSrcPath = new File(rootDir, 'modules/' + dependency) - //println "Scanning for source module at: " + wouldBeSrcPath.getAbsolutePath() - - // First see if we have an actual source module project in the Gradle project tree (user fetchModule'ed it) - if (wouldBeSrcPath.exists()) { - //TODO: This could hit problems with corrupt module directories? - - println "*** Identified source: " + dependency - // Note: if artifactoryPublish is used in a multi-project workspace including modules the .pom gets hard version refs - // Normally they're expected to run in Jenkins standalone where they'll instead match the else and get version '+' - compile project(':modules:' + dependency) - } else { - println "*** Seeking as binary: " + dependency - // The '+' is satisfied by any version. "changing" triggers better checking for updated snapshots - // TODO: When version handling and promotion is in then we can probably ignore snapshots in normal cases - compile(group: 'org.terasology.modules', name: dependency, version: '+', changing: true) - } - } - - // This step resolves artifacts early, after which project config CANNOT be altered again! - configurations.compile.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact -> - def id = artifact.moduleVersion.id - - // Check for any needed module dependencies on other modules that we need at runtime - if (id.group == 'org.terasology.modules' && id.name != "Core") { - File moduleSrcPath = new File(rootDir, 'modules/' + id.name) - File moduleJarPath = new File(rootDir, 'modules/' + id.name + '-' + id.version + '.jar') - - if (moduleSrcPath.exists()) { - println "*** Found module dependency $id.name in source form, not copying a runtime jar from Gradle" - } else { - println "$project.name resolved binary $id.group - $id.name at version $id.version" - - // This copies the jar from the Gradle cache to the game's module dir for runtime usage, if needed - if (!moduleJarPath.exists()) { - println "* Writing a runtime jar to /modules: " + moduleJarPath.name - moduleJarPath.createNewFile() - moduleJarPath << artifact.file.bytes - } - } - } - } - } else { - println "We're in a single-project non-Core module workspace (Jenkins) so will look elsewhere for dependencies" - - // TODO: While this is easy it would prevent modules declaring an engine dependency of a specific version - // TODO: Look for a provided engine jar in the workspace and use that if present - // TODO: Only use engine, engine-tests, and maybe core for compilation, but remove when publishing? - compile(group: 'org.terasology.engine', name: 'engine', version: '+', changing: true) - compile(group: 'org.terasology.engine', name: 'engine-tests', version: '+', changing: true) - - // To get Terasology module dependencies we simply declare them against Artifactory - moduleDepends.each { - println "*** Attempting to fetch dependency module from Artifactory for " + project.name + ": " + it - // The '+' is satisfied by any version - compile(group: 'org.terasology.modules', name: it, version: '+', changing: true) - - } - - // TODO: parse and apply external lib dependencies if any are present - // TODO: Consider / keep an eye on whether resolving artifacts early at this point causes any trouble (is only for logging) - // This step resolves artifacts (both compile & testCompile) and prints out some interesting versions - configurations.testCompile.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact -> - def id = artifact.moduleVersion.id - // Print out module (and engine stuff) dependencies and the exact versions they resolved at - if (id.group.startsWith('org.terasology')) { - println "*** $project.name remotely resolved $id.group - $id.name - version $id.version" - } - } - } -} - -// Change the output dir of each module -sourceSets { - main { - java { - output.classesDir 'build/classes' - } - } -} - -// Generate the module directory structure if missing -task createSkeleton() { - mkdir('assets') - mkdir('assets/animations') - mkdir('assets/atlas') - mkdir('assets/behaviors') - mkdir('assets/blocks') - mkdir('assets/blockSounds') - mkdir('assets/blockTiles') - mkdir('assets/fonts') - mkdir('assets/materials') - mkdir('assets/mesh') - mkdir('assets/music') - mkdir('assets/prefabs') - mkdir('assets/shaders') - mkdir('assets/shapes') - mkdir('assets/skeletalMesh') - mkdir('assets/skins') - mkdir('assets/sounds') - mkdir('assets/textures') - mkdir('assets/ui') - mkdir('overrides') - mkdir('deltas') - mkdir('src/main/java') - mkdir('src/test/java') -} - -task cacheReflections { - description = 'Caches reflection output to make regular startup faster. May go stale and need cleanup at times.' - // TODO: The extra "org" qualifier excludes test classes otherwise sucked up in Jenkins, causing issues. Redo later - File dirToReflect = new File(sourceSets.main.output.classesDir, "org") - inputs.files dirToReflect - outputs.file file(sourceSets.main.output.classesDir.toString() + "/reflections.cache") - dependsOn classes - doFirst { - // Without the .mkdirs() we might hit a scenario where the classes dir doesn't exist yet - dirToReflect.mkdirs() - Reflections reflections = new Reflections(new ConfigurationBuilder() - .addUrls(dirToReflect.toURI().toURL()) - .setScanners(new TypeAnnotationsScanner(), new SubTypesScanner())) - reflections.save(sourceSets.main.output.classesDir.toString() + "/reflections.cache") - } -} - -task cleanReflections(type: Delete) { - description = 'Cleans the reflection cache. Useful in cases where it has gone stale and needs regeneration.' - delete sourceSets.main.output.classesDir.toString() + "/reflections.cache" -} - -// This task syncs everything in the assets dir into the output dir, used when jarring the module -task syncAssets(type: Sync) { - from 'assets' - into 'build/classes/assets' -} - -task syncOverrides(type: Sync) { - from 'overrides' - into 'build/classes/overrides' -} - -task syncDeltas(type: Sync) { - from 'deltas' - into 'build/classes/deltas' -} - -// Instructions for packaging a jar file - is a manifest even needed for modules? -jar { - // Make sure the assets directory is included - dependsOn cacheReflections - dependsOn syncAssets - dependsOn syncOverrides - dependsOn syncDeltas - - // Jarring needs to copy module.txt and all the assets into the output - doFirst { - copy { - from 'module.txt' - into 'build/classes' - } - } -} - -jar.finalizedBy cleanReflections - -// Prep an IntelliJ module for the Terasology module - yes, might want to read that twice :D -idea { - module { - // Change around the output a bit - 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') - } -} - -// Utility task to update the module (except Core) via Git - not tested with local changes present, may cause trouble -task (updateModule, type: GitPull) { - description = 'Updates source for the module from its home (most likely GitHub)' - - // Base whether the task executes on two things - // 1 - we actually asked for it ("gradlew updateModule") - otherwise we don't want it to run TODO: Test for abbreviations? - // 2 - this is not the Core module, which lives with the engine and needs no updates - boolean enabled = "updateModule" in project.gradle.startParameter.taskNames && !project.name.equals("Core") - // TODO: Used to cheat with declaring tasks using << but that defers everything (including config) to execution phase - // Some tasks do not work that way, this one would ALWAYS go with default (root git repo) in that case - // Probably should go through other stuff and use this strategy instead of << - - // Only if we asked for it (and not Core) do we actually configure the repo path and log that we're updating - if (enabled) { - - // This is the Git repo we're actually using - projectDir is specific to the executing project, a.k.a. module whatever - // If not enabled the default is the root project's Git dir, which is a valid Git dir - // However in the case of Core we'd be setting ain invalid Git dir which causes fail - so this avoids that - repoPath = projectDir - - println "Pulling updates via Git to " + getRepoDir() + ", if dependencies change run Gradle again (like 'gradlew idea')" - } -} diff --git a/modules/build.gradle b/modules/build.gradle new file mode 100644 index 00000000000..fa3934fa3bc --- /dev/null +++ b/modules/build.gradle @@ -0,0 +1,70 @@ +// This file is the entry point for Gradle's build logic for modules (subprojects.gradle just sets up project structure) + +// This buildscript block is needed for some plugins to work, even if they are applied deeper in via apply from: blocks +// TODO: Switch to the new Gradle plugin approach when it works in subproject blocks (beyond Gradle 3.3 sometime) + +buildscript { + repositories { + jcenter() + } + dependencies { + // Artifactory plugin + classpath(group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '4.0.0') + + // Needed at this level to support modules in Kotlin + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.6" + } +} + +import groovy.json.JsonSlurper + +println "The build.gradle from /modules says hi. We're going to review some potential modules!" + +if (project.name != project(':').name) { + println "We're running embedded in an engine workspace" + + // We need to prepare the build logic for each module so we use a subprojects block to make this execute for each + subprojects { + + // Parse out the language selection from module.txt + def moduleFile = file('module.txt') + JsonSlurper slurper = new JsonSlurper() + def moduleConfig = slurper.parseText(moduleFile.text) + + // When we know the language we apply the appropriate language-specific wrapper for build logic + if (moduleConfig.language == "scala") { + println "\nFound a Scala module: * $project.name *" + apply from: "$rootDir/gradle/scala.gradle" + } else if (moduleConfig.language == "kotlin") { + println "\nFound a Kotlin module: * $project.name *" + // TODO: This satisfies Kotlin compilation in a module using it. Still need to guarantee a dependency on KotlinLib to get the runtime jar (I think?) + // So during Gradling for a language specific module insert condition: If KotlinLib then include the jar. If not then validate KotlinLib dep in module.txt + apply from: "$rootDir/gradle/kotlin.gradle" + } else { + println "\nFound a Java module: * $project.name *" + apply from: "$rootDir/gradle/java.gradle" + } + } + +} else { + println "Solo module build woo!" + + // Parse out the language selection from module.txt + def moduleFile = file('module.txt') + JsonSlurper slurper = new JsonSlurper() + def moduleConfig = slurper.parseText(moduleFile.text) + + // When we know the language we apply the appropriate language-specific wrapper for build logic + if (moduleConfig.language == "scala") { + println "\nThis is a standalone Scala module workspace: * $project.name *" + apply from: "$rootDir/gradle/scala.gradle" + } else if (moduleConfig.language == "kotlin") { + println "\nThis is a standalone Kotlin module workspace: * $project.name *" + // TODO: This satisfies Kotlin compilation in a module using it. Still need to guarantee a dependency on KotlinLib to get the runtime jar (I think?) + // So during Gradling for a language specific module insert condition: If KotlinLib then include the jar. If not then validate KotlinLib dep in module.txt + apply from: "$rootDir/gradle/kotlin.gradle" + } else { + println "\nThis is a standalone Java module workspace: * $project.name *" + apply from: "$rootDir/gradle/java.gradle" + } +} diff --git a/modules/subprojects.gradle b/modules/subprojects.gradle index 9d372ea39da..28f3b5c1d5f 100644 --- a/modules/subprojects.gradle +++ b/modules/subprojects.gradle @@ -1,15 +1,14 @@ // This magically allows subdirs in this subproject to themselves become sub-subprojects in a proper tree structure new File(rootDir, 'modules').eachDir { possibleSubprojectDir -> + 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" - include subprojectName - def subprojectPath = ':' + subprojectName - def subproject = project(subprojectPath) - subproject.projectDir = possibleSubprojectDir + println "Gradle is reviewing module $subprojectName for inclusion as a sub-project" + + File moduleFile = new File(possibleSubprojectDir, "module.txt") + if (!moduleFile.exists()) { + println "***WARNING*** Module $subprojectName has no module file, expecting corrupt directory, please fix!" } else { - println "***** WARNING: Found a module without a build.gradle, corrupt dir? NOT including $subprojectName *****" + include subprojectName + println "Including $subprojectName as a valid module" } } diff --git a/templates/.gitignore b/templates/.gitignore deleted file mode 100644 index 25e5ed21447..00000000000 --- a/templates/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -# Modules get a copy of build.gradle as they are not allowed to have their own (for build security / sandboxing) -build.gradle - -# IntelliJ -*.iml - -# Eclipse -.checkstyle -.classpath -.project -.settings -bin/ -build/ \ No newline at end of file