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