Skip to content

Commit 664d788

Browse files
authored
[ExecMojo]Add getShebang method to correctly set the command line executable name (#487)
* [ExecMojo]Add `getShebang` method to correctly set the command line executable name - Update description of environmentScript - Make getShebang package private - Add shebang line into env.sh
1 parent 09628c4 commit 664d788

File tree

3 files changed

+116
-7
lines changed

3 files changed

+116
-7
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
MY_ENV_SCRIPT_SET_A_VAR_TO=something
2-
export MY_ENV_SCRIPT_SET_A_VAR_TO
1+
#!/bin/sh
2+
export MY_ENV_SCRIPT_SET_A_VAR_TO=something

src/main/java/org/codehaus/mojo/exec/ExecMojo.java

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121

2222
import java.io.BufferedOutputStream;
23+
import java.io.BufferedReader;
2324
import java.io.File;
2425
import java.io.FileOutputStream;
2526
import java.io.FileWriter;
@@ -277,9 +278,11 @@ public class ExecMojo extends AbstractExecMojo {
277278
private Map<String, String> environmentVariables = new HashMap<>();
278279

279280
/**
280-
* Environment script to be merged with <i>environmentVariables</i> This script is platform specifics, on Unix its
281-
* must be Bourne shell format. Use this feature if you have a need to create environment variable dynamically such
282-
* as invoking Visual Studio environment script file
281+
* Environment script to be merged with <i>environmentVariables</i>. on Unix-like system if the script
282+
* contains a shebang line, the executable filename is read from the shebang line; otherwise,
283+
* Bourne shell format is used.
284+
* Use this feature if you have a need to create environment variable dynamically such as invoking
285+
* Visual Studio environment script file.
283286
*
284287
* @since 1.4.0
285288
*/
@@ -1013,6 +1016,22 @@ private void createArgFile(String filePath, List<String> lines) throws IOExcepti
10131016
}
10141017
}
10151018

