6

Executing the gradle application plugin's installDist task creates a directory build/install/my-application-name/bin that contains wrapper scripts, my-application-name and my-application-name.bat. Running either of these scripts runs the application, and arguments passed to these scripts are passed to the underlying application.

In UNIX shell scripts you can access the name that was used to execute the program as $0. In fact, the UNIX version of the gradle-generated startup script uses $0 several times.

How can I configure the gradle application plugin such that these scripts will pass the value of $0 (and whatever the Windows equivalent is on Windows) into the underlying application, perhaps as a Java system property?

Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299

3 Answers3

7

Since parameter for obtaining the name of the script being run is referenced differently in Linux($0) and in Windows(%0), the most straightforward way to generate custom scripts would be to use separate custom templates for the respective start script generators:

startScripts {
  unixStartScriptGenerator.template = resources.text.fromFile('unixStartScript.txt')
  windowsStartScriptGenerator.template = resources.text.fromFile('windowsStartScript.txt')
}

The default templates are easy to obtain invoking e.g. unixStartScriptGenerator.template.asString()

Documentation on customizing the start scripts can be found here.

jihor
  • 2,478
  • 14
  • 28
  • As someone who knows very little Groovy, how do I insert `-Dprogname="$0"` into the unix script at the right place (as one of the arguments to the JVM)? I don't want to change the entire template, just add this one argument to the JVM (and I want `$0` to be evaluated by the shell -- I don't want a literal dollar-sign and zero to be passed to the JVM). – Laurence Gonsalves Jan 31 '18 at 23:57
  • @LaurenceGonsalves you can change the template in-place: `def gen = unixStartScriptGenerator; gen.template = resources.text.fromString(gen.template.asString().replace('DEFAULT_JVM_OPTS=${defaultJvmOpts}', 'DEFAULT_JVM_OPTS=${defaultJvmOpts}; JAVA_OPTS="\\$JAVA_OPTS -Dprogname=\\$0"'))` – jihor Feb 01 '18 at 09:38
  • @LaurenceGonsalves Without modifying the script, you can try passing the values using `defaultJvmOpts` parameter of the `startScripts` task, but values will be escaped, so inserting the `$` symbol will be a problem. – jihor Feb 01 '18 at 09:57
  • Thanks. I made some tweaks to this, but transforming the template like this was the key ingredient. – Laurence Gonsalves Feb 06 '18 at 07:15
  • How do I prevent the .bat file to be generated? I tried to assign an anon class with instance of `ScriptGenerator` with empty `generateScript()` but that still creates the `.bat` file there. The only different is that it is empty. – L. Holanda Oct 03 '18 at 01:05
2

This is what I ended up doing, based on jihor's answer. I'm posting it here just so that there's a working answer for anyone else interested:

startScripts {
    def gen = unixStartScriptGenerator
    gen.template = resources.text.fromString(
        gen.template.asString().replaceFirst('(?=\nDEFAULT_JVM_OPTS=.*?\n)') {
            '\nJAVA_OPTS="\\$JAVA_OPTS "\'"-Dprogname=\\$0"\''
        })

    // TODO: do something similar for windowsStartScriptGenerator
}

This uses replaceFirst is instead of replace so we can match a pattern. This is a little less brittle, and also lets us use lookahead so we don't have to actually replace what we're looking for. (This is groovy's variant of replaceFirst that takes a closure, by the way. This requires far less escaping than the version that takes a replacement string in this case.)

Also, instead of:

JAVA_OPTS="$JAVA_OPTS -Dprogname=$0"

we actually need something like:

JAVA_OPTS="$JAVA_OPTS "'"-Dprogname=$0"'

This is because $0 may contains special character (like spaces), and the startup script removes one level of quoting in the value of $JAVA_OPTS using eval set --.

(If anyone knows how to make this work on Windows, pleas feel free to update this answer.)

Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
1

I took an alternative approach. According to the documentation, as far back as Gradle 2.4 and all the way through Gradle 4.8, we should be able to set the following properties within the startScripts task:

  • applicationName
  • optsEnvironmentVar
  • exitEnvironmentVar
  • mainClassName
  • executableDir
  • defaultJvmOpts
  • appNameSystemProperty
  • appHomeRelativePath
  • classpath

Unfortunately, this is not true for the following properties, which seem to have never been exposed:

  • appNameSystemProperty
  • appHomeRelativePath

If appNameSystemProperty were exposed as the documentation describes, then we should be able to simply do the following:

startScripts {
    applicationName = 'foo'
    appNameSystemProperty = 'appName'
}

This would then result in the addition of -DappName=foo to the Java command constructed within both of the start scripts.

Since this is not the case, I took the following approach, which is a bit more verbose than the earlier solution to this question, but is perhaps less brittle because it does not rely on tweaking the out-of-box templates. Instead, it results in the documented behavior.

startScripts {
    mainClassName = '...'
    applicationName = 'foo'
    unixStartScriptGenerator =
        new CustomStartScriptGenerator(generator: unixStartScriptGenerator)
    windowsStartScriptGenerator =
        new CustomStartScriptGenerator(generator: windowsStartScriptGenerator)
}

class CustomStartScriptGenerator implements ScriptGenerator {
    @Delegate
    ScriptGenerator generator

    void generateScript(JavaAppStartScriptGenerationDetails details,
                        Writer destination) {
        details = new CustomDetails(details: details)
        this.generator.generateScript(details, destination)
    }

    static class CustomDetails implements JavaAppStartScriptGenerationDetails {
        @Delegate
        JavaAppStartScriptGenerationDetails details

        @Override
        String getAppNameSystemProperty() { 'appName' }
    }
}
Chuck Daniels
  • 312
  • 3
  • 6