diff --git a/.gitattributes b/.gitattributes index f2efb39..03959a9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,4 +6,4 @@ * text=auto eol=lf *.bat text eol=crlf - +*.nsi text eol=crlf diff --git a/build.gradle b/build.gradle index 7ed8c0e..9cc7031 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,12 @@ /* - * build.gradle for Progressia + * Build logic for Progressia + * build.gradle + * + * Please refer to + * + * docs/building/BuildScriptReference.md + * + * for user reference. */ plugins { @@ -8,19 +15,15 @@ plugins { // GrGit // A JGit wrapper for Groovy used to access git commit info id 'org.ajoberstar.grgit' version '4.1.1' - - /* - * Uncomment the following line to enable the Eclipse plugin. - * This is only necessary if you don't use Buildship plugin from the IDE - */ - //id 'eclipse' } + + +/* + * Configure Java version + */ + java { - /* - * We're Java 8 for now. - * Why? As of 2020, most users have Oracle Java, which only supports Java 8. - */ sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } @@ -37,6 +40,8 @@ compileJava { } } + + /* * Dependencies */ @@ -45,7 +50,7 @@ repositories { mavenCentral() /* - * Specify Windcorp Maven repository + * Windcorp Maven repository * Currently used by: * - ru.windcorp.fork.io.github.java-graphics:glm:1.0.1 */ @@ -75,262 +80,308 @@ dependencies { // A unit-testing library testImplementation 'junit:junit:4.13.2' - // See LWJGL dependencies below + // Also see LWJGL dependencies in build_logic/lwjgl.gradle } + + /* - * Progressia uses the following LWJGL libraries: - * - Core libraries - * - OpenGL - * - OpenAL - * - GLFW - * - STB + * Version resolution */ -/* - * LWJGL - * (auto-generated script) - * ((here be dragons)) - */ +import org.ajoberstar.grgit.* -import org.gradle.internal.os.OperatingSystem +// Pattern: vMAJOR.MINOR.PATCH[-SUFFIX] +project.ext.tagFormat = /^v(\d+)\.(\d+)\.(\d+)(-[\w\-]*)?$/ -project.ext.lwjglVersion = "3.2.3" - -switch (OperatingSystem.current()) { - case OperatingSystem.LINUX: - def osArch = System.getProperty("os.arch") - project.ext.lwjglNatives = osArch.startsWith("arm") || osArch.startsWith("aarch64") - ? "natives-linux-${osArch.contains("64") || osArch.startsWith("armv8") ? "arm64" : "arm32"}" - : "natives-linux" - break - case OperatingSystem.MAC_OS: - project.ext.lwjglNatives = "natives-macos" - break - case OperatingSystem.WINDOWS: - project.ext.lwjglNatives = System.getProperty("os.arch").contains("64") ? "natives-windows" : "natives-windows-x86" - break -} - -dependencies { - implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion") - - implementation "org.lwjgl:lwjgl" - implementation "org.lwjgl:lwjgl-glfw" - implementation "org.lwjgl:lwjgl-openal" - implementation "org.lwjgl:lwjgl-opengl" - implementation "org.lwjgl:lwjgl-stb" - - runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" -} - -// LWJGL END - -/* - * Tasks - */ - -/* - * Additional native libraries specification - */ - -project.ext.platforms = new HashSet<>() - -task addNativeDependencies { - doFirst { - def archs = project.ext.platforms - - switch (archs.size()) { - case 0: - println "Adding LWJGL native dependencies for local platform only:\n\t$lwjglNatives" - archs.add project.ext.lwjglNatives - break - case 1: - println "Adding LWJGL native dependencies for platform\n\t" + archs.get(0) - break - default: - println "Adding LWJGL native dependencies for platforms:\n\t" + archs.join("\n\t") - } - - if (project.ext.lwjglNatives.isEmpty()) println "WTF" - - dependencies { - archs.each { arch -> - runtimeOnly "org.lwjgl:lwjgl::$arch" - runtimeOnly "org.lwjgl:lwjgl-glfw::$arch" - runtimeOnly "org.lwjgl:lwjgl-openal::$arch" - runtimeOnly "org.lwjgl:lwjgl-opengl::$arch" - runtimeOnly "org.lwjgl:lwjgl-stb::$arch" - } - } - } -} - -compileJava.mustRunAfter addNativeDependencies // Make sure runtimeOnly has not been resolved - -task requestLinuxDependencies { - description 'Adds linux, linux-arm64 and linux-arm32 native libraries to built artifacts.' - doFirst { - project.ext.platforms.addAll([ - 'natives-linux', - 'natives-linux-arm64', - 'natives-linux-arm32' - ]) - } -} -task requestWindowsDependencies { - description 'Adds windows and windows-x86 native libraries to built artifacts.' - doFirst { - project.ext.platforms.addAll([ - 'natives-windows', - 'natives-windows-x86' - ]) - } -} -task requestMacOSDependencies { - description 'Adds macos native libraries to built artifacts.' - doFirst { - project.ext.platforms.addAll(['natives-macos']) - } -} - -def dependencySpecificationTasks = tasks.findAll { task -> task.name.startsWith('request') && task.name.endsWith('Dependencies') } - -task requestCrossPlatformDependencies { - description 'Adds native libraries for all available platforms to built artifacts.' - dependsOn dependencySpecificationTasks -} - -addNativeDependencies.mustRunAfter dependencySpecificationTasks - -/* - * Determines if the provided dependency should be packaged - */ -def isDependencyRequested(String dep) { - if (dep.endsWith(".jar")) { - dep = dep.substring(0, dep.length() - ".jar".length()) - } - - return !dep.contains("natives-") || - project.ext.platforms.contains(dep.substring(dep.indexOf("natives-"), dep.length())) -} - -/* - * Manifest specification - */ - -task specifyLocalManifest { - dependsOn addNativeDependencies // Make sure all native dependencies are specified - - doFirst { - def classPath = [] - - configurations.runtimeClasspath.each { - if (isDependencyRequested(it.getName())) { - classPath.add("lib/" + it.getName()) - } else { - println "\tRemoving from JAR classpath (not requested): " + it.getName() - } - } - - if (classPath.size() == configurations.runtimeClasspath.size()) { - println "Nothing removed from JAR classpath" +String version_parseVersion(String tag, boolean increment) { + try { + + def data = (tag =~ tagFormat)[0] + + def major = data[1] + def minor = data[2] + def patch = data[3] as int + def suffix = data[4] ?: '' + + if (increment) { + def oldVersion = "$major.$minor.$patch$suffix" + patch++ + def newVersion = "$major.$minor.$patch$suffix" + + logger.info "Version parsed from Git: $oldVersion, incremented to $newVersion" + return newVersion + } else { + def newVersion = "$major.$minor.$patch$suffix" + logger.info "Version parsed from Git: $newVersion" + return newVersion } - def version = "dev"; - def commit = "-"; - def branch = "-"; - def buildId = project.findProperty('buildId') ?: "-"; + } catch (any) { + logger.warn "Could not parse version from tag \"$tag\"" + return tag + } +} + +Tag version_findRelevantTag(Grgit git) { + def tags = git.tag.list() + + def commits = [ git.head() ] + def visited = new HashSet<>() + + while (true) { + if (commits.isEmpty()) return null + + def nextCommits = new HashSet<>() + def formatSpecificationPrinted = false + for (def commit : commits) { + def tag = tags.findAll { it.commit == commit } ?.max { it.dateTime } + + if (tag != null) { + if (tag.name ==~ tagFormat) { + return tag + } else { + if (!formatSpecificationPrinted) { + formatSpecificationPrinted = true + logger.info 'Expecting tag format: vMAJOR.MINOR.PATCH[-SUFFIX]' + } + logger.info 'Ignoring tag due to invalid format: {}', tag.name + } + } + + nextCommits.addAll commit.parentIds.collect(git.resolve.&toCommit) + } + + visited.addAll commits + nextCommits.removeAll visited + commits = nextCommits + } +} + +task resolveVersion { + description 'Resolves version information from Git repository or project properties.' + + doFirst { try { - def git = org.ajoberstar.grgit.Grgit.open() - def head = git.head() + def git = Grgit.open(dir: project.projectDir) - commit = head.id - branch = git.branch.current().name + project.ext.commit = git.head().id + project.ext.branch = git.branch.current().name - def newestTag = git.tag.list().findAll { it.commit == head } ?.max { it.dateTime } ?.name - if (newestTag != null) { - version = newestTag; - } else if (project.hasProperty('buildId')) { - version = project.buildId; + if (project.version != 'unspecified') { + // Leave version as-is + return + } + + def tag = version_findRelevantTag(git) + if (tag == null) { + + String suffix + if (project.hasProperty('buildId')) { + suffix = project.buildId; + } else { + suffix = java.time.ZonedDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern('yyyy_MM_dd')) + } + project.version = "999.0.0-$suffix" + + logger.warn 'Git repository does not contain an applicable tag, using dummy version {}\nSpecify version with -Pversion=1.2.3 or create a Git tag named v1.2.3', project.version + } else { - version = head.dateTime.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE); + + project.version = version_parseVersion(tag.name, tag.commit != git.head()) + } } catch (org.eclipse.jgit.errors.RepositoryNotFoundException e) { - println "No Git repository found in project root" - } + if (project.version == 'unspecified') project.version = 'dev' + project.ext.commit = '-' + project.ext.branch = '-' - jar { - manifest { - attributes( - "Main-Class": "ru.windcorp.progressia.client.ProgressiaClientMain", - "Class-Path": configurations.runtimeClasspath.collect { "lib/" + it.getName() } .findAll { isDependencyRequested(it) } .join(' '), - - "Specification-Title": "Progressia", - - "Implementation-Title": "Progressia", - "Implementation-Version": version, - "Implementation-Version-Git-Commit": commit, - "Implementation-Version-Git-Branch": branch, - "Implementation-Version-BuildId": buildId, - ) - } + logger.warn 'No Git repository found in project root, using dummy version {}\nSpecify version with -Pversion=1.2.3 or create a Git tag named v1.2.3', project.version + } + } + + doLast { + if (!project.hasProperty('buildId')) { + project.ext.buildId = '-' } } } -jar.dependsOn specifyLocalManifest + /* - * Library export + * Configure JAR manifest + */ + +task configureManifest { + description 'Populates JAR manifest with Main-Class, Class-Path and version metadata.' + + jar.dependsOn configureManifest + dependsOn resolveVersion + + doFirst { + jar.manifest.attributes( + 'Main-Class': 'ru.windcorp.progressia.client.ProgressiaClientMain', + 'Class-Path': configurations.runtimeClasspath.collect { "lib/${it.name}" } .join(' '), + + 'Specification-Title': 'Progressia', + + 'Implementation-Title': 'Progressia', + 'Implementation-Version': project.version, + 'Implementation-Version-Git-Commit': project.commit, + 'Implementation-Version-Git-Branch': project.branch, + 'Implementation-Version-BuildId': project.buildId, + ) + } +} + + + +/* + * Copy libraries into buil/libs/lib directory, next to Progressia.jar */ task exportLibs(type: Sync) { - mustRunAfter addNativeDependencies + description 'Copies runtime libraries into a subdirectory next to the output JAR.' + + jar.dependsOn exportLibs - into libsDirectory.get().getAsFile().getPath() + "/lib" - exclude { !isDependencyRequested(it.getName()) } from configurations.runtimeClasspath + into 'build/libs/lib' } -jar.dependsOn(exportLibs) + /* - * Packaging + * Apply LWJGL logic + */ +apply from: 'build_logic/lwjgl.gradle' + + + +/* + * Packaging working directory configuration */ -task packageDebian(type: Exec) { - description 'Builds the project and creates a Debain package.' - group 'Progressia' - - dependsOn build - dependsOn requestLinuxDependencies - - commandLine './buildPackages.sh', 'debian' +import java.nio.file.* +import java.nio.file.attribute.* +task createPackagingDirs() { + description 'Resets build/tmp/packaging directory.' + doLast { - println "Debian package available in build_packages/" + def tmpDir = buildDir.toPath().resolve 'tmp/packaging' + + def nuke = new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { + if (e == null) { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } else { + // directory iteration failed + throw e; + } + } + } + + // Fckn nuke tmpDir from orbit + // I'm so done with deleting file trees in Java/Groovy/whatever + // ...Not using File.deleteDir() because the latter recurses into symlinks, and we don't want to wipe build/libs/lib + if (Files.exists(tmpDir)) { + Files.walkFileTree tmpDir, nuke + } + + Files.createDirectories tmpDir.resolve('workingDir') + Files.createDirectories buildDir.toPath().resolve('packages') + } +} + +task linkBuildOutputForPackaging() { + description 'Symlinks the contents of build/libs into packaging working directory.' + + dependsOn build + dependsOn createPackagingDirs + + onlyIf { preparePackaging.ext.mode == 'symlink' } + + doLast { + def from = buildDir.toPath().resolve 'libs' + def into = buildDir.toPath().resolve "tmp/packaging/workingDir/${preparePackaging.ext.buildDest}" + + Files.createDirectories into + + Files.list(from).each { + def fileName = it.fileName.toString() + + // Exclude all JARs except the current one + if (fileName ==~ "${project.name}.*\\.jar" && fileName != tasks.jar.archiveFileName.get()) + return + + Files.createSymbolicLink into.resolve(it.fileName), it + } + } +} + +task copyBuildOutputForPackaging(type: Copy) { + description 'Copies the contents of build/libs into packaging working directory.' + + dependsOn build + dependsOn createPackagingDirs + + onlyIf { preparePackaging.ext.mode == 'copy' } + + from 'build/libs' + filesMatching("${project.name}*.jar") { + include tasks.jar.archiveFileName.get() + } + into "build/tmp/packaging/workingDir/${ -> preparePackaging.ext.buildDest}" +} + +task preparePackaging { + preparePackaging.ext.buildDest = '' + preparePackaging.ext.mode = 'symlink' + + dependsOn createPackagingDirs + dependsOn linkBuildOutputForPackaging + dependsOn copyBuildOutputForPackaging +} + + + +/* + * Apply all packaging scripts + */ + +new File(projectDir, 'build_logic/packaging').list().each { + apply from: "build_logic/packaging/$it/script.gradle" +} + + + +/* + * Ensure no more than one packaging task is scheduled + */ + +gradle.taskGraph.whenReady { graph -> + if (graph.allTasks.count { it.name ==~ /package[^_]*/ } > 1) { + def offenders = graph.allTasks.findAll { it.name ==~ /package[^_]*/ } + throw new GradleException("Cannot execute multiple package tasks within a single build\n" + + "\tOffending tasks: $offenders") } } -task packageWindows(type: Exec) { - description 'Builds the project and creates a Windows installer.' - group 'Progressia' - dependsOn build - dependsOn requestWindowsDependencies - commandLine './buildPackages.sh', 'windows' - - doLast { - println "Windows installer available in build_packages/" - } -} +/* + * Convenience build tasks + */ task buildCrossPlatform { description 'Builds the project including native libraries for all available platforms.' @@ -340,17 +391,17 @@ task buildCrossPlatform { dependsOn build doLast { - println "Native libraries for all platforms have been added" + logger.info 'Native libraries for all platforms have been added' } } task buildLocal { - description "Builds the project including only native libraries for current platform ($lwjglNatives)." + description "Builds the project including only native libraries for current platform (${lwjgl.localArch})." group 'Progressia' dependsOn build doLast { - println "Native libraries only for platform $lwjglNatives have been added" + logger.info "Native libraries only for platform ${lwjgl.localArch} have been added" } } diff --git a/buildPackages.sh b/buildPackages.sh deleted file mode 100755 index 464d641..0000000 --- a/buildPackages.sh +++ /dev/null @@ -1,191 +0,0 @@ -#!/bin/bash - -# -# Progressia -# Copyright (C) 2020-2021 Wind Corporation and contributors -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -echoerr() { echo "$@" 1>&2; } - -buildDebianPackage() { - - # Commands that must be available to execute this action - requiredCommands='dpkg-deb fakeroot' - - # Package name. Sync with control file manually! - name='progressia-techdemo' - # Version that the package will receive. Sync with control file manually! - version='1.0_all' - - # This directory will be copied into $tmpDir - templateDirectory="build_packages/DEB/template" - - # Files that must be present - requiredFiles="$templateDirectory/DEBIAN/control" - - nameAndVersion="$name-$version" - tmpDir="build_packages/DEB/$nameAndVersion" - outputFile="build_packages/DEB/$nameAndVersion.deb" - - echo "Checking environment to build Debian package" - - for item in $requiredCommands; do - if command -v "$item" &> /dev/null; then - echo "- $item found" - else - echoerr "Command $item not found, cannot package" - exit 100 - fi - done - - for file in $requiredFiles; do - if ! [ -r "$file" ]; then - echoerr "$file is missing or not readable, cannot package" - exit 101 - else - echo "- $file is present and readable" - fi - done - - echo "Environment OK; packaging Debian package" - exitCode=0 - - { - shareDir="$tmpDir/usr/share/progressia" - - mkdir -p "$tmpDir" && - mkdir -p "$shareDir" && - cp -r "$templateDirectory"/* "$tmpDir" && - cp -r 'build/libs/lib' "$shareDir/lib" && - cp 'build/libs/Progressia.jar' "$shareDir/Progressia.jar" && - echo "------ DPKG-DEB ------" && - fakeroot dpkg-deb --build "$tmpDir" && - echo "---- DPKG-DEB END ----" && - mv "$outputFile" build_packages - } || { - echoerr "Could not create Debian package" - exitCode=1 - } - - { - if [ -d "$tmpDir" ]; then - rm -r "$tmpDir" - fi - echo "Cleaned up" - } || { - echoerr "Could not clean up after packaging Debian package" - exitCode=2 - } - - exit "$exitCode" -} - -buildWindowsInstaller() { - - # Commands that must be available to execute this action - requiredCommands='makensis' - - # NSIS configuration file that must be present - configurationFile='build_packages/NSIS/ProgressiaInstaller.nsi' - - # File that will be output - outputFile='build_packages/NSIS/ProgressiaInstaller.exe' - - echo "Checking environment to build Windows installer" - - for item in $requiredCommands; do - if command -v "$item" &> /dev/null; then - echo "- $item found" - else - echoerr "Command $item not found, cannot build" - exit 100 - fi - done - - if ! [ -r "$configurationFile" ]; then - echoerr "$configurationFile is missing or not readable, cannot build" - exit 101 - else - echo "- $configurationFile is present and readable" - fi - - echo "Environment OK; building Windows installer" - exitCode=0 - - { - cp -r 'build/libs/lib' 'build_packages/NSIS/lib' && - cp 'build/libs/Progressia.jar' 'build_packages/NSIS/Progressia.jar' && - cp 'LICENSE' 'build_packages/NSIS/LICENSE.txt' && - echo "------ NSIS ------" && - makensis "$configurationFile" && - echo "---- NSIS END ----" && - mv "$outputFile" build_packages - } || { - echoerr "Could not build Windows installer" - exitCode=1 - } - - { - if [ -d 'build_packages/NSIS/lib' ]; then - rm -r 'build_packages/NSIS/lib' - fi - if [ -e 'build_packages/NSIS/Progressia.jar' ]; then - rm 'build_packages/NSIS/Progressia.jar' - fi - if [ -e 'build_packages/NSIS/LICENSE.txt' ]; then - rm 'build_packages/NSIS/LICENSE.txt' - fi - echo "Cleaned up" - } || { - echoerr "Could not clean up after building Windows installer" - exitCode=2 - } - - exit "$exitCode" -} - -printUsage() { - echoerr "Usage: $0 TARGET" - echoerr " where TARGET is 'debian' or 'windows'" -} - -if [ -n "$2" ]; then - echoerr "Too many arguments." - printUsage - exit 202 -fi - -case "$1" in -"debian") - buildDebianPackage - ;; -"windows") - buildWindowsInstaller - ;; -"") - echoerr "No action specified" - printUsage - exit 200 - ;; -"--help" | "-help" | "help" | "?") - printUsage - ;; -*) - echoerr "Unknown action '$1'" - printUsage - exit 201 - ;; -esac diff --git a/build_logic/lwjgl.gradle b/build_logic/lwjgl.gradle new file mode 100644 index 0000000..4d9f68e --- /dev/null +++ b/build_logic/lwjgl.gradle @@ -0,0 +1,117 @@ +/* + * Build logic for Progressia + * LWJGL dependency logic + */ + +project.ext.lwjgl = new HashMap<>() + +// Version of LWJGL +lwjgl.version = '3.2.3' + +/* + * Target platforms for current operation. + * This is filled in by the request* tasks. This is referenced by the addLwjglNatives task. + * When empty, current platform is assumed. + */ +lwjgl.targets = new HashSet<>() + +// LWJGL components. To include org.lwjgl:lwjgl-foobar, add 'foobar' to this list. +lwjgl.libraries = [ + 'opengl', + 'glfw', + 'openal', + 'stb' +] + +// Determine the architecture of the build environment +import org.gradle.internal.os.OperatingSystem +switch (OperatingSystem.current()) { + case OperatingSystem.LINUX: + def osArch = System.getProperty('os.arch') + lwjgl.localArch = osArch.startsWith('arm') || osArch.startsWith('aarch64') + ? "linux-${osArch.contains('64') || osArch.startsWith('armv8') ? 'arm64' : 'arm32'}" + : 'linux' + break + case OperatingSystem.MAC_OS: + lwjgl.localArch = 'macos' + break + case OperatingSystem.WINDOWS: + lwjgl.localArch = System.getProperty('os.arch').contains('64') ? 'windows' : 'windows-x86' + break +} + +// Declare pure-Java dependencies +dependencies { + // BOM + implementation platform("org.lwjgl:lwjgl-bom:${lwjgl.version}") + + // Core + implementation 'org.lwjgl:lwjgl' + + // Components + lwjgl.libraries.each { implementation "org.lwjgl:lwjgl-$it" } +} + +/* + * Adds LWJGL native libraries to runtimeOnly configuration + */ +task lwjgl_addNativesToRuntimeOnly { + // Make sure runtimeOnly has not been resolved + compileJava.dependsOn lwjgl_addNativesToRuntimeOnly + configureManifest.dependsOn lwjgl_addNativesToRuntimeOnly + exportLibs.dependsOn lwjgl_addNativesToRuntimeOnly + + doFirst { + if (project.hasProperty('forceTargets')) { + try { + def oldTargets = lwjgl.targets.join(',') + + lwjgl.targets.clear() + lwjgl.targets.addAll project.forceTargets.split(',')*.trim().collect { it == 'local' ? lwjgl.localArch : it } + + logger.info 'Overriding selected platforms {} with {}', oldTargets, lwjgl.targets.join(',') + } catch (Exception e) { + throw new GradleException("Could not parse forceTargets \"${project.forceTargets}\", expecting platform-1,platform-2,local", e) + } + } + + if (lwjgl.targets.isEmpty()) { + logger.info 'Adding LWJGL native dependencies for local platform only: {}', lwjgl.localArch + lwjgl.targets.add lwjgl.localArch + } else { + logger.info 'Adding LWJGL native dependencies for platforms: {}', lwjgl.targets.sort().join(', ') + } + + dependencies { + lwjgl.targets.each { target -> + runtimeOnly "org.lwjgl:lwjgl::natives-$target" + lwjgl.libraries.each { lib -> + runtimeOnly "org.lwjgl:lwjgl-$lib::natives-$target" + } + } + } + } +} + +task requestCrossPlatformDependencies { + description 'Adds LWJGL natives for all available platforms.' + + lwjgl_addNativesToRuntimeOnly.mustRunAfter requestCrossPlatformDependencies +} + +def requestTask(String name, String... targets) { + def theTask = task "request${name}Dependencies" + + theTask.doFirst { + lwjgl.targets.addAll targets + } + + theTask.description "Adds LWJGL natives for $name (${targets.join(', ')})." + + requestCrossPlatformDependencies.dependsOn theTask + lwjgl_addNativesToRuntimeOnly.mustRunAfter theTask +} + +requestTask 'Linux', 'linux', 'linux-arm32', 'linux-arm64' +requestTask 'Windows', 'windows', 'windows-x86' +requestTask 'MacOS', 'macos' diff --git a/build_logic/packaging/deb/script.gradle b/build_logic/packaging/deb/script.gradle new file mode 100644 index 0000000..f5c380b --- /dev/null +++ b/build_logic/packaging/deb/script.gradle @@ -0,0 +1,38 @@ +task packageDeb_processResources(type: Copy) { + dependsOn resolveVersion + dependsOn preparePackaging + + from 'src/packaging/deb' + + filesMatching('DEBIAN/control') { + expand(version: { -> project.version}) + } + + into 'build/tmp/packaging/workingDir' +} + +task packageDeb_configure() { + preparePackaging.mustRunAfter packageDeb_configure + + doLast { + tasks.preparePackaging.ext.buildDest = '/usr/share/progressia' + tasks.preparePackaging.ext.mode = 'copy' + } +} + +task packageDeb(type: Exec) { + description 'Builds the project and creates a Debian package.' + group 'Progressia' + + dependsOn packageDeb_configure + dependsOn requestLinuxDependencies + dependsOn build + dependsOn preparePackaging + + dependsOn packageDeb_processResources + + executable 'dpkg-deb' + args '--root-owner-group' + args '--build', 'build/tmp/packaging/workingDir' + args 'build/packages' +} diff --git a/build_logic/packaging/nsis/script.gradle b/build_logic/packaging/nsis/script.gradle new file mode 100644 index 0000000..fe03b0f --- /dev/null +++ b/build_logic/packaging/nsis/script.gradle @@ -0,0 +1,50 @@ +task packageNsis_processResources(type: Copy) { + dependsOn preparePackaging + + from ('src/packaging/nsis') { + exclude 'left_side.png' + } + from('LICENSE') { + rename 'LICENSE', 'LICENSE.txt' + } + into 'build/tmp/packaging/workingDir' +} + +task packageNsis_generateIcon(type: Exec) { + mustRunAfter preparePackaging + + executable 'convert' + args files('src/main/resources/assets/icons/*.original.png').files*.path + args 'build/tmp/packaging/workingDir/logo.ico' +} + +task packageNsis_generateLeftSide(type: Exec) { + mustRunAfter preparePackaging + + executable 'convert' + args 'src/packaging/nsis/left_side.png' + args '-alpha', 'off' + args 'BMP3:build/tmp/packaging/workingDir/left_side.bmp' +} + +task packageNsis(type: Exec) { + description 'Builds the project and creates a Windows NSIS installer.' + group 'Progressia' + + dependsOn requestWindowsDependencies + dependsOn build + dependsOn resolveVersion + dependsOn preparePackaging + + dependsOn packageNsis_processResources + dependsOn packageNsis_generateIcon + dependsOn packageNsis_generateLeftSide + + executable 'makensis' + args '-NOCONFIG' + args "-DPROJECT_NAME=${project.name}" + args "-DPROJECT_VERSION=${ -> project.version}" + args "-DMAIN_JAR_FILE=${ -> project.tasks.jar.archiveFileName.get()}" + args "-DOUTPUT_DIR=${project.buildDir.absolutePath}/packages" + args 'build/tmp/packaging/workingDir/config.nsi' +} diff --git a/build_logic/packaging/zip/script.gradle b/build_logic/packaging/zip/script.gradle new file mode 100644 index 0000000..4975ac8 --- /dev/null +++ b/build_logic/packaging/zip/script.gradle @@ -0,0 +1,41 @@ +task packageZip_processResources(type: Copy) { + dependsOn preparePackaging + + from ('src/packaging/zip') { + filesMatching('start.*') { + filter( + org.apache.tools.ant.filters.ReplaceTokens, + tokens: [mainJarFile: project.tasks.jar.archiveFileName.get()] + ) + } + } + from ('src/main/resource/assets/icons/logo256.original.png') { + rename 'logo256.original.png', 'logo.png' + } + from('LICENSE') { + rename 'LICENSE', 'LICENSE.txt' + } + into 'build/tmp/packaging/workingDir' +} + +task packageZip(type: Zip) { + description 'Builds the project and creates a cross-platform ZIP package.' + group 'Progressia' + + dependsOn resolveVersion + dependsOn requestCrossPlatformDependencies + dependsOn build + dependsOn preparePackaging + + dependsOn packageZip_processResources + + archiveBaseName = project.name + archiveAppendix = 'universal' + + doFirst { + archiveVersion = project.version + } + + from 'build/tmp/packaging/workingDir' + destinationDirectory = file('build/packages') +} diff --git a/build_packages/NSIS/left_side.bmp b/build_packages/NSIS/left_side.bmp deleted file mode 100644 index 1dd523c..0000000 Binary files a/build_packages/NSIS/left_side.bmp and /dev/null differ diff --git a/build_packages/NSIS/logo.ico b/build_packages/NSIS/logo.ico deleted file mode 100644 index e858a48..0000000 Binary files a/build_packages/NSIS/logo.ico and /dev/null differ diff --git a/docs/building/BuildGuide.md b/docs/building/BuildGuide.md index 23833e9..a91eef8 100644 --- a/docs/building/BuildGuide.md +++ b/docs/building/BuildGuide.md @@ -1,8 +1,10 @@ # Build Guide -This document is a guide to building Progressia from source. +This document is a guide to building Progressia from source. For quick reference, see +[Build Script Reference](BuildScriptReference.md). -Compilation should be possible on all platforms that support JDK 8 or later, however, packaging scripts require Bash. +Compilation should be possible on all platforms that support JDK 8 or later, however, packaging scripts require +additional programs in `PATH`. This guide assumes you are familiar with using a terminal or Windows Command Prompt or PowerShell. @@ -150,54 +152,46 @@ GNU/Linux and Windows natives: ./gradlew build requestLinuxDependencies requestWindowsDependencies ``` -For finer control please edit `build.gradle` manually by adding the desired natives to the `project.ext.platforms` set like so: - -``` -project.ext.platforms = new HashSet<>() -project.ext.platforms.add 'natives-windows-x86' -``` - ## Packaging -A Debian package and a Windows installer can be created automatically on systems that support Bash. These tasks are delegated -by Gradle to `buildPackages.sh` in repository root. This script checks the environment and assembles the requested output; the -resulting files are moved into `build_packages`. +A universal ZIP distribution, a Debian package and a Windows NSIS installer may be created automatically by the build +script. + +### Creating a universal ZIP package + +A universal cross-platform ZIP archive can be created with the following Gradle task: + +``` +./gradlew packageZip +``` + +Gradle will then build all artifacts necessary to run the game on all available platforms and package game files, +libraries, launch scripts, etc. into a compressed ZIP archive. + +The resulting file can be found in `build/packages/` ### Creating a Debian package A Debian package can be created with the following Gradle task: ``` -./gradlew packageDebian +./gradlew packageDeb ``` Gradle will then build all artifacts necessary to run the game on GNU/Linux (all three architectures) and invoke -`./buildPackages.sh debian`. Commands `dpkg-deb` and `fakeroot` must be available in system path in order to build the package. +`dpkg-deb`. Commands `dpkg-deb` must be available in system path in order to build the package. ### Creating a Windows installer -A Windows installer can be created with the following Gradle task: +A Windows NSIS installer can be created with the following Gradle task: ``` -./gradlew packageWindows +./gradlew packageNsis ``` Gradle will then build all artifacts necessary to run the game on Windows (both x64 and x86 architectures) and invoke -`./buildPackages.sh windows`. +`makensis`. -Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). Command `makensis` must be available in system -path in order to build the installer. - -## Gradle tasks summary - -- `buildLocal` – creates a build optimized for current platform. Use this to quickly build the game during development. -- `buildCrossPlatform` – creates a build that supports all known architectures. Use this to build a universal version of the game. -- `build` – currently a synonym of `buildLocal`; creates a default build. -- `packageDebian` – creates a Debian package. Do not invoke together with `packageWindows`. -- `packageWindows` – creates a Windows installer. Do not invoke together with `packageDebian`. -- `requestLinuxDependencies` – requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included when building. -- `requestWindowsDependencies` – requests that `natives-windows` and `natives-windows-x86` binaries are included when building. -- `requestMacOSDependencies` – requests that `natives-macos` binaries are included when building. -- `requestCrossPlatformDependencies` – requests that all binaries are included when building. - -All other basic and Java-related Gradle tasks are available as well. \ No newline at end of file +Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). [ImageMagick](https://imagemagick.org), +a command-line image editing tool, is used to generate some assets for the installer. Commands `makensis` and +`convert` (from ImageMagick) must be available in system path in order to build the installer. diff --git a/docs/building/BuildScriptReference.md b/docs/building/BuildScriptReference.md new file mode 100644 index 0000000..c5d6b46 --- /dev/null +++ b/docs/building/BuildScriptReference.md @@ -0,0 +1,103 @@ +# Build Script Reference + +This document is a user's reference for the build script of Progressia. For a beginner-friendly guide, see +[Build Guide](BuildGuide.md). + +## Gradle tasks summary + +- `buildLocal` – creates a build optimized for current platform. Use this to quickly build the game during development. +- `buildCrossPlatform` – creates a build that supports all known architectures. Use this to build a universal version of the game. +- `build` – currently a synonym of `buildLocal`; creates a default build. +- `packageZip` – creates a universal ZIP. Incompatible with other `package` tasks. +- `packageDeb` – creates a Debian package. Incompatible with other `package` tasks. +- `packageNsis` – creates a Windows NSIS installer. Incompatible with other `package` tasks. +- `requestLinuxDependencies` – requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included when building. +- `requestWindowsDependencies` – requests that `natives-windows` and `natives-windows-x86` binaries are included when building. +- `requestMacOSDependencies` – requests that `natives-macos` binaries are included when building. +- `requestCrossPlatformDependencies` – requests that all binaries are included when building. + +To execute a task, run `./gradlew `. + +`build`-type tasks output the executable JAR and all libraries required at runtime into `build/libs`. `package`-type +tasks output packages into `build/packages`. + +## Packaging tasks + +Some packaging tasks require additional software in `PATH`. + +| Task | Commands required in `PATH` | +|---------------|------------------------------------------| +| `packageDeb` | `dpkg-deb` | +| `packageZip` | _none_ | +| `packageNsis` | `makensis`, `convert` (from ImageMagick) | + +## Version and metadata + +### Version scheme + +Progressia builds are identified by four parameters: version, Git commit, Git branch and build ID. + +Versions roughly follow [semantic versioning](https://semver.org/spec/v2.0.0.html), with each version fitting the +`MAJOR.MINOR.PATCH[-SUFFIX]` pattern. Depending on the build environment (see below), version is either "real" with +no metadata (e.g. `0.43.2` or `1.2.1-beta`) or a dummy fallback with build metadata (e.g. `999.0.0-2021_07_23` or +`999.0.0-WJ3`). + +### Version detection + +Build script considers three scenarios when determining the version: + +1. `version` project property is set explicitly. This may be done in a variety of ways, for example with command line +argument `-Pversion=1.2.3` +(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties)) +2. Local Git repository is found, and HEAD is tagged appropriately: version is the tag name with leading `v` +stripped. Example: `v1.2.3` is version `1.2.3` +3. Local Git repository is found, and some ancestor of HEAD is tagged appropriately: version is the tag name with +leading `v` stripped and PATCH incremented by one. Example: `v1.2.3` is version `1.2.4` + +Tags not named like `vMAJOR.MINOR.PATCH[-SUFFIX]` are ignored for cases 2 and 3. + +In all other cases, a fallback dummy value is used for version, appended with build ID or current date. + +### Git metadata + +Git commit and Git branch are correspond to the state of the local Git repository, if any. In case Git metadata is +unavailable, `-` fallback is used for both fields. + +### Build ID + +Build ID uniquely identifies artifacts produced by automated build systems. For example, builds executed by WindCorp +Jenkins suite have build IDs like `WJ3` or `WJ142`. Build ID must be provided explicitly; it is `-` unless specified +otherwise. + +Build ID may be set with `buildId` project property. This may be done in a variety of ways, for example with command +line argument `-PbuildId=WJ3` +(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties)). + +## Native libraries + +LWJGL uses native libraries. Build script declares platform-specific dependencies based on the set of target +platforms, `project.ext.lwjgl.targets` (aka `lwjgl.targets`). These dependencies are added to `runtimeOnly` +configuration. + +When this set is empty, the script selects natives for current platform. Otherwise, all platforms in the set are +included. + +`lwjgl.targets` is populated automatically by packaging tasks and by `buildCrossPlatform`. To add extra targets, +``requestXxxDependencies` tasks may be used. + +Target selection mechanism may be overridden with `forceTargets` project property. This may be done in a variety of +ways, for example with command line argument `-PforceTargets=windows-x86,local` +(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties)). The +value is a comma-separated list of target architectures. `local` target will be replaced with the automatically +detected current architecture. + +### Available targets + +| Name | Task | +|---------------|------------------------------| +| `linux` | `requestLinuxDependencies` | +| `linux-arm32` | `requestLinuxDependencies` | +| `linux-arm64` | `requestLinuxDependencies` | +| `windows` | `requestWindowsDependencies` | +| `windows-x86` | `requestWindowsDependencies` | +| `macos` | `requestMacOSDependencies` | diff --git a/build_packages/DEB/template/DEBIAN/control b/src/packaging/deb/DEBIAN/control similarity index 56% rename from build_packages/DEB/template/DEBIAN/control rename to src/packaging/deb/DEBIAN/control index 0095cf7..fde9093 100644 --- a/build_packages/DEB/template/DEBIAN/control +++ b/src/packaging/deb/DEBIAN/control @@ -1,8 +1,8 @@ -Package: progressia-techdemo -Version: 1.0 +Package: progressia +Version: ${version} Section: custom Priority: optional Architecture: all Maintainer: Javapony Depends: java8-runtime -Description: Progressia Techdemo release +Description: Progressia - a 3D sandbox survival game diff --git a/build_packages/NSIS/ProgressiaInstaller.nsi b/src/packaging/nsis/config.nsi similarity index 86% rename from build_packages/NSIS/ProgressiaInstaller.nsi rename to src/packaging/nsis/config.nsi index abf8d73..0eb5d1f 100644 --- a/build_packages/NSIS/ProgressiaInstaller.nsi +++ b/src/packaging/nsis/config.nsi @@ -1,161 +1,164 @@ -;NSIS Modern User Interface -;Welcome/Finish Page Example Script -;Written by Joost Verburg - -;-------------------------------- -;Include Modern UI - - !include "MUI2.nsh" - -;-------------------------------- -;General - - !define PROJECT_NAME "Progressia" - - ; MUI Settings / Icons - !define MUI_ICON "logo.ico" - ;!define MUI_UNICON ;Uninstall icon - - ; MUI Settings / Header - ; !define MUI_HEADERIMAGE - ; !define MUI_HEADERIMAGE_RIGHT - ; !define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-r-nsis.bmp" - ; !define MUI_HEADERIMAGE_UNBITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-uninstall-r-nsis.bmp" - - ; MUI Settings / Wizard - !define MUI_WELCOMEFINISHPAGE_BITMAP "left_side.bmp" - !define MUI_UNWELCOMEFINISHPAGE_BITMAP "left_side.bmp" - - ;Name and file - Name "${PROJECT_NAME}" - OutFile "${PROJECT_NAME}Installer.exe" - Unicode True - - ;Default installation folder - InstallDir "$PROGRAMFILES\${PROJECT_NAME}" - - ;Get installation folder from registry if available - InstallDirRegKey HKLM "Software\${PROJECT_NAME}" "" - - ;Request application privileges for Windows Vista - RequestExecutionLevel admin - -;-------------------------------- -;Interface Settings - - !define MUI_ABORTWARNING - -;-------------------------------- -;Pages - - !insertmacro MUI_PAGE_WELCOME - !insertmacro MUI_PAGE_LICENSE "LICENSE.txt" - !insertmacro MUI_PAGE_COMPONENTS - !insertmacro MUI_PAGE_DIRECTORY - !insertmacro MUI_PAGE_INSTFILES - !define MUI_FINISHPAGE_RUN - !define MUI_FINISHPAGE_RUN_TEXT "Start ${PROJECT_NAME}" - !define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink" - !insertmacro MUI_PAGE_FINISH - - !insertmacro MUI_UNPAGE_WELCOME - !insertmacro MUI_UNPAGE_CONFIRM - !insertmacro MUI_UNPAGE_COMPONENTS - !insertmacro MUI_UNPAGE_INSTFILES - !insertmacro MUI_UNPAGE_FINISH - -;-------------------------------- -;Languages - - !insertmacro MUI_LANGUAGE "English" - -;-------------------------------- -;Installer Sections - -Section "Install ${PROJECT_NAME}" SEC0000 - - SectionIn RO ;Make it read-only - SetOutPath "$INSTDIR" - SetOverwrite on - - ;Files - File Progressia.jar - File logo.ico - File /r lib - - ;Store installation folder - WriteRegStr HKLM SOFTWARE\Progressia "Install_Dir" "$INSTDIR" - - ;Create uninstaller - - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "DisplayName" "${PROJECT_NAME} (remove only)" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe" - WriteUninstaller "$INSTDIR\Uninstall.exe" - -SectionEnd - -Section "Create Desktop Shortcut" SEC0001 - SetOutPath "$APPDATA\${PROJECT_NAME}" - CreateShortCut "$DESKTOP\${PROJECT_NAME}.lnk" "$INSTDIR\${PROJECT_NAME}.jar" "" "$INSTDIR\logo.ico" -SectionEnd - -Section "Start Menu Shortcuts" SEC0002 - - CreateDirectory "$SMPROGRAMS\${PROJECT_NAME}" - CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe" - CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk" "$INSTDIR\${PROJECT_NAME}.jar" "" "$INSTDIR\logo.ico" - -SectionEnd - -;-------------------------------- -;Uninstaller Section - -Section "Uninstall" - - ;ADD YOUR OWN FILES HERE... - - Delete $INSTDIR\Uninstall.exe - Delete $INSTDIR\Progressia.jar - Delete $INSTDIR\lib\*.* - Delete $INSTDIR\logo.ico - - RMDir $INSTDIR\lib - - Delete $DESKTOP\${PROJECT_NAME}.lnk - - Delete $SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk - Delete $SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk - - RMDir $INSTDIR - - RMDir /r $SMPROGRAMS\${PROJECT_NAME} - - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" - DeleteRegKey HKLM "Software\${PROJECT_NAME}" - -SectionEnd - -Section "un.Remove user data" - - RMDir /r "$APPDATA\${PROJECT_NAME}" - -SectionEnd - -;-------------------------------- -;Functions - -Function LaunchLink - SetOutPath "$APPDATA\${PROJECT_NAME}" - ExecShell "" "$INSTDIR\${PROJECT_NAME}.jar" -FunctionEnd - -;-------------------------------- -;Descriptions - - ;Language strings - LangString DESC_SecDummy ${LANG_ENGLISH} "Install ${PROJECT_NAME}." - - ;Assign language strings to sections - !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN - !insertmacro MUI_DESCRIPTION_TEXT ${SEC0000} $(DESC_SecDummy) - !insertmacro MUI_FUNCTION_DESCRIPTION_END \ No newline at end of file +;NSIS Modern User Interface +;Welcome/Finish Page Example Script +;Written by Joost Verburg + +;-------------------------------- +;Include Modern UI + + !include "MUI2.nsh" + +;-------------------------------- +;General + + ; Expecting the following symbols from caller: + ; PROJECT_NAME + ; PROJECT_VERSION + ; MAIN_JAR_FILE + + ; MUI Settings / Icons + !define MUI_ICON "logo.ico" + ;!define MUI_UNICON ;Uninstall icon + + ; MUI Settings / Header + ; !define MUI_HEADERIMAGE + ; !define MUI_HEADERIMAGE_RIGHT + ; !define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-r-nsis.bmp" + ; !define MUI_HEADERIMAGE_UNBITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-uninstall-r-nsis.bmp" + + ; MUI Settings / Wizard + !define MUI_WELCOMEFINISHPAGE_BITMAP "left_side.bmp" + !define MUI_UNWELCOMEFINISHPAGE_BITMAP "left_side.bmp" + + ;Name and file + Name "${PROJECT_NAME}" + OutFile "${OUTPUT_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}-installer.exe" + Unicode True + + ;Default installation folder + InstallDir "$PROGRAMFILES\${PROJECT_NAME}" + + ;Get installation folder from registry if available + InstallDirRegKey HKLM "Software\${PROJECT_NAME}" "" + + ;Request application privileges for Windows Vista + RequestExecutionLevel admin + +;-------------------------------- +;Interface Settings + + !define MUI_ABORTWARNING + +;-------------------------------- +;Pages + + !insertmacro MUI_PAGE_WELCOME + !insertmacro MUI_PAGE_LICENSE "LICENSE.txt" + !insertmacro MUI_PAGE_COMPONENTS + !insertmacro MUI_PAGE_DIRECTORY + !insertmacro MUI_PAGE_INSTFILES + !define MUI_FINISHPAGE_RUN + !define MUI_FINISHPAGE_RUN_TEXT "Start ${PROJECT_NAME}" + !define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink" + !insertmacro MUI_PAGE_FINISH + + !insertmacro MUI_UNPAGE_WELCOME + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_COMPONENTS + !insertmacro MUI_UNPAGE_INSTFILES + !insertmacro MUI_UNPAGE_FINISH + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" + +;-------------------------------- +;Installer Sections + +Section "Install ${PROJECT_NAME}" SEC0000 + + SectionIn RO ;Make it read-only + SetOutPath "$INSTDIR" + SetOverwrite on + + ;Files + File "${MAIN_JAR_FILE}" + File logo.ico + File /r lib + + ;Store installation folder + WriteRegStr HKLM "SOFTWARE\${PROJECT_NAME}" "Install_Dir" "$INSTDIR" + + ;Create uninstaller + + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "DisplayName" "${PROJECT_NAME} (remove only)" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe" + WriteUninstaller "$INSTDIR\Uninstall.exe" + +SectionEnd + +Section "Create Desktop Shortcut" SEC0001 + SetOutPath "$APPDATA\${PROJECT_NAME}" + CreateShortCut "$DESKTOP\${PROJECT_NAME}.lnk" "$INSTDIR\${MAIN_JAR_FILE}" "" "$INSTDIR\logo.ico" +SectionEnd + +Section "Start Menu Shortcuts" SEC0002 + + CreateDirectory "$SMPROGRAMS\${PROJECT_NAME}" + CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe" + CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk" "$INSTDIR\${MAIN_JAR_FILE}" "" "$INSTDIR\logo.ico" + +SectionEnd + +;-------------------------------- +;Uninstaller Section + +Section "Uninstall" + + ;ADD YOUR OWN FILES HERE... + + Delete $INSTDIR\Uninstall.exe + Delete "$INSTDIR\${MAIN_JAR_FILE}" + Delete $INSTDIR\lib\*.* + Delete $INSTDIR\logo.ico + + RMDir $INSTDIR\lib + + Delete $DESKTOP\${PROJECT_NAME}.lnk + + Delete $SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk + Delete $SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk + + RMDir $INSTDIR + + RMDir /r $SMPROGRAMS\${PROJECT_NAME} + + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" + DeleteRegKey HKLM "Software\${PROJECT_NAME}" + +SectionEnd + +Section "un.Remove user data" + + RMDir /r "$APPDATA\${PROJECT_NAME}" + +SectionEnd + +;-------------------------------- +;Functions + +Function LaunchLink + SetOutPath "$APPDATA\${PROJECT_NAME}" + ExecShell "" "$INSTDIR\${MAIN_JAR_FILE}" +FunctionEnd + +;-------------------------------- +;Descriptions + + ;Language strings + LangString DESC_SecDummy ${LANG_ENGLISH} "Install ${PROJECT_NAME}." + + ;Assign language strings to sections + !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${SEC0000} $(DESC_SecDummy) + !insertmacro MUI_FUNCTION_DESCRIPTION_END diff --git a/src/packaging/nsis/left_side.png b/src/packaging/nsis/left_side.png new file mode 100644 index 0000000..2c08662 Binary files /dev/null and b/src/packaging/nsis/left_side.png differ diff --git a/src/packaging/zip/start.bat b/src/packaging/zip/start.bat new file mode 100644 index 0000000..867a706 --- /dev/null +++ b/src/packaging/zip/start.bat @@ -0,0 +1,2 @@ +@ECHO OFF +java -jar "@mainJarFile@" diff --git a/src/packaging/zip/start.sh b/src/packaging/zip/start.sh new file mode 100755 index 0000000..3ba1381 --- /dev/null +++ b/src/packaging/zip/start.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +jvmFlags="" + +case "$OSTYPE" in + "darwin"*) + # On MacOS, use -XstartOnFirstThread to resolve an issue with OpenGL contexts + jvmFlags="$jvmFlags -XstartOnFirstThread" +esac + +java $jvmFlags -jar "@mainJarFile@"