From 7713ecbe1dc588f7db1d306b1cc8567060c97dc2 Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Sat, 21 Mar 2020 18:20:49 +0100 Subject: [PATCH] feat: Platform-specific distribution packages (#516) * feat(gradle): Bundle platform-spec. `.exe` files * feat(gradle): bundle `TerasologyLauncher.app` for Mac distribution * feat: add launch script for linux to top-level * chore: clean up `jre.gradle` * chore: rearrange build resources into `buildres` directory * chore: delete `bundles` folder and unused gradle file * fix: set fixed name for JAR for Windows `.exe`s to work Co-authored-by: Niruandaleth --- .gitignore | 2 +- build.gradle | 4 +- buildres/linux/TerasologyLauncher.run | 3 + {bundles/macosx => buildres/mac}/Info.plist | 2 +- .../mac}/Resources/terasology.icns | Bin .../windows32}/TerasologyLauncher.x86.exe | Bin .../windows64}/TerasologyLauncher.x64.exe | Bin bundles/linux/run_linux.sh | 4 - .../macosx/MacOS/terasologylauncher.command | 3 - config/gradle/bundles.gradle | 93 ---------------- config/gradle/jre.gradle | 100 ++++++++++-------- src/dist/TerasologyLauncher.exe | Bin 30208 -> 0 bytes 12 files changed, 64 insertions(+), 147 deletions(-) create mode 100755 buildres/linux/TerasologyLauncher.run rename {bundles/macosx => buildres/mac}/Info.plist (88%) rename {bundles/macosx => buildres/mac}/Resources/terasology.icns (100%) rename {src/dist => buildres/windows32}/TerasologyLauncher.x86.exe (100%) rename {src/dist => buildres/windows64}/TerasologyLauncher.x64.exe (100%) delete mode 100644 bundles/linux/run_linux.sh delete mode 100644 bundles/macosx/MacOS/terasologylauncher.command delete mode 100644 config/gradle/bundles.gradle delete mode 100644 src/dist/TerasologyLauncher.exe diff --git a/.gitignore b/.gitignore index 76c56349a..9e6e14aaa 100644 --- a/.gitignore +++ b/.gitignore @@ -49,7 +49,7 @@ certum.jks /src/main/resources/org/terasology/launcher/icons/ # Ignore version info file(s) -src/main/resources/org/terasology/launcher/version/ +/src/main/resources/org/terasology/launcher/version/ # Ignore generated Web API sources web-api-client/ diff --git a/build.gradle b/build.gradle index 83d54d2e9..f86ef4e4e 100644 --- a/build.gradle +++ b/build.gradle @@ -46,8 +46,6 @@ plugins { } apply from: "./config/gradle/jre.gradle" -// TODO salvage useful bits and remove this -//apply from: "./config/gradle/bundles.gradle" apply from: "./config/gradle/quality.gradle" apply from: "./config/gradle/swagger.gradle" @@ -164,6 +162,8 @@ task createVersionInfoFile { processResources.dependsOn createVersionInfoFile jar { + //TODO we only use this name because the `.exe` start scripts require the JAR to be named 'TerasologyLauncher.jar' + jar.archiveFileName = "${project.name}.jar" // replace development "logback.xml" with productive "logback_jar.xml" exclude "logback.xml" rename('logback_jar.xml', 'logback.xml') diff --git a/buildres/linux/TerasologyLauncher.run b/buildres/linux/TerasologyLauncher.run new file mode 100755 index 000000000..472d3c6a6 --- /dev/null +++ b/buildres/linux/TerasologyLauncher.run @@ -0,0 +1,3 @@ +#!/usr/bin/env sh +echo "Starting TerasologyLauncher from './bin/TerasologyLauncher'" +sh ./bin/TerasologyLauncher diff --git a/bundles/macosx/Info.plist b/buildres/mac/Info.plist similarity index 88% rename from bundles/macosx/Info.plist rename to buildres/mac/Info.plist index 40255db5f..0360450ef 100644 --- a/bundles/macosx/Info.plist +++ b/buildres/mac/Info.plist @@ -5,7 +5,7 @@ CFBundleDisplayName Terasology CFBundleExecutable - terasologylauncher.command + TerasologyLauncher CFBundleIconFile terasology.icns diff --git a/bundles/macosx/Resources/terasology.icns b/buildres/mac/Resources/terasology.icns similarity index 100% rename from bundles/macosx/Resources/terasology.icns rename to buildres/mac/Resources/terasology.icns diff --git a/src/dist/TerasologyLauncher.x86.exe b/buildres/windows32/TerasologyLauncher.x86.exe similarity index 100% rename from src/dist/TerasologyLauncher.x86.exe rename to buildres/windows32/TerasologyLauncher.x86.exe diff --git a/src/dist/TerasologyLauncher.x64.exe b/buildres/windows64/TerasologyLauncher.x64.exe similarity index 100% rename from src/dist/TerasologyLauncher.x64.exe rename to buildres/windows64/TerasologyLauncher.x64.exe diff --git a/bundles/linux/run_linux.sh b/bundles/linux/run_linux.sh deleted file mode 100644 index 1bf7c2eb0..000000000 --- a/bundles/linux/run_linux.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -cd "$(dirname "$0")" -jre/bin/java -Xms128m -Xmx512m -jar lib/TerasologyLauncher.jar - diff --git a/bundles/macosx/MacOS/terasologylauncher.command b/bundles/macosx/MacOS/terasologylauncher.command deleted file mode 100644 index 16b271db0..000000000 --- a/bundles/macosx/MacOS/terasologylauncher.command +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -cd "$(dirname "$0")/../Resources/" -jre/bin/java -Xms128m -Xmx512m -jar lib/TerasologyLauncher.jar diff --git a/config/gradle/bundles.gradle b/config/gradle/bundles.gradle deleted file mode 100644 index 2686c0517..000000000 --- a/config/gradle/bundles.gradle +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2016 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -enum Bundle { - win32, - win64, - linux32, - linux64, - macosx; - - File jreArchive -} - -task buildBundles {} - -task unzipBundle(type: Copy) { - destinationDir buildDir - from (zipTree("${projectDir}/build/distributions/TerasologyLauncher.zip")) -} - -def launcherDir = "${buildDir}/TerasologyLauncher/" - -Bundle.each { arch -> - arch.jreArchive = new File("${projectDir}/jres/${arch}.zip") - - def zipBase = "" - // Rebase everything in the zip file for MacOS X - if (arch == Bundle.macosx) { - zipBase = "TerasologyLauncher.app/Contents/Resources/" - } - - def buildBundleTask = task("zip" + arch.name().capitalize(), type: Zip) { - dependsOn unzipBundle - archiveName "TerasologyLauncher-${arch}.zip" - destinationDir new File("${buildDir}/bundles/") - from(zipTree(arch.jreArchive)) { - into "${zipBase}jre" - } - from(launcherDir) { - // Platform independent files - exclude "*.exe" - exclude "bin" - into zipBase - } - /* - Copy only the files relevant to the platform we are bundling the JRE for. - I.e only copy 32-bit natives for windows if we bundle the 32-bit JRE. - */ - if (arch == Bundle.win32) { - from (launcherDir) { - include "TerasologyLauncher.x86.exe" - rename { "TerasologyLauncher.exe" } - } - } else if (arch == Bundle.win64) { - from (launcherDir) { - include "TerasologyLauncher.x64.exe" - rename { "TerasologyLauncher.exe" } - } - } else if (arch == Bundle.linux32) { - from ("bundles/linux") { - include "run_linux.sh" - rename { "terasology_launcher.sh" } - fileMode 755 - } - } else if (arch == Bundle.linux64) { - from ("bundles/launchers") { - include "run_linux.sh" - rename { "terasology_launcher.sh" } - fileMode 755 - } - } else if (arch == Bundle.macosx) { - from ("bundles/macosx") { - include "**/*" - into "TerasologyLauncher.app/Contents" - } - } - } - - buildBundles.dependsOn buildBundleTask -} diff --git a/config/gradle/jre.gradle b/config/gradle/jre.gradle index 67d32ce1a..30d5a1736 100644 --- a/config/gradle/jre.gradle +++ b/config/gradle/jre.gradle @@ -16,7 +16,7 @@ // Uses Bellsoft Liberica JRE def jreVersion = '8u212' -def jreUrlBase = "https://download.bell-sw.com/java/$jreVersion/bellsoft-jre$jreVersion" +def jreUrlBase = "https://download.bell-sw.com/java/${jreVersion}/bellsoft-jre${jreVersion}" def jreUrlFilenames = [ Linux64 : 'linux-amd64.tar.gz', Linux32 : 'linux-i586.tar.gz', @@ -36,67 +36,81 @@ task createRelease() { } task downloadJreAll { - group 'Download' - description 'Downloads JRE for all platforms' + group 'JRE' + description 'Downloads JREs for all platforms' +} + +task unpackJreAll { + group 'JRE' + description 'Unpack JREs for all platforms' +} + +def unpack(file) { + copySpec { + from((file.name.endsWith(".zip")) ? zipTree(file) : tarTree(file) ) { + eachFile { fcd -> + fcd.relativePath = new RelativePath( + true, fcd.relativePath.segments.drop(1)) + } + includeEmptyDirs = false + } + } } jreUrlFilenames.each { os, file -> - def packedJre = new File("$projectDir/jre/$file") - def unpackedJre = new File("$buildDir/jre/$os") - def downloadTask = task("downloadJre$os") { - group 'Download' + def downloadTask = task "downloadJre${os}"(type: Download) { + group 'JRE' description "Downloads JRE for $os" - doFirst { - download { - src "$jreUrlBase-$file" - dest packedJre - overwrite false - } - } + src "${jreUrlBase}-${file}" + dest "${projectDir}/jre/${file}" + overwrite false + } - doLast { - // Unpack the JRE - if (!unpackedJre.exists()) { - unpackedJre.mkdirs() - copy { - from(file.endsWith("zip") - ? zipTree(packedJre) - : tarTree(packedJre)) { - eachFile { fcd -> - fcd.relativePath = new RelativePath( - true, fcd.relativePath.segments.drop(1)) - } - includeEmptyDirs = false - } - into unpackedJre - } - } - } + def unpackTask = task "unpackJre${os}"(type: Copy) { + group 'JRE' + description "Unpack JRE for ${os} to distribution sources" + dependsOn downloadTask + + with unpack(downloadTask.dest) + into "${buildDir}/jre/${os}" } - def distName = os.toLowerCase() distributions { - "$distName" { + def distName = os.toLowerCase() + def distBase = distName.replaceAll("\\d", "") // drop '32' or '64' + + "${distName}" { contents { with distributions.main.contents + into('jre') { - from unpackedJre + from unpackTask } + + from("${projectDir}/buildres/${distBase}") + from("${projectDir}/buildres/${distName}") } } } - def zipTask = tasks.named("${distName}DistZip").get() - def tarTask = tasks.named("${distName}DistTar").get() - downloadJreAll.dependsOn downloadTask - zipTask.dependsOn downloadTask - zipTask.description "Bundles the project with a JRE for $os" - tarTask.dependsOn downloadTask - tarTask.description "Bundles the project with a JRE for $os" - createRelease.dependsOn zipTask + unpackJreAll.dependsOn unpackTask + + createRelease.dependsOn "assemble${os}Dist" +} + +distributions { + mac { + contents { + into 'TerasologyLauncher.app/Contents' + exclude '**/*.bat' + eachFile { details -> + details.path = details.path.toString().replaceAll("(Contents)/bin/(.+)", "\$1/MacOS/\$2") + } + } + } } /** diff --git a/src/dist/TerasologyLauncher.exe b/src/dist/TerasologyLauncher.exe deleted file mode 100644 index 5d40d8d1b0df1a15f4c8bc8d2dc4eb428c54a805..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30208 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