| @@ -1,27 +1,45 | |||
|
|
1 | 1 | package org.implab.gradle.common.dsl; |
|
|
2 | 2 | |
|
|
3 | import java.io.File; | |
|
|
4 | import java.io.InputStream; | |
|
|
3 | 5 | import java.util.Optional; |
|
|
4 | 6 | import java.util.function.Supplier; |
|
|
5 | 7 | |
|
|
6 | 8 | import org.gradle.api.provider.Provider; |
|
|
7 | 9 | import org.implab.gradle.common.exec.RedirectFrom; |
|
|
8 | 10 | |
|
|
9 | 11 | public class RedirectFromSpec { |
|
|
10 | 12 | private Supplier<RedirectFrom> streamRedirect; |
|
|
11 | ||
|
|
13 | ||
|
|
12 | 14 | public boolean isRedirected() { |
|
|
13 | 15 | return streamRedirect != null; |
|
|
14 | 16 | } |
|
|
15 | 17 | |
|
|
16 | 18 | public Optional<RedirectFrom> getRedirection() { |
|
|
17 | 19 | return streamRedirect != null ? Optional.ofNullable(streamRedirect.get()) : Optional.empty(); |
|
|
18 | 20 | } |
|
|
19 | 21 | |
|
|
22 | public void fromFile(File file) { | |
|
|
23 | this.streamRedirect = () -> RedirectFrom.file(file); | |
|
|
24 | } | |
|
|
25 | ||
|
|
26 | public void fromFile(Provider<File> fileProvider) { | |
|
|
27 | this.streamRedirect = fileProvider.map(RedirectFrom::file)::getOrNull; | |
|
|
28 | } | |
|
|
29 | ||
|
|
30 | public void fromStream(InputStream stream) { | |
|
|
31 | this.streamRedirect = () -> RedirectFrom.stream(stream); | |
|
|
32 | } | |
|
|
33 | ||
|
|
34 | public void fromStream(Provider<InputStream> streamProvider) { | |
|
|
35 | this.streamRedirect = streamProvider.map(RedirectFrom::stream)::getOrNull; | |
|
|
36 | } | |
|
|
37 | ||
|
|
20 | 38 | public void from(Object input) { |
|
|
21 | 39 | if (input instanceof Provider<?> inputProvider) { |
|
|
22 | 40 | this.streamRedirect = inputProvider.map(RedirectFrom::any)::get; |
|
|
23 | 41 | } else { |
|
|
24 | 42 | this.streamRedirect = () -> RedirectFrom.any(input); |
|
|
25 | 43 | } |
|
|
26 | 44 | } |
|
|
27 | 45 | } |
| @@ -1,62 +1,67 | |||
|
|
1 | 1 | package org.implab.gradle.common.exec; |
|
|
2 | 2 | |
|
|
3 | import java.util.stream.Stream; | |
|
|
4 | ||
|
|
5 | 3 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
6 | 4 | import org.implab.gradle.common.utils.Values; |
|
|
7 | 5 | |
|
|
8 | 6 | /** Command builder interface, used to specify the executable and parameters */ |
|
|
9 | 7 | @NonNullByDefault |
|
|
10 | 8 | public interface CommandBuilder { |
|
|
11 | 9 | |
|
|
12 | 10 | /** Sets the executable, the parameters are left intact. */ |
|
|
13 | 11 | CommandBuilder executable(String executable); |
|
|
14 | 12 | |
|
|
15 | /** Sets the specified executable and parameters, old executable and parameters | |
|
|
13 | /** | |
|
|
14 | * Sets the specified executable and parameters, old executable and parameters | |
|
|
16 | 15 | * are discarded. |
|
|
17 | 16 | */ |
|
|
18 | 17 | default CommandBuilder commandLine(String executable, String... args) { |
|
|
19 | 18 | return executable(executable) |
|
|
20 | .arguments(args); | |
|
|
19 | .arguments(args); | |
|
|
21 | 20 | } |
|
|
22 | 21 | |
|
|
23 | /** Sets the specified executable and parameters, old executable and parameters | |
|
|
22 | /** | |
|
|
23 | * Sets the specified executable and parameters, old executable and parameters | |
|
|
24 | 24 | * are discarded. |
|
|
25 | 25 | * |
|
|
26 |
* @param command The command line. Must contain at least one element |
|
|
|
26 | * @param command The command line. Must contain at least one element | |
|
|
27 | * (executable). | |
|
|
27 | 28 | * |
|
|
28 | 29 | */ |
|
|
29 | 30 | default CommandBuilder commandLine(Iterable<? extends String> command) { |
|
|
30 | 31 | var iterator = command.iterator(); |
|
|
31 | 32 | |
|
|
32 | 33 | // set executable |
|
|
33 | 34 | executable(Values.take(iterator).orElseThrow()); |
|
|
34 | 35 | // cleat arguments |
|
|
35 | 36 | arguments(); |
|
|
37 | ||
|
|
36 | 38 | // set new arguments |
|
|
37 | 39 | while (iterator.hasNext()) |
|
|
38 | 40 | addArguments(iterator.next()); |
|
|
39 | 41 | |
|
|
40 | 42 | return this; |
|
|
41 | 43 | } |
|
|
42 | 44 | |
|
|
43 | 45 | /** Adds the specified arguments to this builder */ |
|
|
44 |
|
|
|
|
45 | return arguments(() -> Stream | |
|
|
46 | .concat(Stream.of(arg0), Stream.of(args)) | |
|
|
47 | .iterator()); | |
|
|
46 | CommandBuilder addArguments(String arg0, String... args); | |
|
|
47 | ||
|
|
48 | default CommandBuilder addArguments(Iterable<String> args) { | |
|
|
49 | for (String arg : args) | |
|
|
50 | addArguments(arg); | |
|
|
51 | ||
|
|
52 | return this; | |
|
|
48 | 53 | } |
|
|
49 | 54 | |
|
|
50 | 55 | /** Replaces arguments in the builder with the specified one. */ |
|
|
51 | 56 | default CommandBuilder arguments(String... args) { |
|
|
52 | 57 | return arguments(Values.iterable(args)); |
|
|
53 | 58 | } |
|
|
54 | 59 | |
|
|
55 | 60 | /** Replaces arguments in the builder with the specified one. */ |
|
|
56 | 61 | CommandBuilder arguments(Iterable<String> args); |
|
|
57 | 62 | |
|
|
58 | 63 | default CommandBuilder from(CommandSpec commandSpec) { |
|
|
59 | 64 | return executable(commandSpec.executable()) |
|
|
60 | 65 | .arguments(commandSpec.arguments()); |
|
|
61 | 66 | } |
|
|
62 | 67 | } |
| @@ -1,42 +1,39 | |||
|
|
1 | 1 | package org.implab.gradle.common.exec; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.io.ByteArrayInputStream; |
|
|
4 | import java.io.File; | |
|
|
5 | 4 | import java.io.IOException; |
|
|
6 | 5 | import java.nio.charset.StandardCharsets; |
|
|
7 | import java.util.List; | |
|
|
8 | import java.util.Map; | |
|
|
9 | import java.util.Optional; | |
|
|
10 | 6 | import java.util.concurrent.CompletableFuture; |
|
|
7 | import java.util.stream.Collectors; | |
|
|
11 | 8 | |
|
|
12 | 9 | class EchoExecBuilder extends ExecBuilder { |
|
|
13 | 10 | |
|
|
14 | 11 | private final boolean echoToStderr; |
|
|
15 | 12 | |
|
|
16 | 13 | public EchoExecBuilder(boolean echoToStderr) { |
|
|
17 | 14 | this.echoToStderr = echoToStderr; |
|
|
18 | 15 | } |
|
|
19 | 16 | |
|
|
20 | 17 | @Override |
|
|
21 | 18 | protected CompletableFuture<Integer> startInternal( |
|
|
22 | Map<String, String> environment, | |
|
|
23 | Optional<File> directory, | |
|
|
24 | List<String> commandLine, | |
|
|
25 | Optional<RedirectFrom> stdinRedirect, | |
|
|
26 | Optional<RedirectTo> stdoutRedirect, | |
|
|
27 | Optional<RedirectTo> stderrRedirect) throws IOException { | |
|
|
19 | CommandSpec command, | |
|
|
20 | EnvironmentSpec environment, | |
|
|
21 | PipeSpec redirect) throws IOException { | |
|
|
28 | 22 | |
|
|
29 |
var outputRedirect = echoToStderr ? |
|
|
|
23 | var outputRedirect = echoToStderr ? redirect.stderr() : redirect.stdout(); | |
|
|
30 | 24 | |
|
|
31 | 25 | return outputRedirect |
|
|
32 | 26 | .map(to -> { |
|
|
33 |
var bytes = String |
|
|
|
27 | var bytes = String | |
|
|
28 | .format( | |
|
|
29 | "exec: %s", | |
|
|
30 | command.commandLine().stream().collect(Collectors.joining(" "))) | |
|
|
34 | 31 | .getBytes(StandardCharsets.UTF_8); |
|
|
35 | 32 | |
|
|
36 | 33 | return to.redirect(new ByteArrayInputStream(bytes)) |
|
|
37 | 34 | .thenApply((x) -> 0); |
|
|
38 | 35 | }) |
|
|
39 | 36 | .orElse(CompletableFuture.completedFuture(0)); |
|
|
40 | 37 | } |
|
|
41 | 38 | |
|
|
42 | 39 | } |
| @@ -1,25 +1,31 | |||
|
|
1 | 1 | package org.implab.gradle.common.exec; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.io.File; |
|
|
4 | 4 | import java.util.Map; |
|
|
5 | 5 | import java.util.Optional; |
|
|
6 | 6 | |
|
|
7 | 7 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
8 | 8 | |
|
|
9 | 9 | @NonNullByDefault |
|
|
10 | 10 | public interface EnvironmentBuilder { |
|
|
11 | /** Sets the specified environment variable */ | |
|
|
11 | 12 | EnvironmentBuilder setVariable(String envVar, String value); |
|
|
12 | 13 | |
|
|
14 | /** Replaces environment with the supplied one */ | |
|
|
13 | 15 | EnvironmentBuilder environment(Map<String, ? extends String> env); |
|
|
14 | 16 | |
|
|
17 | /** Unsets the specified variable. If the variable is inherited it removed anyway. */ | |
|
|
15 | 18 | EnvironmentBuilder unsetVariable(String envVar); |
|
|
16 | 19 | |
|
|
20 | /** Specifies the working directory */ | |
|
|
17 | 21 | EnvironmentBuilder workingDirectory(File directory); |
|
|
18 | 22 | |
|
|
23 | /** Specifies the working directory */ | |
|
|
19 | 24 | EnvironmentBuilder workingDirectory(Optional<? extends File> directory); |
|
|
20 | 25 | |
|
|
26 | /** Copies the supplied environment to this one */ | |
|
|
21 | 27 | default EnvironmentBuilder from(EnvironmentSpec environmentSpec) { |
|
|
22 | 28 | return environment(environmentSpec.environment()) |
|
|
23 | 29 | .workingDirectory(environmentSpec.workingDirectory()); |
|
|
24 | 30 | } |
|
|
25 | 31 | } |
| @@ -1,199 +1,265 | |||
|
|
1 | 1 | package org.implab.gradle.common.exec; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.ArrayList; |
|
|
4 | 4 | import java.util.HashMap; |
|
|
5 | 5 | import java.util.List; |
|
|
6 | 6 | import java.util.Map; |
|
|
7 | 7 | import java.util.Optional; |
|
|
8 | 8 | import java.util.concurrent.CompletableFuture; |
|
|
9 | 9 | |
|
|
10 | 10 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
11 | 11 | import org.eclipse.jdt.annotation.Nullable; |
|
|
12 | 12 | |
|
|
13 | 13 | import java.io.File; |
|
|
14 | 14 | import java.io.IOException; |
|
|
15 | 15 | |
|
|
16 | 16 | import static java.util.Objects.requireNonNull; |
|
|
17 | 17 | |
|
|
18 | 18 | /** Command line builder */ |
|
|
19 | 19 | @NonNullByDefault |
|
|
20 | 20 | public abstract class ExecBuilder implements |
|
|
21 | 21 | CommandBuilder, |
|
|
22 | 22 | PipeBuilder, |
|
|
23 | 23 | EnvironmentBuilder, |
|
|
24 | 24 | Runnable |
|
|
25 | 25 | { |
|
|
26 | 26 | private String executable; |
|
|
27 | 27 | |
|
|
28 | 28 | private final List<String> arguments = new ArrayList<>(); |
|
|
29 | 29 | |
|
|
30 | 30 | private final Map<String, String> environment = new HashMap<>(); |
|
|
31 | 31 | |
|
|
32 | 32 | private @Nullable File directory; |
|
|
33 | 33 | |
|
|
34 | 34 | private RedirectFrom inputRedirect; |
|
|
35 | 35 | |
|
|
36 | 36 | private RedirectTo outputRedirect; |
|
|
37 | 37 | |
|
|
38 | 38 | private RedirectTo errorRedirect; |
|
|
39 | 39 | |
|
|
40 | 40 | @Override |
|
|
41 | 41 | public ExecBuilder executable(String executable) { |
|
|
42 | 42 | requireNonNull(executable, "cmd can't be null"); |
|
|
43 | 43 | this.executable = executable; |
|
|
44 | 44 | return this; |
|
|
45 | 45 | } |
|
|
46 | 46 | |
|
|
47 | 47 | @Override |
|
|
48 | 48 | public ExecBuilder arguments(Iterable<String> args) { |
|
|
49 | 49 | requireNonNull(args, "Args must not be null"); |
|
|
50 | 50 | arguments.clear(); |
|
|
51 | 51 | for(var arg: args) |
|
|
52 | 52 | arguments.add(arg); |
|
|
53 | 53 | return this; |
|
|
54 | 54 | } |
|
|
55 | 55 | |
|
|
56 | 56 | @Override |
|
|
57 | 57 | public ExecBuilder addArguments(String arg0, String... args) { |
|
|
58 | 58 | requireNonNull(arg0, "arg0 parameter can't be null"); |
|
|
59 | 59 | arguments.add(arg0); |
|
|
60 | 60 | |
|
|
61 | 61 | for (var arg : args) |
|
|
62 | 62 | arguments.add(arg); |
|
|
63 | 63 | |
|
|
64 | 64 | return this; |
|
|
65 | 65 | } |
|
|
66 | 66 | |
|
|
67 | 67 | /** Sets the working directory */ |
|
|
68 | 68 | @Override |
|
|
69 | 69 | public ExecBuilder workingDirectory(File directory) { |
|
|
70 | 70 | requireNonNull(directory, "directory parameter can't be null"); |
|
|
71 | 71 | |
|
|
72 | 72 | this.directory = directory; |
|
|
73 | 73 | return this; |
|
|
74 | 74 | } |
|
|
75 | 75 | |
|
|
76 | 76 | @Override |
|
|
77 | 77 | public ExecBuilder workingDirectory(Optional<? extends File> directory) { |
|
|
78 | 78 | requireNonNull(directory, "directory parameter can't be null"); |
|
|
79 | 79 | |
|
|
80 | 80 | this.directory = directory.orElse(null); |
|
|
81 | 81 | return this; |
|
|
82 | 82 | } |
|
|
83 | 83 | |
|
|
84 | 84 | /** |
|
|
85 | 85 | * Sets the environment value. The value cannot be null. |
|
|
86 | 86 | * |
|
|
87 | 87 | * @param envVar The name of the environment variable |
|
|
88 | 88 | * @param value The value to set, |
|
|
89 | 89 | */ |
|
|
90 | 90 | @Override |
|
|
91 | 91 | public ExecBuilder setVariable(String envVar, String value) { |
|
|
92 | 92 | requireNonNull(value, "Value can't be null"); |
|
|
93 | 93 | requireNonNull(envVar, "envVar parameter can't be null"); |
|
|
94 | 94 | |
|
|
95 | 95 | environment.put(envVar, value); |
|
|
96 | 96 | return this; |
|
|
97 | 97 | } |
|
|
98 | 98 | |
|
|
99 | 99 | @Override |
|
|
100 | public ExecBuilder unsetVariable(String envVar) { | |
|
|
101 | requireNonNull(envVar, "envVar parameter can't be null"); | |
|
|
102 | ||
|
|
103 | environment.remove(envVar); | |
|
|
104 | return this; | |
|
|
105 | } | |
|
|
106 | ||
|
|
107 | @Override | |
|
|
100 | 108 | public ExecBuilder environment(Map<String,? extends String> env) { |
|
|
101 | 109 | requireNonNull(env, "env parameter can't be null"); |
|
|
102 | 110 | |
|
|
103 | 111 | environment.clear(); |
|
|
104 | 112 | environment.putAll(env); |
|
|
105 | 113 | return this; |
|
|
106 | 114 | } |
|
|
107 | 115 | |
|
|
108 | 116 | /** |
|
|
109 | 117 | * Sets redirection for the stdin, {@link RedirectFrom} will be applied |
|
|
110 | 118 | * every time the process is started. |
|
|
111 | 119 | * |
|
|
112 | 120 | * <p> |
|
|
113 | 121 | * If redirection |
|
|
114 | 122 | */ |
|
|
115 | 123 | @Override |
|
|
116 | 124 | public ExecBuilder stdin(RedirectFrom from) { |
|
|
117 | 125 | requireNonNull(from, "from parameter can't be null"); |
|
|
118 | 126 | |
|
|
119 | 127 | inputRedirect = from; |
|
|
120 | 128 | return this; |
|
|
121 | 129 | } |
|
|
122 | 130 | |
|
|
123 | 131 | @Override |
|
|
124 |
public |
|
|
|
132 | public ExecBuilder stdin(Optional<? extends RedirectFrom> from) { | |
|
|
125 | 133 | requireNonNull(from, "from parameter can't be null"); |
|
|
126 | 134 | inputRedirect = from.orElse(null); |
|
|
127 | 135 | return this; |
|
|
128 | 136 | } |
|
|
129 | 137 | |
|
|
130 | 138 | /** |
|
|
131 | 139 | * Sets redirection for the stdout, {@link RedirectTo} will be applied |
|
|
132 | 140 | * every time the process is started. |
|
|
133 | 141 | */ |
|
|
134 | 142 | @Override |
|
|
135 | 143 | public ExecBuilder stdout(RedirectTo out) { |
|
|
136 | 144 | requireNonNull(out, "out parameter can't be null"); |
|
|
137 | 145 | outputRedirect = out; |
|
|
138 | 146 | return this; |
|
|
139 | 147 | } |
|
|
140 | 148 | |
|
|
141 | 149 | @Override |
|
|
142 |
public |
|
|
|
150 | public ExecBuilder stdout(Optional<? extends RedirectTo> to) { | |
|
|
143 | 151 | requireNonNull(to, "from parameter can't be null"); |
|
|
144 | 152 | outputRedirect = to.orElse(null); |
|
|
145 | 153 | return this; |
|
|
146 | 154 | } |
|
|
147 | 155 | |
|
|
148 | 156 | /** |
|
|
149 | 157 | * Sets redirection for the stderr, {@link RedirectTo} will be applied |
|
|
150 | 158 | * every time the process is started. |
|
|
151 | 159 | */ |
|
|
152 | 160 | @Override |
|
|
153 | 161 | public ExecBuilder stderr(RedirectTo err) { |
|
|
154 | 162 | requireNonNull(err, "err parameter can't be null"); |
|
|
155 | 163 | errorRedirect = err; |
|
|
156 | 164 | return this; |
|
|
157 | 165 | } |
|
|
158 | 166 | |
|
|
159 | 167 | @Override |
|
|
160 |
public |
|
|
|
168 | public ExecBuilder stderr(Optional<? extends RedirectTo> to) { | |
|
|
161 | 169 | requireNonNull(to, "from parameter can't be null"); |
|
|
162 | 170 | errorRedirect = to.orElse(null); |
|
|
163 | 171 | return this; |
|
|
164 | 172 | } |
|
|
165 | 173 | |
|
|
174 | @Override | |
|
|
175 | public ExecBuilder from(CommandSpec commandSpec) { | |
|
|
176 | CommandBuilder.super.from(commandSpec); | |
|
|
177 | return this; | |
|
|
178 | } | |
|
|
179 | ||
|
|
180 | @Override | |
|
|
181 | public ExecBuilder from(PipeSpec pipeSpec) { | |
|
|
182 | PipeBuilder.super.from(pipeSpec); | |
|
|
183 | return this; | |
|
|
184 | } | |
|
|
185 | ||
|
|
186 | @Override | |
|
|
187 | public ExecBuilder from(EnvironmentSpec environmentSpec) { | |
|
|
188 | EnvironmentBuilder.super.from(environmentSpec); | |
|
|
189 | return this; | |
|
|
190 | } | |
|
|
191 | ||
|
|
166 | 192 | /** Implement this function to */ |
|
|
167 | 193 | protected abstract CompletableFuture<Integer> startInternal( |
|
|
168 | Map<String, String> environment, | |
|
|
169 | Optional<File> directory, | |
|
|
170 | List<String> commandLine, | |
|
|
171 | Optional<RedirectFrom> stdinRedirect, | |
|
|
172 | Optional<RedirectTo> stdoutRedirect, | |
|
|
173 | Optional<RedirectTo> stderrRedirect) throws IOException; | |
|
|
194 | CommandSpec command, | |
|
|
195 | EnvironmentSpec environment, | |
|
|
196 | PipeSpec redirect) throws IOException; | |
|
|
174 | 197 | |
|
|
175 | 198 | /** |
|
|
176 | 199 | * Creates and starts new process and returns {@link CompletableFuture}. The |
|
|
177 | 200 | * process may be a remote process, or a shell process or etc. |
|
|
178 | 201 | * |
|
|
179 | 202 | * @return |
|
|
180 | 203 | * @throws IOException |
|
|
181 | 204 | */ |
|
|
182 | 205 | public CompletableFuture<Integer> run() throws IOException { |
|
|
183 | 206 | if (executable == null || executable.isEmpty()) |
|
|
184 | 207 | throw new IllegalStateException("The executable isn't set"); |
|
|
185 | 208 | |
|
|
186 | 209 | var commandLine = new ArrayList<String>(); |
|
|
187 | 210 | commandLine.add(executable); |
|
|
188 | 211 | commandLine.addAll(arguments); |
|
|
189 | 212 | |
|
|
190 | 213 | return startInternal( |
|
|
191 | environment, | |
|
|
192 | Optional.ofNullable(directory), | |
|
|
193 | commandLine, | |
|
|
194 | Optional.ofNullable(inputRedirect), | |
|
|
195 | Optional.ofNullable(outputRedirect), | |
|
|
196 | Optional.ofNullable(errorRedirect)); | |
|
|
214 | new SelfCommandSpec(), | |
|
|
215 | new SelfEnvironmentSpec(), | |
|
|
216 | new SelfPipeSpec() | |
|
|
217 | ); | |
|
|
218 | } | |
|
|
219 | ||
|
|
220 | private class SelfCommandSpec implements CommandSpec { | |
|
|
221 | ||
|
|
222 | @Override | |
|
|
223 | public String executable() { | |
|
|
224 | return executable; | |
|
|
225 | } | |
|
|
226 | ||
|
|
227 | @Override | |
|
|
228 | public List<String> arguments() { | |
|
|
229 | return arguments; | |
|
|
230 | } | |
|
|
231 | } | |
|
|
232 | ||
|
|
233 | private class SelfEnvironmentSpec implements EnvironmentSpec { | |
|
|
234 | ||
|
|
235 | @Override | |
|
|
236 | public Map<String, String> environment() { | |
|
|
237 | return environment; | |
|
|
238 | } | |
|
|
239 | ||
|
|
240 | @Override | |
|
|
241 | public Optional<File> workingDirectory() { | |
|
|
242 | return Optional.ofNullable( directory); | |
|
|
243 | } | |
|
|
244 | } | |
|
|
245 | ||
|
|
246 | private class SelfPipeSpec implements PipeSpec { | |
|
|
247 | ||
|
|
248 | @Override | |
|
|
249 | public Optional<RedirectTo> stdout() { | |
|
|
250 | return Optional.ofNullable(outputRedirect); | |
|
|
251 | } | |
|
|
252 | ||
|
|
253 | @Override | |
|
|
254 | public Optional<RedirectTo> stderr() { | |
|
|
255 | return Optional.ofNullable(errorRedirect); | |
|
|
256 | } | |
|
|
257 | ||
|
|
258 | @Override | |
|
|
259 | public Optional<RedirectFrom> stdin() { | |
|
|
260 | return Optional.ofNullable(inputRedirect); | |
|
|
261 | } | |
|
|
262 | ||
|
|
197 | 263 | } |
|
|
198 | 264 | |
|
|
199 | 265 | } |
| @@ -1,58 +1,59 | |||
|
|
1 | 1 | package org.implab.gradle.common.exec; |
|
|
2 | 2 | |
|
|
3 | import java.io.File; | |
|
|
4 | 3 | import java.io.IOException; |
|
|
5 | 4 | import java.util.List; |
|
|
6 | import java.util.Map; | |
|
|
7 | import java.util.Optional; | |
|
|
8 | 5 | import java.util.concurrent.CompletableFuture; |
|
|
9 | 6 | import java.util.function.Consumer; |
|
|
10 | 7 | |
|
|
11 | 8 | /** Shell used to start processes */ |
|
|
12 | 9 | public interface ExecShell { |
|
|
13 | 10 | |
|
|
14 | 11 | /** Creates new process builder {@link ExecBuilder} */ |
|
|
15 | 12 | ExecBuilder executable(); |
|
|
16 | 13 | |
|
|
17 | 14 | /** |
|
|
18 | 15 | * Creates default process execution "shell" which will execute processes |
|
|
19 | 16 | * directly. |
|
|
20 | 17 | */ |
|
|
21 | 18 | static ExecShell system() { |
|
|
22 | 19 | return () -> new SystemExecBuilder(); |
|
|
23 | 20 | } |
|
|
24 | 21 | |
|
|
25 | 22 | /** |
|
|
26 | 23 | * Creates a stub shell which will print command to STDERR and return 0 (no |
|
|
27 | 24 | * error). |
|
|
28 | 25 | */ |
|
|
29 | 26 | static ExecShell echoStderr() { |
|
|
30 | 27 | return () -> new EchoExecBuilder(true); |
|
|
31 | 28 | } |
|
|
32 | 29 | |
|
|
33 | 30 | /** |
|
|
34 | 31 | * Creates a stub shell which will print command to STDOUT and return 0 (no |
|
|
35 | 32 | * error). |
|
|
36 | 33 | */ |
|
|
37 | 34 | |
|
|
38 | 35 | static ExecShell echo() { |
|
|
39 | 36 | return () -> new EchoExecBuilder(false); |
|
|
40 | 37 | } |
|
|
41 | 38 | |
|
|
42 | /** Creates a stub shell which will call the specified consumer and will supply a command line to it. | |
|
|
39 | /** | |
|
|
40 | * Creates a stub shell which will call the specified consumer and will supply a | |
|
|
41 | * command line to it. | |
|
|
43 | 42 | * The command line will be passed as a list of strings. |
|
|
44 | * @param consumer The consumer fot the command line | |
|
|
43 | * | |
|
|
44 | * @param consumer The consumer fot the command line | |
|
|
45 | 45 | * @return |
|
|
46 | 46 | */ |
|
|
47 | 47 | static ExecShell echo(Consumer<List<String>> consumer) { |
|
|
48 | 48 | return () -> new ExecBuilder() { |
|
|
49 | 49 | @Override |
|
|
50 |
protected CompletableFuture<Integer> startInternal( |
|
|
|
51 | List<String> commandLine, Optional<RedirectFrom> stdinRedirect, Optional<RedirectTo> stdoutRedirect, | |
|
|
52 | Optional<RedirectTo> stderrRedirect) throws IOException { | |
|
|
53 | consumer.accept(commandLine); | |
|
|
50 | protected CompletableFuture<Integer> startInternal( | |
|
|
51 | CommandSpec command, | |
|
|
52 | EnvironmentSpec environment, | |
|
|
53 | PipeSpec redirect) throws IOException { | |
|
|
54 | consumer.accept(command.commandLine()); | |
|
|
54 | 55 | return CompletableFuture.completedFuture(0); |
|
|
55 | 56 | } |
|
|
56 | 57 | }; |
|
|
57 | 58 | } |
|
|
58 | 59 | } |
| @@ -1,49 +1,41 | |||
|
|
1 | 1 | package org.implab.gradle.common.exec; |
|
|
2 | 2 | |
|
|
3 | import java.io.File; | |
|
|
4 | 3 | import java.io.IOException; |
|
|
5 | 4 | import java.lang.ProcessBuilder.Redirect; |
|
|
6 | 5 | import java.util.ArrayList; |
|
|
7 | import java.util.List; | |
|
|
8 | import java.util.Map; | |
|
|
9 | import java.util.Optional; | |
|
|
10 | 6 | import java.util.concurrent.CompletableFuture; |
|
|
11 | 7 | |
|
|
12 | 8 | class SystemExecBuilder extends ExecBuilder { |
|
|
13 | 9 | @Override |
|
|
14 | 10 | protected CompletableFuture<Integer> startInternal( |
|
|
15 | Map<String, String> environment, | |
|
|
16 | Optional<File> directory, | |
|
|
17 | List<String> commandLine, | |
|
|
18 | Optional<RedirectFrom> stdinRedirect, | |
|
|
19 | Optional<RedirectTo> stdoutRedirect, | |
|
|
20 | Optional<RedirectTo> stderrRedirect) throws IOException { | |
|
|
11 | CommandSpec command, | |
|
|
12 | EnvironmentSpec environment, | |
|
|
13 | PipeSpec redirect) throws IOException { | |
|
|
21 | 14 | |
|
|
22 | var builder = new ProcessBuilder(commandLine); | |
|
|
23 | ||
|
|
24 | directory.ifPresent(builder::directory); | |
|
|
15 | var builder = new ProcessBuilder(command.commandLine()); | |
|
|
25 | 16 | |
|
|
26 | builder.environment().putAll(environment); | |
|
|
17 | environment.workingDirectory().ifPresent(builder::directory); | |
|
|
27 | 18 | |
|
|
28 | // TODO Auto-generated method stub | |
|
|
19 | builder.environment().putAll(environment.environment()); | |
|
|
20 | ||
|
|
29 | 21 | var tasks = new ArrayList<CompletableFuture<?>>(); |
|
|
30 | 22 | |
|
|
31 |
if (! |
|
|
|
23 | if (!redirect.stdout().isPresent()) | |
|
|
32 | 24 | builder.redirectOutput(Redirect.DISCARD); |
|
|
33 |
if (! |
|
|
|
25 | if (!redirect.stderr().isPresent()) | |
|
|
34 | 26 | builder.redirectError(Redirect.DISCARD); |
|
|
35 | 27 | |
|
|
36 | 28 | // run process |
|
|
37 | 29 | var proc = builder.start(); |
|
|
38 | 30 | |
|
|
39 | 31 | tasks.add(proc.onExit()); |
|
|
40 | 32 | |
|
|
41 |
|
|
|
|
42 |
|
|
|
|
43 |
|
|
|
|
33 | redirect.stdin().map(from -> from.redirect(proc.getOutputStream())).ifPresent(tasks::add); | |
|
|
34 | redirect.stdout().map(to -> to.redirect(proc.getInputStream())).ifPresent(tasks::add); | |
|
|
35 | redirect.stderr().map(to -> to.redirect(proc.getErrorStream())).ifPresent(tasks::add); | |
|
|
44 | 36 | |
|
|
45 | 37 | return CompletableFuture |
|
|
46 | 38 | .allOf(tasks.toArray(new CompletableFuture<?>[0])) |
|
|
47 | 39 | .thenApply(t -> proc.exitValue()); |
|
|
48 | 40 | } |
|
|
49 | 41 | } |
| @@ -1,110 +1,110 | |||
|
|
1 | 1 | package org.implab.gradle.common.tasks; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.io.IOException; |
|
|
4 | 4 | import java.util.Map; |
|
|
5 | 5 | import java.util.concurrent.ExecutionException; |
|
|
6 | 6 | import java.util.stream.Stream; |
|
|
7 | 7 | |
|
|
8 | 8 | import org.gradle.api.DefaultTask; |
|
|
9 | 9 | import org.gradle.api.file.DirectoryProperty; |
|
|
10 | 10 | import org.gradle.api.provider.ListProperty; |
|
|
11 | 11 | import org.gradle.api.provider.MapProperty; |
|
|
12 | 12 | import org.gradle.api.tasks.Internal; |
|
|
13 | 13 | import org.gradle.api.tasks.TaskAction; |
|
|
14 | 14 | import org.implab.gradle.common.dsl.CommandSpec; |
|
|
15 | 15 | import org.implab.gradle.common.dsl.PipeSpec; |
|
|
16 | 16 | import org.implab.gradle.common.dsl.RedirectFromSpec; |
|
|
17 | 17 | import org.implab.gradle.common.dsl.RedirectToSpec; |
|
|
18 | 18 | import org.implab.gradle.common.dsl.EnvironmentSpec; |
|
|
19 | 19 | import org.implab.gradle.common.exec.ExecBuilder; |
|
|
20 | 20 | import org.implab.gradle.common.utils.ObjectsMixin; |
|
|
21 | 21 | import org.implab.gradle.common.utils.Strings; |
|
|
22 | 22 | import org.implab.gradle.common.utils.ThrowingConsumer; |
|
|
23 | 23 | |
|
|
24 | 24 | public abstract class ShellExecTask |
|
|
25 | 25 | extends DefaultTask |
|
|
26 | 26 | implements CommandSpec, PipeSpec, EnvironmentSpec, ObjectsMixin { |
|
|
27 | 27 | |
|
|
28 | 28 | private final RedirectToSpec redirectStderr = new RedirectToSpec(); |
|
|
29 | 29 | |
|
|
30 | 30 | private final RedirectToSpec redirectStdout = new RedirectToSpec(); |
|
|
31 | 31 | |
|
|
32 | 32 | private final RedirectFromSpec redirectStdin = new RedirectFromSpec(); |
|
|
33 | 33 | |
|
|
34 | 34 | @Internal |
|
|
35 | 35 | @Override |
|
|
36 | 36 | public abstract DirectoryProperty getWorkingDirectory(); |
|
|
37 | 37 | |
|
|
38 | 38 | @Internal |
|
|
39 | 39 | @Override |
|
|
40 | 40 | public abstract MapProperty<String, String> getEnvironment(); |
|
|
41 | 41 | |
|
|
42 | 42 | @Internal |
|
|
43 | 43 | @Override |
|
|
44 | 44 | public abstract ListProperty<String> getCommandLine(); |
|
|
45 | 45 | |
|
|
46 | 46 | /** |
|
|
47 | 47 | * STDIN redirection, if not specified, no input will be passed to the command |
|
|
48 | 48 | */ |
|
|
49 | 49 | @Internal |
|
|
50 | 50 | @Override |
|
|
51 | 51 | public RedirectFromSpec getStdin() { |
|
|
52 | 52 | return redirectStdin; |
|
|
53 | 53 | } |
|
|
54 | 54 | |
|
|
55 | 55 | /** |
|
|
56 | 56 | * STDOUT redirection, if not specified, redirected to logger::info |
|
|
57 | 57 | */ |
|
|
58 | 58 | @Internal |
|
|
59 | 59 | @Override |
|
|
60 | 60 | public RedirectToSpec getStdout() { |
|
|
61 | 61 | return redirectStdout; |
|
|
62 | 62 | } |
|
|
63 | 63 | |
|
|
64 | 64 | /** |
|
|
65 | 65 | * STDERR redirection, if not specified, redirected to logger::error |
|
|
66 | 66 | */ |
|
|
67 | 67 | @Internal |
|
|
68 | 68 | @Override |
|
|
69 | 69 | public RedirectToSpec getStderr() { |
|
|
70 | 70 | return redirectStderr; |
|
|
71 | 71 | } |
|
|
72 | 72 | |
|
|
73 | 73 | @Override |
|
|
74 | 74 | public void commandLine(Object arg0, Object... args) { |
|
|
75 | 75 | getCommandLine().set(provider(() -> Stream.concat( |
|
|
76 | 76 | Stream.of(arg0), |
|
|
77 | 77 | Stream.of(args)) |
|
|
78 | 78 | .map(Strings::asString).toList())); |
|
|
79 | 79 | |
|
|
80 | 80 | } |
|
|
81 | 81 | |
|
|
82 | 82 | @Override |
|
|
83 | 83 | public void args(Object arg0, Object... args) { |
|
|
84 | 84 | getCommandLine().addAll(provider(() -> Stream.concat( |
|
|
85 | 85 | Stream.of(arg0), |
|
|
86 | 86 | Stream.of(args)) |
|
|
87 | 87 | .map(Strings::asString).toList())); |
|
|
88 | 88 | } |
|
|
89 | 89 | |
|
|
90 | 90 | protected abstract ExecBuilder execBuilder(); |
|
|
91 | 91 | |
|
|
92 | 92 | @TaskAction |
|
|
93 | 93 | public final void run() throws IOException, InterruptedException, ExecutionException { |
|
|
94 | var execBuilder = execBuilder() | |
|
|
95 |
|
|
|
|
96 |
|
|
|
|
97 |
|
|
|
|
94 | var execBuilder = execBuilder(); | |
|
|
95 | execBuilder.workingDirectory(getWorkingDirectory().get().getAsFile()); | |
|
|
96 | execBuilder.environment(getEnvironment().getOrElse(Map.of())); | |
|
|
97 | execBuilder.commandLine(getCommandLine().get()); | |
|
|
98 | 98 | |
|
|
99 | 99 | getStdout().getRedirection().ifPresent(execBuilder::stdout); |
|
|
100 | 100 | getStderr().getRedirection().ifPresent(execBuilder::stderr); |
|
|
101 | 101 | getStdin().getRedirection().ifPresent(execBuilder::stdin); |
|
|
102 | 102 | |
|
|
103 | 103 | execBuilder.run().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 | 107 | throw new IOException(String.format("The process is terminated with code %s", code)); |
|
|
108 | 108 | } |
|
|
109 | 109 | |
|
|
110 | 110 | } |
| @@ -1,88 +1,89 | |||
|
|
1 | 1 | package org.implab.gradle.common.utils; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.Iterator; |
|
|
4 | 4 | import java.util.Map; |
|
|
5 | import java.util.Spliterator; | |
|
|
6 | 5 | import java.util.Spliterators; |
|
|
7 | 6 | import java.util.Map.Entry; |
|
|
8 | 7 | import java.util.Optional; |
|
|
9 | 8 | import java.util.function.Function; |
|
|
10 | 9 | import java.util.function.Supplier; |
|
|
11 | 10 | import java.util.stream.Collectors; |
|
|
12 | 11 | import java.util.stream.Stream; |
|
|
13 | 12 | import java.util.stream.StreamSupport; |
|
|
14 | 13 | |
|
|
15 | 14 | import org.gradle.api.provider.Provider; |
|
|
16 | 15 | |
|
|
17 | 16 | public final class Values { |
|
|
18 | 17 | |
|
|
19 | 18 | private Values() { |
|
|
20 | 19 | } |
|
|
21 | 20 | |
|
|
22 | 21 | /** |
|
|
23 | 22 | * Converts values in the specified map |
|
|
23 | * | |
|
|
24 | 24 | * @param <K> |
|
|
25 | 25 | * @param <V> |
|
|
26 | 26 | * @param <U> |
|
|
27 | 27 | * @param map |
|
|
28 | 28 | * @param mapper |
|
|
29 | 29 | * @return |
|
|
30 | 30 | */ |
|
|
31 | 31 | public static <K, V, U> Map<K, U> mapValues(Map<K, V> map, Function<V, U> mapper) { |
|
|
32 | 32 | Function<Entry<K, V>, V> getter = Entry::getValue; |
|
|
33 | 33 | |
|
|
34 | 34 | return map.entrySet().stream() |
|
|
35 | 35 | .collect(Collectors.toMap(Entry::getKey, getter.andThen(mapper))); |
|
|
36 | 36 | } |
|
|
37 | 37 | |
|
|
38 | /** Converts the supplied value to a string. | |
|
|
38 | /** | |
|
|
39 | * Converts the supplied value to a string. | |
|
|
39 | 40 | */ |
|
|
40 | 41 | public static String toString(Object value) { |
|
|
41 | 42 | if (value == null) { |
|
|
42 |
return null; |
|
|
|
43 |
|
|
|
|
43 | return null; | |
|
|
44 | } else if (value instanceof String string) { | |
|
|
44 | 45 | return string; |
|
|
45 | 46 | } else if (value instanceof Provider<?> provider) { |
|
|
46 | 47 | return toString(provider.getOrNull()); |
|
|
47 | 48 | } else if (value instanceof Supplier<?> supplier) { |
|
|
48 | 49 | return toString(supplier.get()); |
|
|
49 | 50 | } else { |
|
|
50 | 51 | return value.toString(); |
|
|
51 | 52 | } |
|
|
52 | 53 | } |
|
|
53 | 54 | |
|
|
54 | 55 | public static <T> Stream<T> stream(Iterator<T> remaining) { |
|
|
55 | var spliterator = Spliterators.spliteratorUnknownSize(remaining, 0); | |
|
|
56 | ||
|
|
57 | return StreamSupport.stream(spliterator, false); | |
|
|
56 | return StreamSupport.stream( | |
|
|
57 | Spliterators.spliteratorUnknownSize(remaining, 0), | |
|
|
58 | false); | |
|
|
58 | 59 | } |
|
|
59 | 60 | |
|
|
60 | 61 | public static <T> Optional<T> take(Iterator<T> iterator) { |
|
|
61 | 62 | return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.empty(); |
|
|
62 | 63 | } |
|
|
63 | 64 | |
|
|
64 | 65 | public static <T> Iterable<T> iterable(T[] values) { |
|
|
65 | 66 | return () -> new ArrayIterator<>(values); |
|
|
66 | 67 | } |
|
|
67 | 68 | |
|
|
68 | 69 | private static class ArrayIterator<T> implements Iterator<T> { |
|
|
69 | 70 | private final T[] data; |
|
|
70 | 71 | |
|
|
71 | 72 | private int pos = 0; |
|
|
72 | 73 | |
|
|
73 | 74 | ArrayIterator(T[] data) { |
|
|
74 | 75 | this.data = data; |
|
|
75 | 76 | } |
|
|
76 | 77 | |
|
|
77 | 78 | @Override |
|
|
78 | 79 | public boolean hasNext() { |
|
|
79 | 80 | return pos < data.length; |
|
|
80 | 81 | } |
|
|
81 | 82 | |
|
|
82 | 83 | @Override |
|
|
83 | 84 | public T next() { |
|
|
84 | 85 | return data[pos++]; |
|
|
85 | 86 | } |
|
|
86 | 87 | } |
|
|
87 | 88 | |
|
|
88 | 89 | } |
General Comments 0
You need to be logged in to leave comments.
Login now
