Reputation: 2322
In my project, I tried to configure my application with system environments like this in my build.gradle:
apply plugin: 'application'
applicationDefaultJvmArgs = ["-Ddw.server.applicationConnectors[0].port=${System.env.PORT}"]
This works if PORT is set to a constant, lets say 9001. But if I change PORT to another variable, the executable script does not change, it is already compiled to the old value of PORT in the build/install/bin/{executable-script}, like this:
DEFAULT_JVM_OPTS='"-Ddw.server.applicationConnectors[0].port=9001"'
instead of what I want it to be,
DEFAULT_JVM_OPTS='"-Ddw.server.applicationConnectors[0].port=$PORT"'
Is there some way to tell the application plugin to use a system environment variable instead of evaluating the system.env variable?
Btw, I also tried without single-quotes to avoid evaluation of the $PORT expression
applicationDefaultJvmArgs = ['-Ddw.server.applicationConnectors[0].port=$PORT']
and
applicationDefaultJvmArgs = ['-Ddw.server.applicationConnectors[0].port=\$PORT']
But both of these are compiled to this which will not work.
DEFAULT_JVM_OPTS='"-Ddw.server.applicationConnectors[0].port=\$PORT"'
Upvotes: 6
Views: 3648
Reputation: 777
(I'm the Gradle contributor who submitted the applicationDefaultJvmArgs feature)
If you write "${System.env.PORT}" into the applicationDefaultJvmArgs element, the PORT environment variable is substituted at buildtime, i.e. on the developer machine, before Gradle even sees it, and that value is then used in the starter scripts, which is what you saw.
The fact that a literal $ in applicationDefaultJvmArgs gets quoted in the Unix starter script isn't a bug, it's intentional. The applicationDefaultJvmArgs array is meant to be portable across all target platforms, i.e. Unix and Windows. Which is to say, the Unix/Windows start scripts are generated such that the launched JVMs on both platforms see exactly the string arguments that were passed into applicationDefaultJvmArgs at buildtime. This means that any shell metacharacters like $ in those strings must be quoted in the Unix starter script, so that if you have the literal string '$PORT' in one of the arguments, it gets reproduced exactly like that, i.e. no shell variable expansion takes place. If "$PORT" were written into the unix starter script without the \ before the $, then the starter script would do what you want -- on Unix. On Windows though, where the starter script is a cmd batch file, $ has no special meaning (instead, %% has to be used for variable expansion), so the string "$PORT" would be passed literally to the JVM, and your program wouldn't work as expected. You may not care about Windows (actually I don't either, not much at least), but Gradle, and specifically the application plugin with all its features, are meant to be fully portable across all supported platforms, so this is how things are implemented right now.
If you really want to have your $PORT variable expanded at runtime, i.e. when the starter script is run on the user's machine, you have to modify the generated starter script accordingly in a post-processing step. For example:
startScripts {
doLast {
unixScript.text = unixScript.text.replace('\\$PORT', '$PORT')
windowsScript.text = windowsScript.text.replace('\\$PORT', '%PORT%') //untested
}
}
For the future, one might think about having some kind of platform-independent notation for runtime environment variables in the applicationDefaultJvmArgs, and the application plugin would then expand that in each starter script to the correct platform-specific variable notation. But this isn't planned currently, AFAIK.
Upvotes: 12