Gradle Application Plugin: Difference between revisions
No edit summary |
|||
(40 intermediate revisions by the same user not shown) | |||
Line 5: | Line 5: | ||
=Internal= | =Internal= | ||
* [[ | * [[Gradle_Artifacts#The_Application_Plugin|Gradle Artifacts]] | ||
* [[Gradle Distribution Plugin#Overview|Gradle Distribution Plugin]] | * [[Gradle Distribution Plugin#Overview|Gradle Distribution Plugin]] | ||
=Overview= | =Overview= | ||
The application plugin facilitates creating an executable JVM application. It bundles all application JARS, transitive dependencies and operating system specific scripts | The application plugin facilitates creating an executable JVM application. It bundles all application JARS, [[Gradle_Dependencies_and_Dependency_Configurations#Transitive_Dependency|transitive dependencies]] and operating system specific scripts, custom files from project file structure, and it also generates the bash and Windows wrapper scripts to run the application with, then packages all these into a TAR or ZIP file. The plugin also allows [[#Executing_the_Application_from_Project_Work_Area|executing the application from the project's work area with the "run" task]]. Note the the bundling of the project artifacts is done by the [[Gradle Distribution Plugin#Overview|Distribution Plugin]]. | ||
=Concepts= | |||
==Distribution== | |||
{{Internal|Gradle Distribution Plugin#Distribution|Distribution Plugin - Distribution}} | |||
==Relationship between the Application and Distribution Plugin== | |||
<font color=darkgray>TODO</font> | |||
=Typical Setup= | =Typical Setup= | ||
Line 33: | Line 43: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Provided that the rest of the dependencies between sub-projects are correctly declared in the corresponding build.gradle files, the execution of | Provided that the rest of the dependencies between sub-projects are correctly declared in the corresponding build.gradle files, the execution of: | ||
gradle distZip | gradle distZip | ||
Line 39: | Line 49: | ||
will compile everything and will create a ./build/distributions/<''rootProject.name''>.zip file. The file includes the sub-project artifacts, their transitive dependencies, and wrapper scripts for bash and Windows. | will compile everything and will create a ./build/distributions/<''rootProject.name''>.zip file. The file includes the sub-project artifacts, their transitive dependencies, and wrapper scripts for bash and Windows. | ||
For more details about rootProject.name, see: {{Internal| | Note that the "build" task depends on "distZip", so: | ||
gradle build | |||
implies "gradle distZip" | |||
For more details about rootProject.name, see: {{Internal|Gradle_Project_Coordinates,_State_and_Configured_Properties#rootProject|rootProject}} | |||
=Properties= | =Properties= | ||
Line 48: | Line 64: | ||
mainClassName = 'greeter.Greeter' | mainClassName = 'greeter.Greeter' | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=Building Custom Files with the Distribution= | |||
{{Internal|Gradle Distribution Plugin#Building Custom Files with the Distribution|Building Custom Files with the Gradle Distribution Plugin}} | |||
=Executing the Application from Project Work Area= | =Executing the Application from Project Work Area= | ||
Line 53: | Line 73: | ||
gradle run | gradle run | ||
However, if the application requires command line parameters, they are not propagated. | The tasks executes the class specified with [[#mainClassName|mainClassName]] property. | ||
However, if the application requires command line parameters, they are not propagated. An alternative solution is to deploy the application ZIP file in a temporary area and execute the Application plugin-generates scripts. This is an example of a ./bin/build-and-deploy script: | |||
{{Internal|build-and-deploy|build-and-deploy Example}} | |||
=<span id='Wrapper_Script_Customization'></span>Start Script Customization= | =<span id='Wrapper_Script_Customization'></span>Start Script Customization= | ||
Line 61: | Line 85: | ||
==Start Script Mechanics== | ==Start Script Mechanics== | ||
The Application plugin generates by default Unix and Windows start scripts that are capable launching a configured "main" class in an environment where the classpath is correctly set. The default script templates are based on the same scripts used to launch Gradle, which ship as part of the Gradle distribution. The start scripts are completely customizable though. The Application plugin adds a task named "startScripts" to the project's task list. The task can be view with: | The Application plugin generates by default Unix and Windows start scripts that are capable launching a configured "main" class in an environment where the classpath is correctly set. The default script templates are based on the same scripts used to launch Gradle, which ship as part of the Gradle distribution. The start scripts are completely customizable though, as described in the [[#Configure_the_Start_Script_Template|Configure the Start Script Template]] section. The Application plugin adds a task named "startScripts" to the project's task list. The task can be view with: | ||
gradle tasks --all | gradle tasks --all | ||
The " | The "startScripts" task is a [https://docs.gradle.org/4.7/javadoc/org/gradle/api/tasks/application/CreateStartScripts.html CreateStartScripts] type task. The actual script generation is implemented in [https://docs.gradle.org/4.7/javadoc/org/gradle/jvm/application/scripts/ScriptGenerator.html ScriptGenerator] instances, returned by <tt>CreateStartScripts.getUnixStartScriptGenerator()</tt> and <tt>CreateStartScripts.getWindowsStartScriptGenerator()</tt>. The default generators are of the type [https://docs.gradle.org/4.7/javadoc/org/gradle/jvm/application/scripts/TemplateBasedScriptGenerator.html TemplateBasedScriptGenerator] with default templates. These templates can be changed via the <tt>TemplateBasedScriptGenerator.setTemplate(org.gradle.api.resources.TextResource)</tt> method. | ||
==Configure the Default 'startScripts' Task== | ==Configure the Default 'startScripts' Task== | ||
Line 72: | Line 96: | ||
<syntaxhighlight lang='groovy'> | <syntaxhighlight lang='groovy'> | ||
def | def t = rootProject.tasks.getByPath('startScripts') | ||
t.applicationName = 'some-app-name' | |||
t.mainClassName = 'com.example.Main' | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 91: | Line 115: | ||
==Generate Multiple Start Scripts== | ==Generate Multiple Start Scripts== | ||
We generate multiple start script by using the default "startScript" to create the main start script (or if there's no main, one of the start scripts), and we declare a new CreateStartScripts task instance for each additional script. We use the default "startScript" to read common configuration from, and we make it to depend on all newly added tasks, so they are executed automatically: | |||
<syntaxhighlight lang='groovy'> | |||
... | |||
apply plugin: 'application' | |||
// | |||
// this generates an additional (not main) wrapper script | |||
// | |||
task secondaryStartScript(type: CreateStartScripts) { | |||
applicationName = 'secondary' | |||
mainClassName = 'com.example.secondary.Main' | |||
// | |||
// get the rest of the configuration from the already configured 'startScripts' task; | |||
// except 'applicationName' and 'mainClassName', the rest of the configuration should | |||
// be similar | |||
// | |||
def t = rootProject.tasks.getByPath('startScripts'); | |||
classpath = t.classpath; | |||
outputDir = t.outputDir; | |||
} | |||
// | |||
// this configures the main 'startScript' task to generate the "main" script | |||
// | |||
def t = rootProject.tasks.getByPath('startScripts') | |||
t.applicationName = 'primary' | |||
t.mainClassName = 'com.example.primary.Main' | |||
t.dependsOn(secondaryStartScript) // trigger 'secondaryStartScript' execution automatically | |||
</syntaxhighlight> | |||
==Configure the Start Script Template== | ==Configure the Start Script Template== | ||
The Gradle Application Plugin builds the wrapper scripts from built-in templates. That can be changed as described in [[#Start_Script_Template_Configuration_Process|Start Script Template Configuration Process]] section below. | |||
===Template Specification=== | |||
The default implementations used by Gradle Application [https://docs.gradle.org/4.7/javadoc/org/gradle/api/tasks/application/CreateStartScripts.html CreateStartScripts] use [[Groovy_Template_Engines#Simple_Template_Engine|Groovy's SimpleTemplateEngine]] to parse the template. The following variables can be used: | |||
* 'applicationName' | |||
* 'optsEnvironmentVar' | |||
* 'exitEnvironmentVar' | |||
* 'mainClassName' | |||
* 'executableDir' | |||
* 'defaultJvmOpts' | |||
* 'appNameSystemProperty' | |||
* 'appHomeRelativePath' | |||
* 'classpath'. Note that 'classpath' is replaced with a path that relies on "$APP_HOME": | |||
<syntaxhighlight lang='bash'> | |||
$APP_HOME/lib/example.jar:$APP_HOME/lib/log4j.jar | |||
</syntaxhighlight> | |||
===Start Script Template Configuration Process=== | |||
'''1'''. Get the Template. Copy the wrapper script template to be used in ./src/main/templates and name it "gradle-application-plugin-script-template". | |||
An example of such template, that offloads functionality to an external library, so that library must be included as well, is https://github.com/ovidiuf/templates/blob/master/gradle-application-plugin-script-template | |||
'''2'''. Get the Supporting Libraries. Copy the supporting libraries in ./src/main/dist/bin. If [https://github.com/ovidiuf/templates/blob/master/gradle-application-plugin-script-template gradle-application-plugin-script-template] is used, we need https://github.com/ovidiuf/templates/blob/master/bash.shlib, so copy that file in ./src/main/dist/bin. | |||
'''3'''. Update the Default startScript Task. | |||
Example: | |||
<syntaxhighlight lang='groovy'> | |||
def startScriptTemplate = './src/main/templates/gradle-application-plugin-script-template' | |||
def t = rootProject.tasks.getByPath('startScripts') | |||
t.applicationName = 'server' | |||
t.mainClassName = 'io.example.server.Main' | |||
t.unixStartScriptGenerator.template = resources.text.fromFile(startScriptTemplate) | |||
//t.windowsStartScriptGenerator.template = resources.text.fromFile('customWindowsStartScript.txt'); | |||
</syntaxhighlight> | |||
'''4'''. Update any Additional startScript Tasks. | |||
<syntaxhighlight lang='groovy'> | |||
task secondaryStartScript(type: CreateStartScripts) { | |||
applicationName = 'client' | |||
mainClassName = 'io.example.client.Main' | |||
// | |||
// get the rest of the configuration from the already configured 'startScripts' task; | |||
// except 'applicationName' and 'mainClassName', the rest of the configuration should | |||
// be similar | |||
// | |||
def t = rootProject.tasks.getByPath('startScripts') | |||
classpath = t.classpath | |||
outputDir = t.outputDir | |||
unixStartScriptGenerator.template = resources.text.fromFile(startScriptTemplate) | |||
//t.windowsStartScriptGenerator.template = resources.text.fromFile('customWindowsStartScript.txt'); | |||
} | |||
</syntaxhighlight> |
Latest revision as of 05:45, 5 October 2020
External
Internal
Overview
The application plugin facilitates creating an executable JVM application. It bundles all application JARS, transitive dependencies and operating system specific scripts, custom files from project file structure, and it also generates the bash and Windows wrapper scripts to run the application with, then packages all these into a TAR or ZIP file. The plugin also allows executing the application from the project's work area with the "run" task. Note the the bundling of the project artifacts is done by the Distribution Plugin.
Concepts
Distribution
Relationship between the Application and Distribution Plugin
TODO
Typical Setup
This setup describes configuration of a multi-project build, whose sub-project create various components of the application and the top level project bundles all those in a distributable artifact. The dependencies between the sub-projects must be configured in the corresponding build.gradle files. To build the application artifact, we assume that there's a sub-project that contains the "main()" static method that triggers the execution, that method exists in "io.example.Main" class, and the sub-project in question is named "main".
As such, the top-level project build.gradle should contain the following relevant configuration:
...
apply plugin: 'application'
mainClassName = "io.example.Main"
dependencies {
...
implementation project(':main')
}
Provided that the rest of the dependencies between sub-projects are correctly declared in the corresponding build.gradle files, the execution of:
gradle distZip
will compile everything and will create a ./build/distributions/<rootProject.name>.zip file. The file includes the sub-project artifacts, their transitive dependencies, and wrapper scripts for bash and Windows.
Note that the "build" task depends on "distZip", so:
gradle build
implies "gradle distZip"
For more details about rootProject.name, see:
Properties
mainClassName
mainClassName = 'greeter.Greeter'
Building Custom Files with the Distribution
Executing the Application from Project Work Area
gradle run
The tasks executes the class specified with mainClassName property.
However, if the application requires command line parameters, they are not propagated. An alternative solution is to deploy the application ZIP file in a temporary area and execute the Application plugin-generates scripts. This is an example of a ./bin/build-and-deploy script:
Start Script Customization
This section explains the mechanics behind creating the application start (wrapper) script and shows how the process can be customized: use a different template, create multiple scripts, etc.
Start Script Mechanics
The Application plugin generates by default Unix and Windows start scripts that are capable launching a configured "main" class in an environment where the classpath is correctly set. The default script templates are based on the same scripts used to launch Gradle, which ship as part of the Gradle distribution. The start scripts are completely customizable though, as described in the Configure the Start Script Template section. The Application plugin adds a task named "startScripts" to the project's task list. The task can be view with:
gradle tasks --all
The "startScripts" task is a CreateStartScripts type task. The actual script generation is implemented in ScriptGenerator instances, returned by CreateStartScripts.getUnixStartScriptGenerator() and CreateStartScripts.getWindowsStartScriptGenerator(). The default generators are of the type TemplateBasedScriptGenerator with default templates. These templates can be changed via the TemplateBasedScriptGenerator.setTemplate(org.gradle.api.resources.TextResource) method.
Configure the Default 'startScripts' Task
Get a reference to the "startScript" task as described in Get the Reference of a Task by Name and then reconfigure state as needed:
def t = rootProject.tasks.getByPath('startScripts')
t.applicationName = 'some-app-name'
t.mainClassName = 'com.example.Main'
The following variables can be configured:
- applicationName - this will be the name of the script, without (for Linux) and with .bat extension (for Windows).
- mainClassName
- outputDir file('build/sample')
- classpath files('path/to/some.jar')
- optsEnvironmentVar
- exitEnvironmentVar
- defaultJvmOpts
- appNameSystemProperty
- appHomeRelativePath
Generate Multiple Start Scripts
We generate multiple start script by using the default "startScript" to create the main start script (or if there's no main, one of the start scripts), and we declare a new CreateStartScripts task instance for each additional script. We use the default "startScript" to read common configuration from, and we make it to depend on all newly added tasks, so they are executed automatically:
...
apply plugin: 'application'
//
// this generates an additional (not main) wrapper script
//
task secondaryStartScript(type: CreateStartScripts) {
applicationName = 'secondary'
mainClassName = 'com.example.secondary.Main'
//
// get the rest of the configuration from the already configured 'startScripts' task;
// except 'applicationName' and 'mainClassName', the rest of the configuration should
// be similar
//
def t = rootProject.tasks.getByPath('startScripts');
classpath = t.classpath;
outputDir = t.outputDir;
}
//
// this configures the main 'startScript' task to generate the "main" script
//
def t = rootProject.tasks.getByPath('startScripts')
t.applicationName = 'primary'
t.mainClassName = 'com.example.primary.Main'
t.dependsOn(secondaryStartScript) // trigger 'secondaryStartScript' execution automatically
Configure the Start Script Template
The Gradle Application Plugin builds the wrapper scripts from built-in templates. That can be changed as described in Start Script Template Configuration Process section below.
Template Specification
The default implementations used by Gradle Application CreateStartScripts use Groovy's SimpleTemplateEngine to parse the template. The following variables can be used:
- 'applicationName'
- 'optsEnvironmentVar'
- 'exitEnvironmentVar'
- 'mainClassName'
- 'executableDir'
- 'defaultJvmOpts'
- 'appNameSystemProperty'
- 'appHomeRelativePath'
- 'classpath'. Note that 'classpath' is replaced with a path that relies on "$APP_HOME":
$APP_HOME/lib/example.jar:$APP_HOME/lib/log4j.jar
Start Script Template Configuration Process
1. Get the Template. Copy the wrapper script template to be used in ./src/main/templates and name it "gradle-application-plugin-script-template".
An example of such template, that offloads functionality to an external library, so that library must be included as well, is https://github.com/ovidiuf/templates/blob/master/gradle-application-plugin-script-template
2. Get the Supporting Libraries. Copy the supporting libraries in ./src/main/dist/bin. If gradle-application-plugin-script-template is used, we need https://github.com/ovidiuf/templates/blob/master/bash.shlib, so copy that file in ./src/main/dist/bin.
3. Update the Default startScript Task.
Example:
def startScriptTemplate = './src/main/templates/gradle-application-plugin-script-template'
def t = rootProject.tasks.getByPath('startScripts')
t.applicationName = 'server'
t.mainClassName = 'io.example.server.Main'
t.unixStartScriptGenerator.template = resources.text.fromFile(startScriptTemplate)
//t.windowsStartScriptGenerator.template = resources.text.fromFile('customWindowsStartScript.txt');
4. Update any Additional startScript Tasks.
task secondaryStartScript(type: CreateStartScripts) {
applicationName = 'client'
mainClassName = 'io.example.client.Main'
//
// get the rest of the configuration from the already configured 'startScripts' task;
// except 'applicationName' and 'mainClassName', the rest of the configuration should
// be similar
//
def t = rootProject.tasks.getByPath('startScripts')
classpath = t.classpath
outputDir = t.outputDir
unixStartScriptGenerator.template = resources.text.fromFile(startScriptTemplate)
//t.windowsStartScriptGenerator.template = resources.text.fromFile('customWindowsStartScript.txt');
}