1019+
String getShebang(File script) {
1020+
if (script == null || !script.canRead()) {
1021+
getLog().warn("Cannot read script file " + script);
1022+
return null;
1023+
}
1024+
try (BufferedReader reader = Files.newBufferedReader(script.toPath())) {
1025+
String line = reader.readLine();
1026+
if (line != null && line.startsWith("#!")) {
1027+
return line.substring(2).trim();
1028+
}
1029+
} catch (IOException e) {
1030+
getLog().warn("Could not read shebang from " + script.getAbsolutePath(), e);
1031+
}
1032+
return null;
1033+
}
1034+
10161035
protected Map<String, String> createEnvs(File envScriptFile) throws MojoExecutionException {
10171036
Map<String, String> results = null;
10181037

@@ -1023,7 +1042,16 @@ protected Map<String, String> createEnvs(File envScriptFile) throws MojoExecutio
10231042
Commandline cl = new Commandline(); // commons-exec instead?
10241043
cl.setExecutable(tmpEnvExecFile.getAbsolutePath());
10251044
if (!OS.isFamilyWindows()) {
1026-
cl.setExecutable("sh");
1045+
String shebang = getShebang(envScriptFile);
1046+
if (shebang != null && !shebang.isEmpty()) {
1047+
String[] parts = shebang.split("\\s+");
1048+
cl.setExecutable(parts[0]);
1049+
for (int i = 1; i < parts.length; i++) {
1050+
cl.createArg().setValue(parts[i]);
1051+
}
1052+
} else {
1053+
cl.setExecutable("sh");
1054+
}
10271055
cl.createArg().setFile(tmpEnvExecFile);
10281056
}
10291057

@@ -1077,8 +1105,13 @@ protected File createEnvWrapperFile(File envScript) throws IOException {
10771105
} else {
10781106
tmpFile = Files.createTempFile("env", ".sh").toFile();
10791107
// tmpFile.setExecutable( true );//java 6 only
1108+
String shebang = getShebang(envScript);
10801109
try (PrintWriter writer = new PrintWriter(tmpFile)) {
1081-
writer.append("#! /bin/sh").println();
1110+
if (shebang != null && !shebang.isEmpty()) {
1111+
writer.append("#!").append(shebang).println();
1112+
} else {
1113+
writer.append("#!/bin/sh").println();
1114+
}
10821115
writer.append(". ").append(envScript.getCanonicalPath()).println(); // works on all unix??
10831116
writer.append("echo " + EnvStreamConsumer.START_PARSING_INDICATOR)
10841117
.println();

src/test/java/org/codehaus/mojo/exec/ExecMojoTest.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,4 +356,80 @@ private String getCommandLineAsString(CommandLine commandline) {
356356
}
357357
return result;
358358
}
359+
360+
public void testGetShebang() throws Exception {
361+
ExecMojo execMojo = new ExecMojo();
362+
363+
// without shebang
364+
File noShebang = Files.createTempFile("noShebang", ".sh").toFile();
365+
Files.write(noShebang.toPath(), Arrays.asList("echo hello"), StandardCharsets.UTF_8);
366+
assertNull(execMojo.getShebang(noShebang));
367+
noShebang.delete();
368+
369+
// with shebang
370+
File withShebang = Files.createTempFile("withShebang", ".sh").toFile();
371+
Files.write(withShebang.toPath(), Arrays.asList("#!/bin/bash -x", "echo hello"), StandardCharsets.UTF_8);
372+
assertEquals("/bin/bash -x", execMojo.getShebang(withShebang));
373+
withShebang.delete();
374+
375+
// with shebang but no args
376+
File withShebangNoArgs =
377+
Files.createTempFile("withShebangNoArgs", ".sh").toFile();
378+
Files.write(
379+
withShebangNoArgs.toPath(),
380+
Arrays.asList("#!/usr/bin/env python3", "print('hello')"),
381+
StandardCharsets.UTF_8);
382+
assertEquals("/usr/bin/env python3", execMojo.getShebang(withShebangNoArgs));
383+
withShebangNoArgs.delete();
384+
}
385+
386+
public void testCreateEnvWrapperFile() throws Exception {
387+
ExecMojo execMojo = new ExecMojo();
388+
389+
// without shebang
390+
File envScript = Files.createTempFile("envScript", ".sh").toFile();
391+
Files.write(envScript.toPath(), Arrays.asList("export TEST_VAR=test_value"), StandardCharsets.UTF_8);
392+
File wrapper = execMojo.createEnvWrapperFile(envScript);
393+
List<String> lines = Files.readAllLines(wrapper.toPath(), StandardCharsets.UTF_8);
394+
395+
if (OS.isFamilyWindows()) {
396+
assertEquals("@echo off", lines.get(0));
397+
assertTrue(lines.get(1).startsWith("call \""));
398+
assertTrue(lines.get(1).endsWith(envScript.getCanonicalPath() + "\""));
399+
assertEquals("echo " + EnvStreamConsumer.START_PARSING_INDICATOR, lines.get(2));
400+
assertEquals("set", lines.get(3));
401+
} else {
402+
assertEquals("#!/bin/sh", lines.get(0));
403+
assertEquals(". " + envScript.getCanonicalPath(), lines.get(1));
404+
assertEquals("echo " + EnvStreamConsumer.START_PARSING_INDICATOR, lines.get(2));
405+
assertEquals("env", lines.get(3));
406+
}
407+
wrapper.delete();
408+
envScript.delete();
409+
410+
// with shebang
411+
File envScriptWithShebang =
412+
Files.createTempFile("envScriptWithShebang", ".sh").toFile();
413+
Files.write(
414+
envScriptWithShebang.toPath(),
415+
Arrays.asList("#!/bin/bash", "export TEST_VAR=test_value"),
416+
StandardCharsets.UTF_8);
417+
File wrapper2 = execMojo.createEnvWrapperFile(envScriptWithShebang);
418+
List<String> lines2 = Files.readAllLines(wrapper2.toPath(), StandardCharsets.UTF_8);
419+
420+
if (OS.isFamilyWindows()) {
421+
assertEquals("@echo off", lines2.get(0));
422+
assertTrue(lines2.get(1).startsWith("call \""));
423+
assertTrue(lines2.get(1).endsWith(envScriptWithShebang.getCanonicalPath() + "\""));
424+
assertEquals("echo " + EnvStreamConsumer.START_PARSING_INDICATOR, lines2.get(2));
425+
assertEquals("set", lines2.get(3));
426+
} else {
427+
assertEquals("#!/bin/bash", lines2.get(0));
428+
assertEquals(". " + envScriptWithShebang.getCanonicalPath(), lines2.get(1));
429+
assertEquals("echo " + EnvStreamConsumer.START_PARSING_INDICATOR, lines2.get(2));
430+
assertEquals("env", lines2.get(3));
431+
}
432+
wrapper2.delete();
433+
envScriptWithShebang.delete();
434+
}
359435
}

0 commit comments

Comments
 (0)