Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make environment variables accessible to Gradle task

I need to run a Gradle task inside a shell environment, which must be created before the task is launched. Using commandLine or executable is not appropriate, as I need to run the task in the same process as the shell script. Originally, I called the script directly inside gradlew, but later I decided to source it from build.gradle.kts and call subsequent tasks through gradlew:

val setupRosEnv by tasks.creating(Exec::class) {
    executable = "bash"

    args("-c", "source $rosPath/setup.sh && source gradlew myTask")
}

I can build everything by running ./gradlew setupRosEnv from the CLI. Besides sourcing the script then running gradlew, is there a way to achieve this using the Gradle API? The current solution seems a bit hacky, and is clunky for other tasks to depend on setupRosEnv, as this will lead to an infinite loop or must be explicitly handled to prevent tasks being run more than once.

As the shell script itself is generated by ROS, it cannot be translated to Gradle or easily parsed.

like image 566
breandan Avatar asked Oct 23 '18 01:10

breandan


People also ask

How do I pass environment variables in Gradle task?

If you are using an IDE, go to run, edit configurations, gradle, select gradle task and update the environment variables. See the picture below. Alternatively, if you are executing gradle commands using terminal, just type 'export KEY=VALUE', and your job is done. Save this answer.

How do I set environment variables in Gradle Windows 10?

In File Explorer right-click on the This PC (or Computer ) icon, then click Properties -> Advanced System Settings -> Environmental Variables . Under System Variables select Path , then click Edit . Add an entry for C:\Gradle\gradle-7.5.1\bin . Click OK to save.

Does Gradle use JAVA_HOME?

Gradle uses whatever JDK it finds in your path. Alternatively, you can set the JAVA_HOME environment variable to point to the installation directory of the desired JDK. See the full compatibility notes for Java, Groovy, Kotlin and Android.


1 Answers

It depends how your gradle task myTask use the environment. If it use the environment by System.getenv, you can do use the following step.

  1. parse the bash environment file env.sh and load all variables into Properties
  2. append the environment variables to current process in java.lang.ProcessEnvironment by reflection
  3. use the injected environment variables in your build task

Below is just roughly an example code copied from Java with minor modification, but it works fine in gradle build task.

task myEnvironInjected << {
    println("task with injected enviroment")
}

task myBuildTask(dependsOn: myEnvironInjected) << {
    def v1 = System.getenv("V1")
    println("my build task running V1=${v1}")
}

myEnvironInjected.doFirst {
    final Map<String, String> bashEnvMap = new HashMap<>();

    try {
        // a simple simulation of bash command source env.sh
        // use it carefully
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream
                ("/path/to/env.sh")));

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));

        String line;
        while ((line = reader.readLine()) != null) {
            if (line.length() > 0 && line.startsWith("export ")) {
                String newLine = line.trim().replaceFirst("^export ", "");
                // remove single or double quote from the value if it has
                int quoteIndex = newLine.indexOf('=')+1;
                if (quoteIndex < newLine.length() && (newLine.charAt(quoteIndex) == ('"' as char) ||
                        newLine.charAt(quoteIndex) == ('\'' as char))) {
                    newLine = newLine.substring(0, quoteIndex) + newLine.substring(quoteIndex+1, newLine.length()-1);
                }
                writer.write(newLine);
                writer.newLine();
            }
        }
        writer.flush();
        writer.close();

        InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        Properties properties = new Properties();
        properties.load(inputStream);

        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            bashEnvMap.put(((String) entry.getKey()), ((String) entry.getValue()));
        }

        outputStream.close();
        inputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

    for (Class<?> aClass : Collections.class.getDeclaredClasses()) {
        if ("java.util.Collections\$UnmodifiableMap".equals(aClass.getName())) {
            try {
                Field mapField = aClass.getDeclaredField("m");
                mapField.setAccessible(true);
                Object mapObject = mapField.get(System.getenv());
                Map<String, String> environMap = ((Map<String, String>) mapObject);
                environMap.putAll(bashEnvMap);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    for (Map.Entry<String, String> envEntry : System.getenv().entrySet()) {
        System.out.println(envEntry.getKey() + "=" + envEntry.getValue());
    }
}

My test file env.sh looks like this.

export V1="v1 vvv"
export V2='v 2222'
export V3=v33333

If your build task use the environment variables not by System.getenv, your hacking method might be the best solution.

task myBuildTaskWithSourceEnv(type: Exec) {
    commandLine '/bin/bash'
    setArgs(['-c', 'source ../env.sh;set;../gradlew :app:assembleDebug'])
}
like image 139
alijandro Avatar answered Oct 20 '22 06:10

alijandro