2

I have been trying to set a simple environment variable (or system property, whatever works really) through build.gradle file to access it from a java method (which is not part of integration tests) but after exhausting almost every answer on this forum and elsewhere that I could find, I have still not been able to find a working solution. For reference I have to access it from a file in src/main folder.

The problem I'm facing is that if I specify it like this

test {
    environment 'PROJECT_DIR', "${projectDir}"
}

and access it like this from my java file

String projectDir = System.getenv("PROJECT_DIR");

projectDir is null because I assume it's not part of test files.

If I specify it like this

task MyTask(type: Exec) {
    environment 'PROJECT_DIR', "${projectDir}"
}

tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn MyTask
}

and accessing the same way as before, it gives an error saying

Execution failed for task '<project-name>:MyTask'.
> execCommand == null!

If I try to do something similar for system property like

task setProp(type: Exec) {
    systemProperty 'PROJECT_DIR', "${projectDir}"
}
tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn MyTask
}

and accessing it like

String projectDir = System.getProperty("PROJECT_DIR");

it ends up with this error

Could not find method systemProperty() for arguments [PROJECT_DIR, <path>]

At the end of the day, I could find nothing that works out. However, both environment 'PROJECT_DIR', "${projectDir}" and systemProperty 'PROJECT_DIR', "${projectDir}" work if I include them in an existing JavaExec task. The problem is that my file cannot be triggered by any kind of JavaExec tasks as it is part of a bigger deployed service, so setting it through build.gradle seems like my only resort.

I wanted to check if there is something I've missed or some other way I could just make the project directory accessible to my java class.

scottstots
  • 155
  • 2
  • 17
  • Assume this is to have the variable available during build/deploy time, but not during runtime? – djmonki Apr 03 '23 at 12:01
  • It looks like you referenced this question, https://stackoverflow.com/questions/36322536/how-to-set-an-environment-variable-from-a-gradle-build . The issue is scope for most of these... You set an env var for a task, but then it disappears when the task completes. For the `Exec` task, environment lasts for the task, same with most others. In the same question, it suggested `run.doFirst { environment 'A', 'B' }`. I assume you tried that as well? The `run` would apply the env value for a `run`, but it depends what your target task and type are. – User51 Apr 04 '23 at 13:54
  • 3
    Stepping back a moment, what are you trying to achieve by setting this environment variable? The notions of scope are already addressed: your variable only lives as long as the thing that defined it (unix shells have `.bashrc`, programs on both OSes inherit env variables from their parent process). Ask yourself this as well: do you have any other storage medium that _would_ be persistent (e.g. a file in a common config dir)? – Rogue Apr 04 '23 at 16:41

2 Answers2

0

The problem I'm facing is that if I specify it like this

test {
    environment 'PROJECT_DIR', "${projectDir}"
}

This is exactly the right way to set an environment variable that is accessible while you are executing the test task.

and access it like this from my java file

String projectDir = System.getenv("PROJECT_DIR");

projectDir is null because I assume it's not part of test files.

It does not matter whether it is a "test file" or a "production file". As you set the environment variable on the test task, it will be available and accessible during execution of the test task. So it can hardly be that it is null during the exeuction of the test task, except if you have a typo somewhere or missed to provide complete information.

If I specify it like this

task MyTask(type: Exec) {
    environment 'PROJECT_DIR', "${projectDir}"
}

tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn MyTask
}

This does not make any sense, really. You define an Exec task, which is a task that executes some executable. During execution of that executable, the environment variable would be set. But you do not configure which executable to execute, so what should happen?

Then you make your Java compilation task depend on that Exec task which makes even less sense. Besides that any dependsOn is a code-smell (except if a lifecycle task is on the left-hand side) and should be avoided.

and accessing the same way as before, it gives an error saying

Execution failed for task '<project-name>:MyTask'.
> execCommand == null!

Yep, that is because you did not tell the Exec task which executable to execute as I just explained above.

If I try to do something similar for system property like

task setProp(type: Exec) {
    systemProperty 'PROJECT_DIR', "${projectDir}"
}
tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn MyTask
}

and accessing it like

String projectDir = System.getProperty("PROJECT_DIR");

it ends up with this error

Could not find method systemProperty() for arguments [PROJECT_DIR, <path>]

Same comments as with the last example above. But additionally, what should a system property be in the context of an Exec task that executes an arbitrary executable. System properties are a Java specific thing, so you cannot set a system property on a generic Exec task.

At the end of the day, I could find nothing that works out. However, both environment 'PROJECT_DIR', "${projectDir}" and systemProperty 'PROJECT_DIR', "${projectDir}" work if I include them in an existing JavaExec task. The problem is that my file cannot be triggered by any kind of JavaExec tasks as it is part of a bigger deployed service, so setting it through build.gradle seems like my only resort.

If I got you right here, then this paragraph is the most important in your question. Do I get you right, that you want to set this property at build time using your build script, but then expect it to be available to your production code when running outside of Gradle in the "bigger deployed service"?

This cannot work, what you set in Gradle is only available during the execution of the task you are setting them on. They cannot be persisted automagically or something like that. That would also be completely against what they are for. They are there so that you can configure things at runtime, not at build time, so that for example a user of a program can configure certain things.

If you want to set the property at build time and then access it at runtime, you need to generate this value into a file. For this you can for example use a task of type WriteProperties, but be aware that you should properly configure it as task generating resources, by giving it to sourceSets.main.resources.srcDir(...) as argument, or maybe the easier way is to have a template file in your resources and then configure the processResources task to fill in the placeholders in the template for example using expand. Then at runtime you can read that properties file to get the value you need.

Vampire
  • 35,631
  • 4
  • 76
  • 102
0

If your goal is to ensure that a particular environment variable (or system property) is set when your program (that one you build with gradle …) starts, then you may want to have a look to this answer.

Gradle generates a start script for each application, and that – as anything else, too – can be configured. The linked answer gives a starting point on how this can be achieved.

tquadrat
  • 3,033
  • 1
  • 16
  • 29