| @@ -0,0 +1,7 | |||||
|
|
1 | [versions] | |||
|
|
2 | immutables = "2.10.1" | |||
|
|
3 | jdt = "2.3.0" | |||
|
|
4 | ||||
|
|
5 | [libraries] | |||
|
|
6 | immutables = { module = "org.immutables:value", version.ref = "immutables" } | |||
|
|
7 | jdt-annotations = { module = "org.eclipse.jdt:org.eclipse.jdt.annotation", version.ref = "jdt" } | |||
| @@ -1,40 +1,41 | |||||
| 1 | plugins { |
|
1 | plugins { | |
| 2 | id "java-library" |
|
2 | id "java-library" | |
| 3 | id "ivy-publish" |
|
3 | id "ivy-publish" | |
| 4 | } |
|
4 | } | |
| 5 |
|
5 | |||
| 6 | java { |
|
6 | java { | |
| 7 | withJavadocJar() |
|
7 | withJavadocJar() | |
| 8 | withSourcesJar() |
|
8 | withSourcesJar() | |
| 9 | toolchain { |
|
9 | toolchain { | |
| 10 | languageVersion = JavaLanguageVersion.of(17) |
|
10 | languageVersion = JavaLanguageVersion.of(17) | |
| 11 | } |
|
11 | } | |
| 12 | } |
|
12 | } | |
| 13 |
|
13 | |||
| 14 | dependencies { |
|
14 | dependencies { | |
| 15 |
compileOnly |
|
15 | compileOnly libs.jdt.annotations | |
|
|
16 | ||||
| 16 | api gradleApi() |
|
17 | api gradleApi() | |
| 17 | } |
|
18 | } | |
| 18 |
|
19 | |||
| 19 | task printVersion{ |
|
20 | task printVersion{ | |
| 20 | doLast { |
|
21 | doLast { | |
| 21 | println "project: $project.group:$project.name:$project.version" |
|
22 | println "project: $project.group:$project.name:$project.version" | |
| 22 | println "jar: ${->jar.archiveFileName.get()}" |
|
23 | println "jar: ${->jar.archiveFileName.get()}" | |
| 23 | } |
|
24 | } | |
| 24 | } |
|
25 | } | |
| 25 |
|
26 | |||
| 26 | publishing { |
|
27 | publishing { | |
| 27 | repositories { |
|
28 | repositories { | |
| 28 | ivy { |
|
29 | ivy { | |
| 29 | url "${System.properties["user.home"]}/ivy-repo" |
|
30 | url "${System.properties["user.home"]}/ivy-repo" | |
| 30 | } |
|
31 | } | |
| 31 | } |
|
32 | } | |
| 32 | publications { |
|
33 | publications { | |
| 33 | ivy(IvyPublication) { |
|
34 | ivy(IvyPublication) { | |
| 34 | from components.java |
|
35 | from components.java | |
| 35 | descriptor.description { |
|
36 | descriptor.description { | |
| 36 | text = providers.provider({ description }) |
|
37 | text = providers.provider({ description }) | |
| 37 | } |
|
38 | } | |
| 38 | } |
|
39 | } | |
| 39 | } |
|
40 | } | |
| 40 | } No newline at end of file |
|
41 | } | |
| @@ -1,67 +1,83 | |||||
| 1 | package org.implab.gradle.common.exec; |
|
1 | package org.implab.gradle.common.exec; | |
| 2 |
|
2 | |||
|
|
3 | import java.util.function.Consumer; | |||
|
|
4 | ||||
| 3 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
5 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 4 | import org.implab.gradle.common.utils.Values; |
|
6 | import org.implab.gradle.common.utils.Values; | |
| 5 |
|
7 | |||
| 6 | /** Command builder interface, used to specify the executable and parameters */ |
|
8 | /** Command builder interface, used to specify the executable and parameters */ | |
| 7 | @NonNullByDefault |
|
9 | @NonNullByDefault | |
| 8 | public interface CommandBuilder { |
|
10 | public interface CommandBuilder { | |
| 9 |
|
11 | |||
| 10 | /** Sets the executable, the parameters are left intact. */ |
|
12 | /** Sets the executable, the parameters are left intact. */ | |
| 11 | CommandBuilder executable(String executable); |
|
13 | CommandBuilder executable(String executable); | |
| 12 |
|
14 | |||
| 13 | /** |
|
15 | /** | |
| 14 | * Sets the specified executable and parameters, old executable and parameters |
|
16 | * Sets the specified executable and parameters, old executable and parameters | |
| 15 | * are discarded. |
|
17 | * are discarded. | |
| 16 | */ |
|
18 | */ | |
| 17 | default CommandBuilder commandLine(String executable, String... args) { |
|
19 | default CommandBuilder commandLine(String executable, String... args) { | |
| 18 | return executable(executable) |
|
20 | return executable(executable) | |
| 19 | .arguments(args); |
|
21 | .arguments(args); | |
| 20 | } |
|
22 | } | |
| 21 |
|
23 | |||
| 22 | /** |
|
24 | /** | |
| 23 | * Sets the specified executable and parameters, old executable and parameters |
|
25 | * Sets the specified executable and parameters, old executable and parameters | |
| 24 | * are discarded. |
|
26 | * are discarded. | |
| 25 | * |
|
27 | * | |
| 26 | * @param command The command line. Must contain at least one element |
|
28 | * @param command The command line. Must contain at least one element | |
| 27 | * (executable). |
|
29 | * (executable). | |
| 28 | * |
|
30 | * | |
| 29 | */ |
|
31 | */ | |
| 30 | default CommandBuilder commandLine(Iterable<? extends String> command) { |
|
32 | default CommandBuilder commandLine(Iterable<? extends String> command) { | |
| 31 | var iterator = command.iterator(); |
|
33 | var iterator = command.iterator(); | |
| 32 |
|
34 | |||
| 33 | // set executable |
|
35 | // set executable | |
| 34 | executable(Values.take(iterator).orElseThrow()); |
|
36 | executable(Values.take(iterator).orElseThrow()); | |
| 35 | // cleat arguments |
|
37 | // cleat arguments | |
| 36 | arguments(); |
|
38 | arguments(); | |
| 37 |
|
39 | |||
| 38 | // set new arguments |
|
40 | // set new arguments | |
| 39 | while (iterator.hasNext()) |
|
41 | while (iterator.hasNext()) | |
| 40 | addArguments(iterator.next()); |
|
42 | addArguments(iterator.next()); | |
| 41 |
|
43 | |||
| 42 | return this; |
|
44 | return this; | |
| 43 | } |
|
45 | } | |
| 44 |
|
46 | |||
|
|
47 | default Consumer<Boolean> flag(String name) { | |||
|
|
48 | return f -> { | |||
|
|
49 | if (f) | |||
|
|
50 | addArguments(name); | |||
|
|
51 | }; | |||
|
|
52 | } | |||
|
|
53 | ||||
|
|
54 | default Consumer<String> param(String name) { | |||
|
|
55 | return v -> { | |||
|
|
56 | if (v != null && v.length() > 0) | |||
|
|
57 | addArguments(name, v); | |||
|
|
58 | }; | |||
|
|
59 | } | |||
|
|
60 | ||||
| 45 | /** Adds the specified arguments to this builder */ |
|
61 | /** Adds the specified arguments to this builder */ | |
| 46 |
CommandBuilder addArguments( |
|
62 | CommandBuilder addArguments(String... args); | |
| 47 |
|
63 | |||
| 48 | default CommandBuilder addArguments(Iterable<String> args) { |
|
64 | default CommandBuilder addArguments(Iterable<String> args) { | |
| 49 | for (String arg : args) |
|
65 | for (String arg : args) | |
| 50 | addArguments(arg); |
|
66 | addArguments(arg); | |
| 51 |
|
67 | |||
| 52 | return this; |
|
68 | return this; | |
| 53 | } |
|
69 | } | |
| 54 |
|
70 | |||
| 55 | /** Replaces arguments in the builder with the specified one. */ |
|
71 | /** Replaces arguments in the builder with the specified one. */ | |
| 56 | default CommandBuilder arguments(String... args) { |
|
72 | default CommandBuilder arguments(String... args) { | |
| 57 | return arguments(Values.iterable(args)); |
|
73 | return arguments(Values.iterable(args)); | |
| 58 | } |
|
74 | } | |
| 59 |
|
75 | |||
| 60 | /** Replaces arguments in the builder with the specified one. */ |
|
76 | /** Replaces arguments in the builder with the specified one. */ | |
| 61 | CommandBuilder arguments(Iterable<String> args); |
|
77 | CommandBuilder arguments(Iterable<String> args); | |
| 62 |
|
78 | |||
| 63 | default CommandBuilder from(CommandSpec commandSpec) { |
|
79 | default CommandBuilder from(CommandSpec commandSpec) { | |
| 64 | return executable(commandSpec.executable()) |
|
80 | return executable(commandSpec.executable()) | |
| 65 | .arguments(commandSpec.arguments()); |
|
81 | .arguments(commandSpec.arguments()); | |
| 66 | } |
|
82 | } | |
| 67 | } |
|
83 | } | |
| @@ -1,31 +1,33 | |||||
| 1 | package org.implab.gradle.common.exec; |
|
1 | package org.implab.gradle.common.exec; | |
| 2 |
|
2 | |||
| 3 | import java.io.File; |
|
3 | import java.io.File; | |
| 4 | import java.util.Map; |
|
4 | import java.util.Map; | |
| 5 | import java.util.Optional; |
|
5 | import java.util.Optional; | |
| 6 |
|
6 | |||
| 7 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
7 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 8 |
|
8 | |||
| 9 | @NonNullByDefault |
|
9 | @NonNullByDefault | |
| 10 | public interface EnvironmentBuilder { |
|
10 | public interface EnvironmentBuilder { | |
| 11 | /** Sets the specified environment variable */ |
|
11 | /** Sets the specified environment variable */ | |
| 12 |
EnvironmentBuilder |
|
12 | EnvironmentBuilder putEnvironment(String envVar, String value); | |
| 13 |
|
13 | |||
| 14 | /** Replaces environment with the supplied one */ |
|
14 | /** Replaces environment with the supplied one */ | |
| 15 | EnvironmentBuilder environment(Map<String, ? extends String> env); |
|
15 | EnvironmentBuilder environment(Map<String, ? extends String> env); | |
| 16 |
|
16 | |||
| 17 | /** Unsets the specified variable. If the variable is inherited it removed anyway. */ |
|
17 | /** | |
| 18 | EnvironmentBuilder unsetVariable(String envVar); |
|
18 | * Enables or disables environment inheritance for the child process | |
|
|
19 | */ | |||
|
|
20 | EnvironmentBuilder inheritEnvironment(boolean inherit); | |||
| 19 |
|
21 | |||
| 20 | /** Specifies the working directory */ |
|
22 | /** Specifies the working directory */ | |
| 21 | EnvironmentBuilder workingDirectory(File directory); |
|
23 | EnvironmentBuilder workingDirectory(File directory); | |
| 22 |
|
24 | |||
| 23 | /** Specifies the working directory */ |
|
25 | /** Specifies the working directory */ | |
| 24 | EnvironmentBuilder workingDirectory(Optional<? extends File> directory); |
|
26 | EnvironmentBuilder workingDirectory(Optional<? extends File> directory); | |
| 25 |
|
27 | |||
| 26 | /** Copies the supplied environment to this one */ |
|
28 | /** Copies the supplied environment to this one */ | |
| 27 | default EnvironmentBuilder from(EnvironmentSpec environmentSpec) { |
|
29 | default EnvironmentBuilder from(EnvironmentSpec environmentSpec) { | |
| 28 | return environment(environmentSpec.environment()) |
|
30 | return environment(environmentSpec.environment()) | |
| 29 | .workingDirectory(environmentSpec.workingDirectory()); |
|
31 | .workingDirectory(environmentSpec.workingDirectory()); | |
| 30 | } |
|
32 | } | |
| 31 | } |
|
33 | } | |
| @@ -1,11 +1,14 | |||||
| 1 | package org.implab.gradle.common.exec; |
|
1 | package org.implab.gradle.common.exec; | |
| 2 |
|
2 | |||
| 3 | import java.io.File; |
|
3 | import java.io.File; | |
| 4 | import java.util.Map; |
|
4 | import java.util.Map; | |
| 5 | import java.util.Optional; |
|
5 | import java.util.Optional; | |
| 6 |
|
6 | |||
| 7 | public interface EnvironmentSpec { |
|
7 | public interface EnvironmentSpec { | |
|
|
8 | ||||
|
|
9 | boolean inheritEnvironment(); | |||
|
|
10 | ||||
| 8 | Map<String,String> environment(); |
|
11 | Map<String,String> environment(); | |
| 9 |
|
12 | |||
| 10 | Optional<File> workingDirectory(); |
|
13 | Optional<File> workingDirectory(); | |
| 11 | } |
|
14 | } | |
| @@ -1,265 +1,264 | |||||
| 1 | package org.implab.gradle.common.exec; |
|
1 | package org.implab.gradle.common.exec; | |
| 2 |
|
2 | |||
| 3 | import java.util.ArrayList; |
|
3 | import java.util.ArrayList; | |
| 4 | import java.util.HashMap; |
|
4 | import java.util.HashMap; | |
| 5 | import java.util.List; |
|
5 | import java.util.List; | |
| 6 | import java.util.Map; |
|
6 | import java.util.Map; | |
| 7 | import java.util.Optional; |
|
7 | import java.util.Optional; | |
| 8 | import java.util.concurrent.CompletableFuture; |
|
8 | import java.util.concurrent.CompletableFuture; | |
| 9 |
|
9 | |||
| 10 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
10 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 11 | import org.eclipse.jdt.annotation.Nullable; |
|
11 | import org.eclipse.jdt.annotation.Nullable; | |
| 12 |
|
12 | |||
| 13 | import java.io.File; |
|
13 | import java.io.File; | |
| 14 | import java.io.IOException; |
|
14 | import java.io.IOException; | |
| 15 |
|
15 | |||
| 16 | import static java.util.Objects.requireNonNull; |
|
16 | import static java.util.Objects.requireNonNull; | |
| 17 |
|
17 | |||
| 18 | /** Command line builder */ |
|
18 | /** Command line builder */ | |
| 19 | @NonNullByDefault |
|
19 | @NonNullByDefault | |
| 20 | public abstract class ExecBuilder implements |
|
20 | public abstract class ExecBuilder implements | |
| 21 | CommandBuilder, |
|
21 | CommandBuilder, | |
| 22 | PipeBuilder, |
|
22 | PipeBuilder, | |
| 23 | EnvironmentBuilder, |
|
23 | EnvironmentBuilder, | |
| 24 | Runnable |
|
24 | Executable { | |
| 25 | { |
|
|||
| 26 | private String executable; |
|
25 | private String executable; | |
| 27 |
|
26 | |||
| 28 | private final List<String> arguments = new ArrayList<>(); |
|
27 | private final List<String> arguments = new ArrayList<>(); | |
| 29 |
|
28 | |||
|
|
29 | private boolean inheritEnvironment = true; | |||
|
|
30 | ||||
| 30 | private final Map<String, String> environment = new HashMap<>(); |
|
31 | private final Map<String, String> environment = new HashMap<>(); | |
| 31 |
|
32 | |||
| 32 | private @Nullable File directory; |
|
33 | private @Nullable File directory; | |
| 33 |
|
34 | |||
| 34 | private RedirectFrom inputRedirect; |
|
35 | private RedirectFrom inputRedirect; | |
| 35 |
|
36 | |||
| 36 | private RedirectTo outputRedirect; |
|
37 | private RedirectTo outputRedirect; | |
| 37 |
|
38 | |||
| 38 | private RedirectTo errorRedirect; |
|
39 | private RedirectTo errorRedirect; | |
| 39 |
|
40 | |||
| 40 | @Override |
|
41 | @Override | |
| 41 | public ExecBuilder executable(String executable) { |
|
42 | public ExecBuilder executable(String executable) { | |
| 42 | requireNonNull(executable, "cmd can't be null"); |
|
43 | requireNonNull(executable, "cmd can't be null"); | |
| 43 | this.executable = executable; |
|
44 | this.executable = executable; | |
| 44 | return this; |
|
45 | return this; | |
| 45 | } |
|
46 | } | |
| 46 |
|
47 | |||
| 47 | @Override |
|
48 | @Override | |
| 48 | public ExecBuilder arguments(Iterable<String> args) { |
|
49 | public ExecBuilder arguments(Iterable<String> args) { | |
| 49 | requireNonNull(args, "Args must not be null"); |
|
50 | requireNonNull(args, "Args must not be null"); | |
| 50 | arguments.clear(); |
|
51 | arguments.clear(); | |
| 51 | for(var arg: args) |
|
52 | for (var arg : args) | |
| 52 | arguments.add(arg); |
|
53 | arguments.add(arg); | |
| 53 | return this; |
|
54 | return this; | |
| 54 | } |
|
55 | } | |
| 55 |
|
56 | |||
| 56 | @Override |
|
57 | @Override | |
| 57 |
public ExecBuilder addArguments( |
|
58 | public ExecBuilder addArguments(String... args) { | |
| 58 | requireNonNull(arg0, "arg0 parameter can't be null"); |
|
|||
| 59 | arguments.add(arg0); |
|
|||
| 60 |
|
||||
| 61 | for (var arg : args) |
|
59 | for (var arg : args) | |
| 62 | arguments.add(arg); |
|
60 | arguments.add(requireNonNull(arg, "arguments element shouldn't be null")); | |
| 63 |
|
61 | |||
| 64 | return this; |
|
62 | return this; | |
| 65 | } |
|
63 | } | |
| 66 |
|
64 | |||
| 67 | /** Sets the working directory */ |
|
65 | /** Sets the working directory */ | |
| 68 | @Override |
|
66 | @Override | |
| 69 | public ExecBuilder workingDirectory(File directory) { |
|
67 | public ExecBuilder workingDirectory(File directory) { | |
| 70 | requireNonNull(directory, "directory parameter can't be null"); |
|
68 | requireNonNull(directory, "directory parameter can't be null"); | |
| 71 |
|
69 | |||
| 72 | this.directory = directory; |
|
70 | this.directory = directory; | |
| 73 | return this; |
|
71 | return this; | |
| 74 | } |
|
72 | } | |
| 75 |
|
73 | |||
| 76 | @Override |
|
74 | @Override | |
| 77 | public ExecBuilder workingDirectory(Optional<? extends File> directory) { |
|
75 | public ExecBuilder workingDirectory(Optional<? extends File> directory) { | |
| 78 | requireNonNull(directory, "directory parameter can't be null"); |
|
76 | requireNonNull(directory, "directory parameter can't be null"); | |
| 79 |
|
77 | |||
| 80 | this.directory = directory.orElse(null); |
|
78 | this.directory = directory.orElse(null); | |
| 81 | return this; |
|
79 | return this; | |
| 82 | } |
|
80 | } | |
| 83 |
|
81 | |||
|
|
82 | @Override | |||
|
|
83 | public ExecBuilder inheritEnvironment(boolean inherit) { | |||
|
|
84 | this.inheritEnvironment = inherit; | |||
|
|
85 | return this; | |||
|
|
86 | } | |||
|
|
87 | ||||
| 84 | /** |
|
88 | /** | |
| 85 | * Sets the environment value. The value cannot be null. |
|
89 | * Sets the environment value. The value cannot be null. | |
| 86 | * |
|
90 | * | |
| 87 | * @param envVar The name of the environment variable |
|
91 | * @param envVar The name of the environment variable | |
| 88 | * @param value The value to set, |
|
92 | * @param value The value to set, | |
| 89 | */ |
|
93 | */ | |
| 90 | @Override |
|
94 | @Override | |
| 91 |
public ExecBuilder |
|
95 | public ExecBuilder putEnvironment(String envVar, String value) { | |
| 92 | requireNonNull(value, "Value can't be null"); |
|
96 | requireNonNull(value, "Value can't be null"); | |
| 93 | requireNonNull(envVar, "envVar parameter can't be null"); |
|
97 | requireNonNull(envVar, "envVar parameter can't be null"); | |
| 94 |
|
98 | |||
| 95 | environment.put(envVar, value); |
|
99 | environment.put(envVar, value); | |
| 96 | return this; |
|
100 | return this; | |
| 97 | } |
|
101 | } | |
| 98 |
|
102 | |||
| 99 | @Override |
|
103 | @Override | |
| 100 |
public ExecBuilder |
|
104 | public ExecBuilder environment(Map<String, ? extends String> env) { | |
| 101 | requireNonNull(envVar, "envVar parameter can't be null"); |
|
|||
| 102 |
|
||||
| 103 | environment.remove(envVar); |
|
|||
| 104 | return this; |
|
|||
| 105 | } |
|
|||
| 106 |
|
||||
| 107 | @Override |
|
|||
| 108 | public ExecBuilder environment(Map<String,? extends String> env) { |
|
|||
| 109 | requireNonNull(env, "env parameter can't be null"); |
|
105 | requireNonNull(env, "env parameter can't be null"); | |
| 110 |
|
106 | |||
| 111 | environment.clear(); |
|
107 | environment.clear(); | |
| 112 | environment.putAll(env); |
|
108 | environment.putAll(env); | |
| 113 | return this; |
|
109 | return this; | |
| 114 | } |
|
110 | } | |
| 115 |
|
111 | |||
| 116 | /** |
|
112 | /** | |
| 117 | * Sets redirection for the stdin, {@link RedirectFrom} will be applied |
|
113 | * Sets redirection for the stdin, {@link RedirectFrom} will be applied | |
| 118 | * every time the process is started. |
|
114 | * every time the process is started. | |
| 119 | * |
|
115 | * | |
| 120 | * <p> |
|
116 | * <p> | |
| 121 | * If redirection |
|
117 | * If redirection | |
| 122 | */ |
|
118 | */ | |
| 123 | @Override |
|
119 | @Override | |
| 124 | public ExecBuilder stdin(RedirectFrom from) { |
|
120 | public ExecBuilder stdin(RedirectFrom from) { | |
| 125 | requireNonNull(from, "from parameter can't be null"); |
|
121 | requireNonNull(from, "from parameter can't be null"); | |
| 126 |
|
122 | |||
| 127 | inputRedirect = from; |
|
123 | inputRedirect = from; | |
| 128 | return this; |
|
124 | return this; | |
| 129 | } |
|
125 | } | |
| 130 |
|
126 | |||
| 131 | @Override |
|
127 | @Override | |
| 132 | public ExecBuilder stdin(Optional<? extends RedirectFrom> from) { |
|
128 | public ExecBuilder stdin(Optional<? extends RedirectFrom> from) { | |
| 133 | requireNonNull(from, "from parameter can't be null"); |
|
129 | requireNonNull(from, "from parameter can't be null"); | |
| 134 | inputRedirect = from.orElse(null); |
|
130 | inputRedirect = from.orElse(null); | |
| 135 | return this; |
|
131 | return this; | |
| 136 | } |
|
132 | } | |
| 137 |
|
133 | |||
| 138 | /** |
|
134 | /** | |
| 139 | * Sets redirection for the stdout, {@link RedirectTo} will be applied |
|
135 | * Sets redirection for the stdout, {@link RedirectTo} will be applied | |
| 140 | * every time the process is started. |
|
136 | * every time the process is started. | |
| 141 | */ |
|
137 | */ | |
| 142 | @Override |
|
138 | @Override | |
| 143 | public ExecBuilder stdout(RedirectTo out) { |
|
139 | public ExecBuilder stdout(RedirectTo out) { | |
| 144 | requireNonNull(out, "out parameter can't be null"); |
|
140 | requireNonNull(out, "out parameter can't be null"); | |
| 145 | outputRedirect = out; |
|
141 | outputRedirect = out; | |
| 146 | return this; |
|
142 | return this; | |
| 147 | } |
|
143 | } | |
| 148 |
|
144 | |||
| 149 | @Override |
|
145 | @Override | |
| 150 | public ExecBuilder stdout(Optional<? extends RedirectTo> to) { |
|
146 | public ExecBuilder stdout(Optional<? extends RedirectTo> to) { | |
| 151 | requireNonNull(to, "from parameter can't be null"); |
|
147 | requireNonNull(to, "from parameter can't be null"); | |
| 152 | outputRedirect = to.orElse(null); |
|
148 | outputRedirect = to.orElse(null); | |
| 153 | return this; |
|
149 | return this; | |
| 154 | } |
|
150 | } | |
| 155 |
|
151 | |||
| 156 | /** |
|
152 | /** | |
| 157 | * Sets redirection for the stderr, {@link RedirectTo} will be applied |
|
153 | * Sets redirection for the stderr, {@link RedirectTo} will be applied | |
| 158 | * every time the process is started. |
|
154 | * every time the process is started. | |
| 159 | */ |
|
155 | */ | |
| 160 | @Override |
|
156 | @Override | |
| 161 | public ExecBuilder stderr(RedirectTo err) { |
|
157 | public ExecBuilder stderr(RedirectTo err) { | |
| 162 | requireNonNull(err, "err parameter can't be null"); |
|
158 | requireNonNull(err, "err parameter can't be null"); | |
| 163 | errorRedirect = err; |
|
159 | errorRedirect = err; | |
| 164 | return this; |
|
160 | return this; | |
| 165 | } |
|
161 | } | |
| 166 |
|
162 | |||
| 167 | @Override |
|
163 | @Override | |
| 168 | public ExecBuilder stderr(Optional<? extends RedirectTo> to) { |
|
164 | public ExecBuilder stderr(Optional<? extends RedirectTo> to) { | |
| 169 | requireNonNull(to, "from parameter can't be null"); |
|
165 | requireNonNull(to, "from parameter can't be null"); | |
| 170 | errorRedirect = to.orElse(null); |
|
166 | errorRedirect = to.orElse(null); | |
| 171 | return this; |
|
167 | return this; | |
| 172 | } |
|
168 | } | |
| 173 |
|
169 | |||
| 174 | @Override |
|
170 | @Override | |
| 175 | public ExecBuilder from(CommandSpec commandSpec) { |
|
171 | public ExecBuilder from(CommandSpec commandSpec) { | |
| 176 | CommandBuilder.super.from(commandSpec); |
|
172 | CommandBuilder.super.from(commandSpec); | |
| 177 | return this; |
|
173 | return this; | |
| 178 | } |
|
174 | } | |
| 179 |
|
175 | |||
| 180 | @Override |
|
176 | @Override | |
| 181 | public ExecBuilder from(PipeSpec pipeSpec) { |
|
177 | public ExecBuilder from(PipeSpec pipeSpec) { | |
| 182 | PipeBuilder.super.from(pipeSpec); |
|
178 | PipeBuilder.super.from(pipeSpec); | |
| 183 | return this; |
|
179 | return this; | |
| 184 | } |
|
180 | } | |
| 185 |
|
181 | |||
| 186 | @Override |
|
182 | @Override | |
| 187 | public ExecBuilder from(EnvironmentSpec environmentSpec) { |
|
183 | public ExecBuilder from(EnvironmentSpec environmentSpec) { | |
| 188 | EnvironmentBuilder.super.from(environmentSpec); |
|
184 | EnvironmentBuilder.super.from(environmentSpec); | |
| 189 | return this; |
|
185 | return this; | |
| 190 | } |
|
186 | } | |
| 191 |
|
187 | |||
| 192 | /** Implement this function to */ |
|
188 | /** Implement this function to */ | |
| 193 | protected abstract CompletableFuture<Integer> startInternal( |
|
189 | protected abstract CompletableFuture<Integer> startInternal( | |
| 194 | CommandSpec command, |
|
190 | CommandSpec command, | |
| 195 | EnvironmentSpec environment, |
|
191 | EnvironmentSpec environment, | |
| 196 | PipeSpec redirect) throws IOException; |
|
192 | PipeSpec redirect) throws IOException; | |
| 197 |
|
193 | |||
| 198 | /** |
|
194 | /** | |
| 199 | * Creates and starts new process and returns {@link CompletableFuture}. The |
|
195 | * Creates and starts new process and returns {@link CompletableFuture}. The | |
| 200 | * process may be a remote process, or a shell process or etc. |
|
196 | * process may be a remote process, or a shell process or etc. | |
| 201 | * |
|
197 | * | |
| 202 | * @return |
|
198 | * @return | |
| 203 | * @throws IOException |
|
199 | * @throws IOException | |
| 204 | */ |
|
200 | */ | |
| 205 |
public CompletableFuture<Integer> |
|
201 | public CompletableFuture<Integer> exec() throws IOException { | |
| 206 | if (executable == null || executable.isEmpty()) |
|
202 | if (executable == null || executable.isEmpty()) | |
| 207 | throw new IllegalStateException("The executable isn't set"); |
|
203 | throw new IllegalStateException("The executable isn't set"); | |
| 208 |
|
204 | |||
| 209 | var commandLine = new ArrayList<String>(); |
|
205 | var commandLine = new ArrayList<String>(); | |
| 210 | commandLine.add(executable); |
|
206 | commandLine.add(executable); | |
| 211 | commandLine.addAll(arguments); |
|
207 | commandLine.addAll(arguments); | |
| 212 |
|
208 | |||
| 213 | return startInternal( |
|
209 | return startInternal( | |
| 214 | new SelfCommandSpec(), |
|
210 | new SelfCommandSpec(), | |
| 215 | new SelfEnvironmentSpec(), |
|
211 | new SelfEnvironmentSpec(), | |
| 216 | new SelfPipeSpec() |
|
212 | new SelfPipeSpec()); | |
| 217 | ); |
|
|||
| 218 | } |
|
213 | } | |
| 219 |
|
214 | |||
| 220 | private class SelfCommandSpec implements CommandSpec { |
|
215 | private class SelfCommandSpec implements CommandSpec { | |
| 221 |
|
216 | |||
| 222 | @Override |
|
217 | @Override | |
| 223 | public String executable() { |
|
218 | public String executable() { | |
| 224 | return executable; |
|
219 | return executable; | |
| 225 | } |
|
220 | } | |
| 226 |
|
221 | |||
| 227 | @Override |
|
222 | @Override | |
| 228 | public List<String> arguments() { |
|
223 | public List<String> arguments() { | |
| 229 | return arguments; |
|
224 | return arguments; | |
| 230 | } |
|
225 | } | |
| 231 | } |
|
226 | } | |
| 232 |
|
227 | |||
| 233 | private class SelfEnvironmentSpec implements EnvironmentSpec { |
|
228 | private class SelfEnvironmentSpec implements EnvironmentSpec { | |
|
|
229 | @Override | |||
|
|
230 | public boolean inheritEnvironment() { | |||
|
|
231 | return inheritEnvironment; | |||
|
|
232 | } | |||
| 234 |
|
233 | |||
| 235 | @Override |
|
234 | @Override | |
| 236 | public Map<String, String> environment() { |
|
235 | public Map<String, String> environment() { | |
| 237 | return environment; |
|
236 | return environment; | |
| 238 | } |
|
237 | } | |
| 239 |
|
238 | |||
| 240 | @Override |
|
239 | @Override | |
| 241 | public Optional<File> workingDirectory() { |
|
240 | public Optional<File> workingDirectory() { | |
| 242 |
return Optional.ofNullable( |
|
241 | return Optional.ofNullable(directory); | |
| 243 | } |
|
242 | } | |
| 244 | } |
|
243 | } | |
| 245 |
|
244 | |||
| 246 | private class SelfPipeSpec implements PipeSpec { |
|
245 | private class SelfPipeSpec implements PipeSpec { | |
| 247 |
|
246 | |||
| 248 | @Override |
|
247 | @Override | |
| 249 | public Optional<RedirectTo> stdout() { |
|
248 | public Optional<RedirectTo> stdout() { | |
| 250 | return Optional.ofNullable(outputRedirect); |
|
249 | return Optional.ofNullable(outputRedirect); | |
| 251 | } |
|
250 | } | |
| 252 |
|
251 | |||
| 253 | @Override |
|
252 | @Override | |
| 254 | public Optional<RedirectTo> stderr() { |
|
253 | public Optional<RedirectTo> stderr() { | |
| 255 | return Optional.ofNullable(errorRedirect); |
|
254 | return Optional.ofNullable(errorRedirect); | |
| 256 | } |
|
255 | } | |
| 257 |
|
256 | |||
| 258 | @Override |
|
257 | @Override | |
| 259 | public Optional<RedirectFrom> stdin() { |
|
258 | public Optional<RedirectFrom> stdin() { | |
| 260 | return Optional.ofNullable(inputRedirect); |
|
259 | return Optional.ofNullable(inputRedirect); | |
| 261 | } |
|
260 | } | |
| 262 |
|
261 | |||
| 263 | } |
|
262 | } | |
| 264 |
|
263 | |||
| 265 | } |
|
264 | } | |
| @@ -1,19 +1,18 | |||||
| 1 | package org.implab.gradle.common.exec; |
|
1 | package org.implab.gradle.common.exec; | |
| 2 |
|
2 | |||
| 3 | import java.io.IOException; |
|
3 | import java.io.IOException; | |
| 4 | import java.util.concurrent.CompletableFuture; |
|
4 | import java.util.concurrent.CompletableFuture; | |
| 5 | import java.util.concurrent.CompletionException; |
|
5 | import java.util.concurrent.CompletionException; | |
| 6 |
|
6 | |||
| 7 |
public interface |
|
7 | public interface Executable { | |
| 8 |
CompletableFuture<Integer> |
|
8 | CompletableFuture<Integer> exec() throws IOException; | |
| 9 |
|
9 | |||
| 10 | /** Starts process and will throw error if error code is non-zero */ |
|
10 | /** Starts process and will throw error if error code is non-zero */ | |
| 11 |
default CompletableFuture< |
|
11 | default CompletableFuture<Void> execChecked() throws IOException { | |
| 12 |
return |
|
12 | return exec().thenAccept(code -> { | |
| 13 |
if ( |
|
13 | if (code != 0) | |
| 14 | throw new CompletionException(new IOException( |
|
14 | throw new CompletionException(new IOException( | |
| 15 | String.format("The process is terminated with code %d", code))); |
|
15 | String.format("The process is terminated with code %d", code))); | |
| 16 | return code; |
|
|||
| 17 | }); |
|
16 | }); | |
| 18 | } |
|
17 | } | |
| 19 | } |
|
18 | } | |
| @@ -1,49 +1,53 | |||||
| 1 | package org.implab.gradle.common.exec; |
|
1 | package org.implab.gradle.common.exec; | |
| 2 |
|
2 | |||
| 3 | import java.io.File; |
|
3 | import java.io.File; | |
| 4 | import java.io.FileInputStream; |
|
4 | import java.io.FileInputStream; | |
| 5 | import java.io.InputStream; |
|
5 | import java.io.InputStream; | |
| 6 | import java.io.OutputStream; |
|
6 | import java.io.OutputStream; | |
| 7 | import java.util.concurrent.CompletableFuture; |
|
7 | import java.util.concurrent.CompletableFuture; | |
| 8 |
|
8 | |||
| 9 | /** Describes how to redirect input streams. This interface is used to configure |
|
9 | /** | |
|
|
10 | * Describes how to redirect input streams. This interface is used to configure | |||
| 10 | * lazy redirection. {@link #redirect(OutputStream)} is called when the process |
|
11 | * lazy redirection. {@link #redirect(OutputStream)} is called when the process | |
| 11 | * is started. |
|
12 | * is started. Before the process is started the redirection isn't invoked and | |
|
|
13 | * no resources are allocated or used. | |||
| 12 | */ |
|
14 | */ | |
| 13 | public interface RedirectFrom { |
|
15 | public interface RedirectFrom { | |
| 14 | CompletableFuture<Void> redirect(OutputStream to); |
|
16 | CompletableFuture<Void> redirect(OutputStream to); | |
| 15 |
|
17 | |||
|
|
18 | /** | |||
|
|
19 | * Read file contents and redirect it to the output stream. | |||
|
|
20 | */ | |||
| 16 | public static RedirectFrom file(final File file) { |
|
21 | public static RedirectFrom file(final File file) { | |
| 17 | return to -> CompletableFuture.runAsync(() -> { |
|
22 | return to -> CompletableFuture.runAsync(() -> { | |
| 18 | try (var from = new FileInputStream(file); to) { |
|
23 | try (var from = new FileInputStream(file); to) { | |
| 19 | from.transferTo(to); |
|
24 | from.transferTo(to); | |
| 20 | } catch (Exception e) { |
|
25 | } catch (Exception e) { | |
| 21 | // silence! |
|
26 | // silence! | |
| 22 | } |
|
27 | } | |
| 23 | }); |
|
28 | }); | |
| 24 | } |
|
29 | } | |
| 25 |
|
30 | |||
| 26 | public static RedirectFrom stream(final InputStream from) { |
|
31 | public static RedirectFrom stream(final InputStream from) { | |
| 27 | return to -> CompletableFuture.runAsync(() -> { |
|
32 | return to -> CompletableFuture.runAsync(() -> { | |
| 28 | try (from; to) { |
|
33 | try (from; to) { | |
| 29 | from.transferTo(to); |
|
34 | from.transferTo(to); | |
| 30 | } catch (Exception e) { |
|
35 | } catch (Exception e) { | |
| 31 | // silence! |
|
36 | // silence! | |
| 32 | } |
|
37 | } | |
| 33 | }); |
|
38 | }); | |
| 34 | } |
|
39 | } | |
| 35 |
|
40 | |||
| 36 | public static RedirectFrom any(final Object output) { |
|
41 | public static RedirectFrom any(final Object output) { | |
| 37 | if (output instanceof File f) { |
|
42 | if (output instanceof File f) { | |
| 38 | return file(f); |
|
43 | return file(f); | |
| 39 | } else if (output instanceof InputStream stm) { |
|
44 | } else if (output instanceof InputStream stm) { | |
| 40 | return stream(stm); |
|
45 | return stream(stm); | |
| 41 | } else if (output instanceof RedirectFrom self) { |
|
46 | } else if (output instanceof RedirectFrom self) { | |
| 42 | return self; |
|
47 | return self; | |
| 43 | } else { |
|
48 | } else { | |
| 44 | throw new IllegalArgumentException("The specified argument type isn't supported: " + output.getClass()); |
|
49 | throw new IllegalArgumentException("The specified argument type isn't supported: " + output.getClass()); | |
| 45 | } |
|
50 | } | |
| 46 | } |
|
51 | } | |
| 47 |
|
52 | |||
| 48 |
|
||||
| 49 | } |
|
53 | } | |
| @@ -1,75 +1,90 | |||||
| 1 | package org.implab.gradle.common.exec; |
|
1 | package org.implab.gradle.common.exec; | |
| 2 |
|
2 | |||
| 3 | import java.io.File; |
|
3 | import java.io.File; | |
| 4 | import java.io.FileOutputStream; |
|
4 | import java.io.FileOutputStream; | |
| 5 | import java.io.InputStream; |
|
5 | import java.io.InputStream; | |
| 6 | import java.io.OutputStream; |
|
6 | import java.io.OutputStream; | |
| 7 | import java.util.Scanner; |
|
7 | import java.util.Scanner; | |
| 8 | import java.util.concurrent.CompletableFuture; |
|
8 | import java.util.concurrent.CompletableFuture; | |
| 9 | import java.util.function.Consumer; |
|
9 | import java.util.function.Consumer; | |
| 10 |
|
10 | |||
| 11 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
11 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 12 |
|
12 | |||
| 13 | /** |
|
13 | /** | |
| 14 | * RedirectSpec |
|
14 | * Redirection specification for the {@link InputStream}. Redirection is invoked | |
|
|
15 | * when the {@link InputStream} becomes available, for example, on process | |||
|
|
16 | * start. Before the process is started the redirection isn't invoked and no | |||
|
|
17 | * resources are allocated or used. | |||
| 15 | */ |
|
18 | */ | |
| 16 | @NonNullByDefault |
|
19 | @NonNullByDefault | |
| 17 | public interface RedirectTo { |
|
20 | public interface RedirectTo { | |
| 18 | CompletableFuture<Void> redirect(InputStream from); |
|
21 | CompletableFuture<Void> redirect(InputStream from); | |
| 19 |
|
22 | |||
| 20 | public interface StringConsumer extends Consumer<String> { |
|
23 | public interface StringConsumer extends Consumer<String> { | |
| 21 | } |
|
24 | } | |
| 22 |
|
25 | |||
|
|
26 | /** Creates a redirect to the specified consumer */ | |||
| 23 | public static RedirectTo consumer(final Consumer<String> consumer) { |
|
27 | public static RedirectTo consumer(final Consumer<String> consumer) { | |
| 24 | return consumer(new StringConsumer() { |
|
28 | return consumer(new StringConsumer() { | |
| 25 | @Override |
|
29 | @Override | |
| 26 | public void accept(String s) { |
|
30 | public void accept(String s) { | |
| 27 | consumer.accept(s); |
|
31 | consumer.accept(s); | |
| 28 | } |
|
32 | } | |
| 29 | }); |
|
33 | }); | |
| 30 | } |
|
34 | } | |
| 31 |
|
35 | |||
|
|
36 | /** Creates a redirect to the specified consumer */ | |||
| 32 | public static RedirectTo consumer(final StringConsumer consumer) { |
|
37 | public static RedirectTo consumer(final StringConsumer consumer) { | |
| 33 | return (src) -> CompletableFuture.runAsync(() -> { |
|
38 | return (src) -> CompletableFuture.runAsync(() -> { | |
| 34 | try (Scanner sc = new Scanner(src)) { |
|
39 | try (Scanner sc = new Scanner(src)) { | |
| 35 | while (sc.hasNextLine()) { |
|
40 | while (sc.hasNextLine()) { | |
| 36 | consumer.accept(sc.nextLine()); |
|
41 | consumer.accept(sc.nextLine()); | |
| 37 | } |
|
42 | } | |
| 38 | } |
|
43 | } | |
| 39 | }); |
|
44 | }); | |
| 40 | } |
|
45 | } | |
| 41 |
|
46 | |||
|
|
47 | /** | |||
|
|
48 | * Creates a redirect to the specified file. There will be opened the output | |||
|
|
49 | * stream in this redirection and original stream will be transferred to this | |||
|
|
50 | * this output stream. | |||
|
|
51 | */ | |||
| 42 | public static RedirectTo file(final File file) { |
|
52 | public static RedirectTo file(final File file) { | |
| 43 | return src -> CompletableFuture.runAsync(() -> { |
|
53 | return src -> CompletableFuture.runAsync(() -> { | |
| 44 | try (OutputStream out = new FileOutputStream(file)) { |
|
54 | try (OutputStream out = new FileOutputStream(file)) { | |
| 45 | src.transferTo(out); |
|
55 | src.transferTo(out); | |
| 46 | } catch (Exception e) { |
|
56 | } catch (Exception e) { | |
| 47 | // silence! |
|
57 | // silence! | |
| 48 | } |
|
58 | } | |
| 49 | }); |
|
59 | }); | |
| 50 | } |
|
60 | } | |
| 51 |
|
61 | |||
|
|
62 | /** Creates a redirect to the specified output stream. */ | |||
| 52 | public static RedirectTo stream(final OutputStream dest) { |
|
63 | public static RedirectTo stream(final OutputStream dest) { | |
| 53 | return src -> CompletableFuture.runAsync(() -> { |
|
64 | return src -> CompletableFuture.runAsync(() -> { | |
| 54 | try (dest; src) { |
|
65 | try (dest; src) { | |
| 55 | src.transferTo(dest); |
|
66 | src.transferTo(dest); | |
| 56 | } catch (Exception e) { |
|
67 | } catch (Exception e) { | |
| 57 | // silence! |
|
68 | // silence! | |
| 58 | } |
|
69 | } | |
| 59 | }); |
|
70 | }); | |
| 60 | } |
|
71 | } | |
| 61 |
|
72 | |||
|
|
73 | /** | |||
|
|
74 | * Creates the redirection to the specified destination, actual type of | |||
|
|
75 | * redirection will be determined from the type of the output object. | |||
|
|
76 | */ | |||
| 62 | public static RedirectTo any(final Object output) { |
|
77 | public static RedirectTo any(final Object output) { | |
| 63 | if (output instanceof StringConsumer fn) { |
|
78 | if (output instanceof StringConsumer fn) { | |
| 64 | return consumer(s -> fn.accept(s)); |
|
79 | return consumer(s -> fn.accept(s)); | |
| 65 | } else if (output instanceof File f) { |
|
80 | } else if (output instanceof File f) { | |
| 66 | return file(f); |
|
81 | return file(f); | |
| 67 | } else if (output instanceof OutputStream stm) { |
|
82 | } else if (output instanceof OutputStream stm) { | |
| 68 | return stream(stm); |
|
83 | return stream(stm); | |
| 69 | } else if (output instanceof RedirectTo self) { |
|
84 | } else if (output instanceof RedirectTo self) { | |
| 70 | return self; |
|
85 | return self; | |
| 71 | } else { |
|
86 | } else { | |
| 72 | throw new IllegalArgumentException("The specified argument type isn't supported: " + output.getClass()); |
|
87 | throw new IllegalArgumentException("The specified argument type isn't supported: " + output.getClass()); | |
| 73 | } |
|
88 | } | |
| 74 | } |
|
89 | } | |
| 75 | } No newline at end of file |
|
90 | } | |
| @@ -1,41 +1,45 | |||||
| 1 | package org.implab.gradle.common.exec; |
|
1 | package org.implab.gradle.common.exec; | |
| 2 |
|
2 | |||
| 3 | import java.io.IOException; |
|
3 | import java.io.IOException; | |
| 4 | import java.lang.ProcessBuilder.Redirect; |
|
4 | import java.lang.ProcessBuilder.Redirect; | |
| 5 | import java.util.ArrayList; |
|
5 | import java.util.ArrayList; | |
| 6 | import java.util.concurrent.CompletableFuture; |
|
6 | import java.util.concurrent.CompletableFuture; | |
| 7 |
|
7 | |||
| 8 | class SystemExecBuilder extends ExecBuilder { |
|
8 | class SystemExecBuilder extends ExecBuilder { | |
| 9 | @Override |
|
9 | @Override | |
| 10 | protected CompletableFuture<Integer> startInternal( |
|
10 | protected CompletableFuture<Integer> startInternal( | |
| 11 | CommandSpec command, |
|
11 | CommandSpec command, | |
| 12 | EnvironmentSpec environment, |
|
12 | EnvironmentSpec environment, | |
| 13 | PipeSpec redirect) throws IOException { |
|
13 | PipeSpec redirect) throws IOException { | |
| 14 |
|
14 | |||
| 15 | var builder = new ProcessBuilder(command.commandLine()); |
|
15 | var builder = new ProcessBuilder(command.commandLine()); | |
| 16 |
|
16 | |||
| 17 | environment.workingDirectory().ifPresent(builder::directory); |
|
17 | environment.workingDirectory().ifPresent(builder::directory); | |
| 18 |
|
18 | |||
|
|
19 | // if the env isn't inherited we need to clear it | |||
|
|
20 | if (!environment.inheritEnvironment()) | |||
|
|
21 | builder.environment().clear(); | |||
|
|
22 | ||||
| 19 | builder.environment().putAll(environment.environment()); |
|
23 | builder.environment().putAll(environment.environment()); | |
| 20 |
|
24 | |||
| 21 | var tasks = new ArrayList<CompletableFuture<?>>(); |
|
25 | var tasks = new ArrayList<CompletableFuture<?>>(); | |
| 22 |
|
26 | |||
| 23 | if (!redirect.stdout().isPresent()) |
|
27 | if (!redirect.stdout().isPresent()) | |
| 24 | builder.redirectOutput(Redirect.DISCARD); |
|
28 | builder.redirectOutput(Redirect.DISCARD); | |
| 25 | if (!redirect.stderr().isPresent()) |
|
29 | if (!redirect.stderr().isPresent()) | |
| 26 | builder.redirectError(Redirect.DISCARD); |
|
30 | builder.redirectError(Redirect.DISCARD); | |
| 27 |
|
31 | |||
| 28 | // run process |
|
32 | // run process | |
| 29 | var proc = builder.start(); |
|
33 | var proc = builder.start(); | |
| 30 |
|
34 | |||
| 31 | tasks.add(proc.onExit()); |
|
35 | tasks.add(proc.onExit()); | |
| 32 |
|
36 | |||
| 33 | redirect.stdin().map(from -> from.redirect(proc.getOutputStream())).ifPresent(tasks::add); |
|
37 | redirect.stdin().map(from -> from.redirect(proc.getOutputStream())).ifPresent(tasks::add); | |
| 34 | redirect.stdout().map(to -> to.redirect(proc.getInputStream())).ifPresent(tasks::add); |
|
38 | redirect.stdout().map(to -> to.redirect(proc.getInputStream())).ifPresent(tasks::add); | |
| 35 | redirect.stderr().map(to -> to.redirect(proc.getErrorStream())).ifPresent(tasks::add); |
|
39 | redirect.stderr().map(to -> to.redirect(proc.getErrorStream())).ifPresent(tasks::add); | |
| 36 |
|
40 | |||
| 37 | return CompletableFuture |
|
41 | return CompletableFuture | |
| 38 | .allOf(tasks.toArray(new CompletableFuture<?>[0])) |
|
42 | .allOf(tasks.toArray(new CompletableFuture<?>[0])) | |
| 39 | .thenApply(t -> proc.exitValue()); |
|
43 | .thenApply(t -> proc.exitValue()); | |
| 40 | } |
|
44 | } | |
| 41 | } |
|
45 | } | |
| @@ -1,110 +1,110 | |||||
| 1 | package org.implab.gradle.common.tasks; |
|
1 | package org.implab.gradle.common.tasks; | |
| 2 |
|
2 | |||
| 3 | import java.io.IOException; |
|
3 | import java.io.IOException; | |
| 4 | import java.util.Map; |
|
4 | import java.util.Map; | |
| 5 | import java.util.concurrent.ExecutionException; |
|
5 | import java.util.concurrent.ExecutionException; | |
| 6 | import java.util.stream.Stream; |
|
6 | import java.util.stream.Stream; | |
| 7 |
|
7 | |||
| 8 | import org.gradle.api.DefaultTask; |
|
8 | import org.gradle.api.DefaultTask; | |
| 9 | import org.gradle.api.file.DirectoryProperty; |
|
9 | import org.gradle.api.file.DirectoryProperty; | |
| 10 | import org.gradle.api.provider.ListProperty; |
|
10 | import org.gradle.api.provider.ListProperty; | |
| 11 | import org.gradle.api.provider.MapProperty; |
|
11 | import org.gradle.api.provider.MapProperty; | |
| 12 | import org.gradle.api.tasks.Internal; |
|
12 | import org.gradle.api.tasks.Internal; | |
| 13 | import org.gradle.api.tasks.TaskAction; |
|
13 | import org.gradle.api.tasks.TaskAction; | |
| 14 | import org.implab.gradle.common.dsl.CommandSpec; |
|
14 | import org.implab.gradle.common.dsl.CommandSpec; | |
| 15 | import org.implab.gradle.common.dsl.PipeSpec; |
|
15 | import org.implab.gradle.common.dsl.PipeSpec; | |
| 16 | import org.implab.gradle.common.dsl.RedirectFromSpec; |
|
16 | import org.implab.gradle.common.dsl.RedirectFromSpec; | |
| 17 | import org.implab.gradle.common.dsl.RedirectToSpec; |
|
17 | import org.implab.gradle.common.dsl.RedirectToSpec; | |
| 18 | import org.implab.gradle.common.dsl.EnvironmentSpec; |
|
18 | import org.implab.gradle.common.dsl.EnvironmentSpec; | |
| 19 | import org.implab.gradle.common.exec.ExecBuilder; |
|
19 | import org.implab.gradle.common.exec.ExecBuilder; | |
| 20 | import org.implab.gradle.common.utils.ObjectsMixin; |
|
20 | import org.implab.gradle.common.utils.ObjectsMixin; | |
| 21 | import org.implab.gradle.common.utils.Strings; |
|
21 | import org.implab.gradle.common.utils.Strings; | |
| 22 | import org.implab.gradle.common.utils.ThrowingConsumer; |
|
22 | import org.implab.gradle.common.utils.ThrowingConsumer; | |
| 23 |
|
23 | |||
| 24 | public abstract class ShellExecTask |
|
24 | public abstract class ShellExecTask | |
| 25 | extends DefaultTask |
|
25 | extends DefaultTask | |
| 26 | implements CommandSpec, PipeSpec, EnvironmentSpec, ObjectsMixin { |
|
26 | implements CommandSpec, PipeSpec, EnvironmentSpec, ObjectsMixin { | |
| 27 |
|
27 | |||
| 28 | private final RedirectToSpec redirectStderr = new RedirectToSpec(); |
|
28 | private final RedirectToSpec redirectStderr = new RedirectToSpec(); | |
| 29 |
|
29 | |||
| 30 | private final RedirectToSpec redirectStdout = new RedirectToSpec(); |
|
30 | private final RedirectToSpec redirectStdout = new RedirectToSpec(); | |
| 31 |
|
31 | |||
| 32 | private final RedirectFromSpec redirectStdin = new RedirectFromSpec(); |
|
32 | private final RedirectFromSpec redirectStdin = new RedirectFromSpec(); | |
| 33 |
|
33 | |||
| 34 | @Internal |
|
34 | @Internal | |
| 35 | @Override |
|
35 | @Override | |
| 36 | public abstract DirectoryProperty getWorkingDirectory(); |
|
36 | public abstract DirectoryProperty getWorkingDirectory(); | |
| 37 |
|
37 | |||
| 38 | @Internal |
|
38 | @Internal | |
| 39 | @Override |
|
39 | @Override | |
| 40 | public abstract MapProperty<String, String> getEnvironment(); |
|
40 | public abstract MapProperty<String, String> getEnvironment(); | |
| 41 |
|
41 | |||
| 42 | @Internal |
|
42 | @Internal | |
| 43 | @Override |
|
43 | @Override | |
| 44 | public abstract ListProperty<String> getCommandLine(); |
|
44 | public abstract ListProperty<String> getCommandLine(); | |
| 45 |
|
45 | |||
| 46 | /** |
|
46 | /** | |
| 47 | * STDIN redirection, if not specified, no input will be passed to the command |
|
47 | * STDIN redirection, if not specified, no input will be passed to the command | |
| 48 | */ |
|
48 | */ | |
| 49 | @Internal |
|
49 | @Internal | |
| 50 | @Override |
|
50 | @Override | |
| 51 | public RedirectFromSpec getStdin() { |
|
51 | public RedirectFromSpec getStdin() { | |
| 52 | return redirectStdin; |
|
52 | return redirectStdin; | |
| 53 | } |
|
53 | } | |
| 54 |
|
54 | |||
| 55 | /** |
|
55 | /** | |
| 56 | * STDOUT redirection, if not specified, redirected to logger::info |
|
56 | * STDOUT redirection, if not specified, redirected to logger::info | |
| 57 | */ |
|
57 | */ | |
| 58 | @Internal |
|
58 | @Internal | |
| 59 | @Override |
|
59 | @Override | |
| 60 | public RedirectToSpec getStdout() { |
|
60 | public RedirectToSpec getStdout() { | |
| 61 | return redirectStdout; |
|
61 | return redirectStdout; | |
| 62 | } |
|
62 | } | |
| 63 |
|
63 | |||
| 64 | /** |
|
64 | /** | |
| 65 | * STDERR redirection, if not specified, redirected to logger::error |
|
65 | * STDERR redirection, if not specified, redirected to logger::error | |
| 66 | */ |
|
66 | */ | |
| 67 | @Internal |
|
67 | @Internal | |
| 68 | @Override |
|
68 | @Override | |
| 69 | public RedirectToSpec getStderr() { |
|
69 | public RedirectToSpec getStderr() { | |
| 70 | return redirectStderr; |
|
70 | return redirectStderr; | |
| 71 | } |
|
71 | } | |
| 72 |
|
72 | |||
| 73 | @Override |
|
73 | @Override | |
| 74 | public void commandLine(Object arg0, Object... args) { |
|
74 | public void commandLine(Object arg0, Object... args) { | |
| 75 | getCommandLine().set(provider(() -> Stream.concat( |
|
75 | getCommandLine().set(provider(() -> Stream.concat( | |
| 76 | Stream.of(arg0), |
|
76 | Stream.of(arg0), | |
| 77 | Stream.of(args)) |
|
77 | Stream.of(args)) | |
| 78 | .map(Strings::asString).toList())); |
|
78 | .map(Strings::asString).toList())); | |
| 79 |
|
79 | |||
| 80 | } |
|
80 | } | |
| 81 |
|
81 | |||
| 82 | @Override |
|
82 | @Override | |
| 83 | public void args(Object arg0, Object... args) { |
|
83 | public void args(Object arg0, Object... args) { | |
| 84 | getCommandLine().addAll(provider(() -> Stream.concat( |
|
84 | getCommandLine().addAll(provider(() -> Stream.concat( | |
| 85 | Stream.of(arg0), |
|
85 | Stream.of(arg0), | |
| 86 | Stream.of(args)) |
|
86 | Stream.of(args)) | |
| 87 | .map(Strings::asString).toList())); |
|
87 | .map(Strings::asString).toList())); | |
| 88 | } |
|
88 | } | |
| 89 |
|
89 | |||
| 90 | protected abstract ExecBuilder execBuilder(); |
|
90 | protected abstract ExecBuilder execBuilder(); | |
| 91 |
|
91 | |||
| 92 | @TaskAction |
|
92 | @TaskAction | |
| 93 | public final void run() throws IOException, InterruptedException, ExecutionException { |
|
93 | public final void run() throws IOException, InterruptedException, ExecutionException { | |
| 94 | var execBuilder = execBuilder(); |
|
94 | var execBuilder = execBuilder(); | |
| 95 | execBuilder.workingDirectory(getWorkingDirectory().get().getAsFile()); |
|
95 | execBuilder.workingDirectory(getWorkingDirectory().get().getAsFile()); | |
| 96 | execBuilder.environment(getEnvironment().getOrElse(Map.of())); |
|
96 | execBuilder.environment(getEnvironment().getOrElse(Map.of())); | |
| 97 | execBuilder.commandLine(getCommandLine().get()); |
|
97 | execBuilder.commandLine(getCommandLine().get()); | |
| 98 |
|
98 | |||
| 99 | getStdout().getRedirection().ifPresent(execBuilder::stdout); |
|
99 | getStdout().getRedirection().ifPresent(execBuilder::stdout); | |
| 100 | getStderr().getRedirection().ifPresent(execBuilder::stderr); |
|
100 | getStderr().getRedirection().ifPresent(execBuilder::stderr); | |
| 101 | getStdin().getRedirection().ifPresent(execBuilder::stdin); |
|
101 | getStdin().getRedirection().ifPresent(execBuilder::stdin); | |
| 102 |
|
102 | |||
| 103 |
execBuilder. |
|
103 | execBuilder.exec().thenAccept(ThrowingConsumer.guard(this::checkRetCode)).join(); | |
| 104 | } |
|
104 | } | |
| 105 |
|
105 | |||
| 106 | protected void checkRetCode(Integer code) throws IOException { |
|
106 | protected void checkRetCode(Integer code) throws IOException { | |
| 107 | throw new IOException(String.format("The process is terminated with code %s", code)); |
|
107 | throw new IOException(String.format("The process is terminated with code %s", code)); | |
| 108 | } |
|
108 | } | |
| 109 |
|
109 | |||
| 110 | } |
|
110 | } | |
General Comments 0
You need to be logged in to leave comments.
Login now
