Skip to content

Instantly share code, notes, and snippets.

@DrPlantabyte
Last active July 20, 2017 11:54
Show Gist options
  • Save DrPlantabyte/3d1fc5341d6fc976c8fa88eba7580d06 to your computer and use it in GitHub Desktop.
Save DrPlantabyte/3d1fc5341d6fc976c8fa88eba7580d06 to your computer and use it in GitHub Desktop.
This is my default build.gradle file, which has a createProject task that makes all of the folders so you don't have to guess the convention and even intializes it with a blank JavaFX app
// buildscript blocks MUST go before plugins
buildscript {
dependencies {
classpath group: 'de.dynamicfiles.projects.gradle.plugins', name: 'javafx-gradle-plugin', version: '8.8.2'
}
repositories {
mavenLocal()
mavenCentral()
}
}
// java plugins
plugins {
id 'org.unbroken-dome.test-sets' version '1.3.2'
}
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'javafx-gradle-plugin'
def jvmVersion = '1.8'
targetCompatibility = jvmVersion
sourceCompatibility = jvmVersion
///// Project settings /////
// - change before running the createProject task
mainClassName = 'cch.appname.App'
archivesBaseName = 'App Name'
def resourceName = 'app' // simple name used for resources such as icons and executable names
def versionFileName = 'version.properties'
def authorVendor = 'CCHall'
////////////////////////////
// customizing build folder
def sourceDir = 'src'
def resourceDir = 'resources'
def testDir = 'unit-test'
def integrationTestDir = 'integration-test'
def testResources = "${testDir}-${resourceDir}"
def integrationTestResources = "${integrationTestDir}-${resourceDir}"
def importDir = 'lib'
def buildDirName = 'build'
def nativeInstallerResources = 'deploy-resources'
buildDir = buildDirName // buildDir is a file, not a string, unlike the other properties
libsDirName = 'jar'
distsDirName = 'distributions'
docsDirName = 'code-doc'
testResultsDirName = 'test-results'
testReportDirName = 'tests'
def runDir = 'run'
// add integration testing with the testsets plugin
testSets {
integrationTest { dirName = integrationTestDir }
}
project.integrationTest {
outputs.upToDateWhen { false }
}
tasks.withType(Test) {
// fixes issue where integration results overwrite unit test results
reports.html.destination = file("${reporting.baseDir}/${name}")
}
integrationTest.mustRunAfter test
//check.dependsOn integrationTest // optional: make build require successful integration test
// source folders
sourceSets {
main {
java {
srcDirs = [sourceDir]
}
resources {
srcDirs = [resourceDir]
}
}
test {
java {
srcDirs = [testDir]
}
resources {
srcDirs = [testResources]
}
}
integrationTest {
java {
srcDirs = [integrationTestDir]
}
resources {
srcDirs = [integrationTestResources]
}
}
}
// In this section you declare where to find the dependencies of your project
repositories {
flatDir {
dirs importDir // add libraries folder to build path
}
jcenter() // repo for 'standard' java tools like JUnit
// mavenCentral() // repo with lots of goodies and LGPL/MIT/Apache open source libraries
}
dependencies {
testCompile "junit:junit:+" // latest JUnit verion (whatever that might be)
//compile name: 'xxx' // import xxx.jar from the libraries folder
//compile "zzz.xyz:libname:version" // import artifactId "libname" in groupId "zzz.xyz" from Maven repository
//integrationTestCompile "zzz.xyz:libname:version" // add specific dependency for integration tests
}
// version file stuff
def majorVersionKey = 'version.major'
def minorVersionKey = 'version.minor'
def buildVersionKey = 'version.build'
def namespaceDirPath = folderName(classPathToFilePath(mainClassName))
def namespace = namespaceDirPath.toString().replace('/','.').replace('\\','.')
def versionFilepath = "${resourceDir}/${namespaceDirPath}/${versionFileName}"
// customized behavior
compileJava.doFirst( {
incrementPropertyNumber("${projectDir}/${versionFilepath}", buildVersionKey)
version = readVersionNumber("${projectDir}/${versionFilepath}",majorVersionKey,minorVersionKey,buildVersionKey)
})
jfxNative.doFirst{
jfx.nativeReleaseVersion = readVersionNumber("${projectDir}/${versionFilepath}",majorVersionKey,minorVersionKey,buildVersionKey)
}
run.doFirst{
workingDir = new File("${projectDir}/${runDir}")
if( ! workingDir.exists()){
workingDir.mkdirs()
}
}
clean.doLast{
workingDir = new File("${projectDir}/${runDir}")
if( workingDir.exists() ) {
ant.delete(includeEmptyDirs: 'true') {
fileset(dir: file("${projectDir}/${runDir}"), includes: '*/**')
fileset(dir: file("${projectDir}/${runDir}"), includes: '*')
}
}
}
///// JavaFX native installer configuration /////
// See https://github.com/FibreFoX/javafx-gradle-plugin for JavaFX plugin configuration options
jfx {
// minimal requirements for jfxJar-task
mainClass = mainClassName
vendor = authorVendor
// Extended options
verbose = true
jfxAppOutputDir = "${buildDirName}/jfx/app".toString()
jfxMainAppJarName = "${archivesBaseName}.jar".toString()
deployDir = nativeInstallerResources // defaults to src/main/deploy
useEnvironmentRelativeExecutables = true
libFolderName = "lib"
// gradle jfxJar
css2bin = false
preLoader = null // String
updateExistingJar = false
allPermissions = false
manifestAttributes = null // Map<String, String>
addPackagerJar = true
copyAdditionalAppResourcesToJar = false
skipCopyingDependencies = false
useLibFolderContentForManifestClasspath = false
fixedManifestClasspath = null
// gradle jfxNative
identifier = null // String - setting this for windows-bundlers makes it possible to generate upgradeable installers (using same GUID)
nativeOutputDir = "${buildDirName}/jfx/native-deploy".toString()
bundler = "ALL" // set this to some specific, if your don't want all bundlers running, examples "windows.app", "jnlp", ...
jvmProperties = null // Map<String, String>
jvmArgs = null // List<String>
userJvmArgs = null // Map<String, String>
launcherArguments = null // List<String>
//nativeReleaseVersion = "$project.version".toString()
needShortcut = false
needMenu = false
//bundleArguments = [
// optional parameters to pass to the bundler (usually OS-specific)
// see https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/self-contained-packaging.html#A1308687
//]
appName = resourceName // this is used for files below "${nativeOutputDir}", e.g. "src/main/deploy/windows/project.ico"
additionalBundlerResources = null // path to some additional resources for the bundlers when creating application-bundle
additionalAppResources = null // path to some additional resources when creating application-bundle
//secondaryLaunchers = [ [appName:"run-${resourceName}".toString()] ] // alternative app names, there can be many (and you can give them different icons)
fileAssociations = null // List<Map<String, Object>>
noBlobSigning = false // when using bundler "jnlp", you can choose to NOT use blob signing
customBundlers = null // List<String>
failOnError = false
onlyCustomBundlers = false
skipJNLP = false
skipNativeVersionNumberSanitizing = false // anything than numbers or dots are removed
additionalJarsignerParameters = null // List<String>
skipMainClassScanning = false // set to true might increase build-speed
skipNativeLauncherWorkaround124 = false
skipNativeLauncherWorkaround167 = false
skipNativeLauncherWorkaround205 = false
skipJNLPRessourcePathWorkaround182 = false
skipSigningJarFilesJNLP185 = false
skipSizeRecalculationForJNLP185 = false
skipMacBundlerWorkaround = false
// gradle jfxRun
runJavaParameter = null // String
runAppParameter = null // String
// per default the outcome of the gradle "jarTask" will be used, set this to specify otherwise (like proguard-output)
alternativePathToJarFile = null // String
// to disable patching of ant-javafx.jar, set this to false
usePatchedJFXAntLib = true
// making it able to support absolute paths, defaults to "false" for maintaining old behaviour
checkForAbsolutePaths = false
// gradle jfxGenerateKeyStore
keyStore = "${nativeOutputDir}/keystore.jks".toString()
keyStoreAlias = "${resourceName}".toString()
keyStorePassword = "spirogyra".toString()
keyPassword = null // will default to keyStorePassword
keyStoreType = "jks"
overwriteKeyStore = false
certDomain = null // required
certOrgUnit = null // defaults to "none"
certOrg = null // required
certState = null // required
certCountry = null // required
}
///// utility functions and tasks /////
// Task to create project directory structure.
task createProject {
doLast {
def projectName = project.name
def mainClassFilePath = classPathToFilePath(mainClassName)
def mainClassFile = "${sourceDir}/${mainClassFilePath}.java"
def mainClassITFile = "${integrationTestDir}/${mainClassFilePath}IT.java"
println "Creating directory structure for ${projectName}"
makeFolder("${projectDir}/${sourceDir}")
makeFolder("${projectDir}/${resourceDir}")
makeFolder("${projectDir}/${testDir}")
makeFolder("${projectDir}/${testResources}")
makeFolder("${projectDir}/${integrationTestDir}")
makeFolder("${projectDir}/${integrationTestResources}")
makeFolder("${projectDir}/${importDir}")
makeFolder("${projectDir}/${nativeInstallerResources}/package")
makeFolder("${projectDir}/${nativeInstallerResources}/package/windows")
makeFolder("${projectDir}/${nativeInstallerResources}/package/macosx")
makeFolder("${projectDir}/${nativeInstallerResources}/package/linux")
makeFolder(buildDir) // is a file, not a string
makeFolder("${projectDir}/${runDir}")
makeFolder( folderName("${projectDir}/${mainClassFile}") )
makeFolder( folderName("${projectDir}/${mainClassITFile}") )
makeFolder( folderName("${projectDir}/${versionFilepath}") )
println "Version properties file for ${projectName}: ${versionFilepath}"
def copyrightYear = java.time.ZonedDateTime.now().getYear()
writeToFile(versionFilepath, ["${majorVersionKey}=0", "${minorVersionKey}=0", "${buildVersionKey}=0", "authors=No names given", "title=${archivesBaseName}", "copyright=Copyright ${copyrightYear}", "license.redistribution=All rights reserved. You may not redistribute this software or any of its components or derivatives without written permission from the authors of this software."])
if ( ! file("${projectDir}/${mainClassFile}").exists() ){
println "Creating basic java files"
def mainViewFile = "${resourceDir}/${namespaceDirPath}/MainView.fxml"
def mainViewController = "${sourceDir}/${namespaceDirPath}/MainViewController.java"
def className = fileName(mainClassFile).toString().replace('.java','')
def languageFileBaseName = 'Language'
def languageBaseFile = "${resourceDir}/${namespaceDirPath}/${languageFileBaseName}"
writeToFile("${projectDir}/${mainClassFile}", ["package ${namespace};", "import java.io.IOException;", "import java.nio.file.Paths;", "import java.util.Locale;", "import java.util.Properties;", "import java.util.ResourceBundle;", "import java.util.concurrent.atomic.AtomicReference;", "import java.util.logging.Level;", "import java.util.logging.Logger;", "import javafx.application.Application;", "import javafx.fxml.FXMLLoader;", "import javafx.scene.Parent;", "import javafx.scene.Scene;", "import javafx.stage.Stage;", "public class ${className} extends Application{", " private static ${className} instance = null;", " private Stage rootStage = null;", " private final Properties properties = new Properties();", " @Override", " public void start(Stage primaryStage) throws Exception {", " rootStage = primaryStage;", " ", " rootStage.setTitle(properties.getProperty(\"title\", \"<TITLE NOT FOUND>\"));", " //rootStage.getIcons().add( new javafx.scene.image.Image( ${className}.class.getResourceAsStream( \"icon.png\" )));", " AtomicReference<Object> rootController = new AtomicReference<>();", " AtomicReference<Parent> rootPane = new AtomicReference<>();", " loadFXML(\"MainView.fxml\",rootController, rootPane);", " switchToScreen(rootPane.get());", " }", " private void loadFXML(String fxmlPath, AtomicReference<Object> controller, AtomicReference<Parent> rootPane) throws IOException{", " Locale locality = Locale.getDefault();", " ResourceBundle localization = ResourceBundle.getBundle(\"${namespace}.Language\", locality);", " FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlPath),localization);", " rootPane.set( loader.load());", " controller.set(loader.getController());", " }", " private void switchToScreen(Parent rootFXMLElement) {", " rootStage.setScene(new Scene(rootFXMLElement));", " rootStage.sizeToScene();", " rootStage.show();", " }", " public static ${className} getInstance(){", " if(instance == null){", " throw new IllegalStateException(\"Cannot get instance: application not yet initialized\");", " }", " return instance;", " }", " public Stage getMainStage() {", " return rootStage;", " }", " @Override public void init() throws Exception {", " instance = this;", " properties.load(getClass().getResource(\"version.properties\").openStream());", " }", " @Override", " public void stop(){", " quit();", " }", " void quit(){", " javafx.application.Platform.exit();", " System.exit(0);", " }", " public static void main(String[] args) {", " Application.launch(args);", " }", "}"])
writeToFile("${projectDir}/${mainViewController}", ["package ${namespace};", "import java.net.URL;", "import java.util.ResourceBundle;", "import javafx.fxml.Initializable;", "public class MainViewController implements Initializable {", " private boolean rightToLeftLanguage = false;", " @Override", " public void initialize(URL url, ResourceBundle rb) {", " if(rb.containsKey(\"language.rtl\")) rightToLeftLanguage = Boolean.parseBoolean(rb.getString(\"language.rtl\"));", " }", "}"])
writeToFile("${projectDir}/${mainViewFile}", ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<?import javafx.geometry.*?>", "<?import java.lang.*?>", "<?import java.util.*?>", "<?import javafx.scene.*?>", "<?import javafx.scene.control.*?>", "<?import javafx.scene.layout.*?>", "<AnchorPane id=\"AnchorPane\" prefHeight=\"400.0\" prefWidth=\"600.0\" xmlns=\"http://javafx.com/javafx/8.0.40\" xmlns:fx=\"http://javafx.com/fxml/1\" fx:controller=\"${namespace}.MainViewController\">", " <children>", " <BorderPane prefHeight=\"200.0\" prefWidth=\"200.0\" AnchorPane.bottomAnchor=\"0.0\" AnchorPane.leftAnchor=\"0.0\" AnchorPane.rightAnchor=\"0.0\" AnchorPane.topAnchor=\"0.0\">", " <center>", " <SplitPane dividerPositions=\"0.667\" BorderPane.alignment=\"CENTER\">", " <items>", " <BorderPane />", " <VBox spacing=\"8.0\">", " <padding>", " <Insets bottom=\"8.0\" left=\"8.0\" right=\"8.0\" top=\"8.0\" />", " </padding>", " <children>", " <Label text=\"%gui.mainview.label2\" />", " <Separator prefWidth=\"200.0\" />", " <FlowPane hgap=\"8.0\" vgap=\"8.0\">", " <children>", " <Label text=\"%gui.mainview.label1\" />", " <Button mnemonicParsing=\"false\" text=\"%gui.mainview.button1\" />", " </children>", " </FlowPane>", " </children>", " </VBox>", " </items>", " </SplitPane>", " </center>", " </BorderPane>", " </children>", "</AnchorPane>"])
writeToFile("${projectDir}/${mainClassITFile}", ["package ${namespace};", 'import javafx.application.Application;', 'import org.junit.Test;', 'import static org.junit.Assert.*;', "public class ${className}IT {", " public ${className}IT() { }", ' @Test', " public void test${className}() {", ' System.out.println(String.format("Running integration test for %s",', " ${mainClassName}.class));", " Application.launch(${mainClassName}.class);", ' }', '}'])
println "Creating language localization files (English, Spanish, and Simplified Chinese)"
writeToFile("${projectDir}/${languageBaseFile}.properties", ["language.name=English", "language.rtl=false", "gui.mainview.label1=Label 1", "gui.mainview.label2=Label 2", "gui.mainview.button1=Button 1"])
writeToFile("${projectDir}/${languageBaseFile}_en_US.properties", ["language.name=English", "language.rtl=false", "gui.mainview.label1=Label 1", "gui.mainview.label2=Label 2", "gui.mainview.button1=Button 1"])
writeToFile("${projectDir}/${languageBaseFile}_es.properties", ["language.name=Espa\\u00f1ol", "language.rtl=false", "gui.mainview.label1=etiqueta 1", "gui.mainview.label2=etiqueta 2", "gui.mainview.button1=bot\\u00f3n 1"])
writeToFile("${projectDir}/${languageBaseFile}_zh_CN.properties", ["language.name=\\u7b80\\u4f53\\u4e2d\\u6587", "language.rtl=false", "gui.mainview.label1=\\u6807\\u7b7e1", "gui.mainview.label2=\\u6807\\u7b7e2", "gui.mainview.button1=\\u6309\\u94ae1"])
}
println "Creating DVCS ignore files"
if ( ! file('.hgignore').exists() ){
println "Creating .hgignore file"
writeToFile('.hgignore',['# mercurial ignore file', '# using glob syntax (use "syntax: regexp" for REGEX expressions)', 'syntax: glob', '# non-committed project folders and files', "${runDir}/**", "${buildDirName}/**", 'out/**', '.*gradle/**', '# ignore netbeans cache', 'private/**', '# ignore IntelliJ cache files', '.idea/**', '# ignore eclipse files', '.settings/**', '.classpath', '.project', '# ignore lock files', '# Open/Libre Office', '.~lock.*', '# MS Office', '~$*', '~*.tmp', '# gedit and notepad++', '*~'])
} else {
println ".hgignore file already exists"
}
if ( ! file('.gitignore').exists() ){
println "Creating .gitignore file"
writeToFile('.gitignore',['# git ignore file', '# using glob syntax', '# (Note: unlike hg, git does not support REGEX parsing in ignore files)', '# non-committed project folders and files', "${runDir}/*", "${buildDirName}/*", 'out/*', '.*gradle/*', '# ignore netbeans cache', 'private/*', '# ignore IntelliJ cache files', '.idea/*', '# ignore eclipse files', '.settings/*', '.classpath', '.project', '# ignore lock files', '# Open/Libre Office', '.~lock.*', '# MS Office', '~$*', '~*.tmp', '# gedit and notepad++', '*~'])
} else {
println ".gitignore file already exists"
}
println "\tFor Windows icons, add the following files:"
println "${projectDir}/${nativeInstallerResources}/package/windows/${resourceName}.ico"
println "${projectDir}/${nativeInstallerResources}/package/windows/${resourceName}-setup-icon.bmp"
println "\tFor Mac OS X icons, add the following files:"
println "${projectDir}/${nativeInstallerResources}/package/macosx/${resourceName}.icns"
println "\tFor Linux icons, add the following files:"
println "${projectDir}/${nativeInstallerResources}/package/linux/${resourceName}.png (image size must be 512x512 pixels)"
println "Project creation complete."
}
}
// functions
public String readVersionNumber( def filepath, def majorVersionKey, def minorVersionKey, def buildVersionKey ){
Properties props = new Properties()
File propsFile = new File(filepath)
props.load(propsFile.newDataInputStream())
def majorV = props.getProperty(majorVersionKey)
def minorV = props.getProperty(minorVersionKey)
def buildV = props.getProperty(buildVersionKey)
return "${majorV}.${minorV}.${buildV}".toString()
}
public void incrementPropertyNumber( def filepath, def buildVersionKey ){
Properties props = new Properties()
File propsFile = new File(filepath)
props.load(propsFile.newDataInputStream())
Integer nextbuildnum = ( ((props.getProperty(buildVersionKey)) as BigDecimal) + 1 )
props.setProperty(buildVersionKey, nextbuildnum.toString())
props.store(propsFile.newWriter(), null)
}
public void makeFolder( def folderpath) {
println "Creating directory: ${folderpath}"
java.nio.file.Files.createDirectories(java.nio.file.Paths.get(folderpath.toString()))
}
public void writeToFile( def filepath, def contentList) {
println "Writing to file: ${filepath}"
File f = new File(filepath)
contentList.each {
f << ("${it}\n")
}
}
public String fileName( def filepath) {
return java.nio.file.Paths.get(filepath.toString()).getFileName().toString()
}
public String folderName( def filepath) {
return java.nio.file.Paths.get(filepath.toString()).getParent().toString().replace('\\','/')
}
public String relativeTo( def filepath, def rootfilepath) {
return java.nio.file.Paths.get(rootfilepath.toString()).relativize(java.nio.file.Paths.get(filepath.toString())).toString().replace('\\','/')
}
public String classPathToFilePath( def classPathString ) {
return classPathString.toString().replace('.','/')
}
///// end of utility functions /////
@DrPlantabyte
Copy link
Author

Added JavaFX native deployment plugin and configuration

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment