##// END OF EJS Templates
WIP exec builder
cin -
r11:1ba8ae1fcc76 default
parent child
Show More
@@ -0,0 +1,13
1 package org.implab.gradle.common.dsl;
2
3
4 import org.gradle.api.provider.ListProperty;
5
6 public interface CommandSpec {
7 ListProperty<String> getCommandLine();
8
9 void commandLine(Object arg0, Object... args);
10
11 void args(Object arg0, Object... args);
12
13 }
@@ -0,0 +1,48
1 package org.implab.gradle.common.dsl;
2
3 import java.util.HashMap;
4 import java.util.Map;
5
6 import org.gradle.api.Action;
7 import org.gradle.api.file.DirectoryProperty;
8 import org.gradle.api.provider.MapProperty;
9 import org.implab.gradle.common.utils.Closures;
10 import org.implab.gradle.common.utils.Values;
11
12 import groovy.lang.Closure;
13
14 /**
15 * Configuration properties of the execution shell. This object specifies a
16 * working directory and environment variables for the processes started within
17 * this shell.
18 */
19 public interface EnvironmentSpec {
20
21 /** Working directory */
22 DirectoryProperty getWorkingDirectory();
23
24 /** Environment variables */
25 MapProperty<String, String> getEnvironment();
26
27 /**
28 * Configures the environment variable using the specified action. The
29 * action is called when the value is calculated;
30 */
31 default void env(Action<Map<String, Object>> configure) {
32 var provider = getEnvironment()
33 .orElse(Map.of())
34 .map((base) -> {
35 var props = new HashMap<String, Object>(base);
36
37 configure.execute(props);
38
39 return Values.mapValues(props, Values::toString);
40 });
41
42 getEnvironment().set(provider);
43 }
44
45 default void env(Closure<?> configure) {
46 env(Closures.action(configure));
47 }
48 }
@@ -0,0 +1,12
1 package org.implab.gradle.common.dsl;
2
3 import org.eclipse.jdt.annotation.NonNullByDefault;
4
5 @NonNullByDefault
6 public interface PipeSpec {
7 RedirectToSpec getStdout();
8
9 RedirectToSpec getStderr();
10
11 RedirectFromSpec getStdin();
12 }
@@ -0,0 +1,34
1 package org.implab.gradle.common.exec;
2
3 import java.util.stream.Stream;
4
5 import org.eclipse.jdt.annotation.NonNullByDefault;
6 import org.implab.gradle.common.utils.Values;
7
8 @NonNullByDefault
9 public interface CommandBuilder {
10
11 CommandBuilder executable(String executable);
12
13 default CommandBuilder commandLine(String executable, String... args) {
14 return executable(executable)
15 .arguments(args);
16 }
17
18 default CommandBuilder arg(String arg0, String... args) {
19 return arguments(() -> Stream
20 .concat(Stream.of(arg0), Stream.of(args))
21 .iterator());
22 }
23
24 default CommandBuilder arguments(String... args) {
25 return arguments(Values.iterable(args));
26 }
27
28 CommandBuilder arguments(Iterable<String> args);
29
30 default CommandBuilder from(CommandSpec commandSpec) {
31 return executable(commandSpec.executable())
32 .arguments(commandSpec.arguments());
33 }
34 }
@@ -0,0 +1,16
1 package org.implab.gradle.common.exec;
2
3 import java.util.List;
4 import java.util.stream.Stream;
5
6 public interface CommandSpec {
7
8 String executable();
9
10 List<String> arguments();
11
12 default List<String> commandLine() {
13 return Stream.concat(Stream.of(executable()), arguments().stream()).toList();
14 }
15
16 }
@@ -0,0 +1,25
1 package org.implab.gradle.common.exec;
2
3 import java.io.File;
4 import java.util.Map;
5 import java.util.Optional;
6
7 import org.eclipse.jdt.annotation.NonNullByDefault;
8
9 @NonNullByDefault
10 public interface EnvironmentBuilder {
11 EnvironmentBuilder setVariable(String envVar, String value);
12
13 EnvironmentBuilder environment(Map<String, ? extends String> env);
14
15 EnvironmentBuilder unsetVariable(String envVar);
16
17 EnvironmentBuilder workingDirectory(File directory);
18
19 EnvironmentBuilder workingDirectory(Optional<? extends File> directory);
20
21 default EnvironmentBuilder from(EnvironmentSpec environmentSpec) {
22 return environment(environmentSpec.environment())
23 .workingDirectory(environmentSpec.workingDirectory());
24 }
25 }
@@ -0,0 +1,11
1 package org.implab.gradle.common.exec;
2
3 import java.io.File;
4 import java.util.Map;
5 import java.util.Optional;
6
7 public interface EnvironmentSpec {
8 Map<String,String> environment();
9
10 Optional<File> workingDirectory();
11 }
@@ -0,0 +1,27
1 package org.implab.gradle.common.exec;
2
3 import java.util.Optional;
4
5 import org.eclipse.jdt.annotation.NonNullByDefault;
6
7 @NonNullByDefault
8 public interface PipeBuilder {
9 PipeBuilder stdin(RedirectFrom from);
10
11 PipeBuilder stdin(Optional<? extends RedirectFrom> from);
12
13 PipeBuilder stdout(RedirectTo to);
14
15 PipeBuilder stdout(Optional<? extends RedirectTo> to);
16
17 PipeBuilder stderr(RedirectTo to);
18
19 PipeBuilder stderr(Optional<? extends RedirectTo> to);
20
21 default PipeBuilder from(PipeSpec pipeSpec) {
22 return stdin(pipeSpec.stdin())
23 .stdout(pipeSpec.stdout())
24 .stderr(pipeSpec.stderr());
25 }
26
27 }
@@ -0,0 +1,14
1 package org.implab.gradle.common.exec;
2
3 import java.util.Optional;
4
5 import org.eclipse.jdt.annotation.NonNullByDefault;
6
7 @NonNullByDefault
8 public interface PipeSpec {
9 Optional<RedirectTo> stdout();
10
11 Optional<RedirectTo> stderr();
12
13 Optional<RedirectFrom> stdin();
14 }
@@ -0,0 +1,19
1 package org.implab.gradle.common.exec;
2
3 import java.io.IOException;
4 import java.util.concurrent.CompletableFuture;
5 import java.util.concurrent.CompletionException;
6
7 public interface Runnable {
8 CompletableFuture<Integer> run() throws IOException;
9
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)
14 throw new CompletionException(new IOException(
15 String.format("The process is terminated with code %d", code)));
16 return code;
17 });
18 }
19 }
@@ -0,0 +1,18
1 package org.implab.gradle.common.utils;
2
3 import java.util.concurrent.CompletionException;
4 import java.util.function.Consumer;
5
6 public interface ThrowingConsumer<T, E extends Throwable> {
7 void accept(T value) throws E;
8
9 static <T> Consumer<T> guard(ThrowingConsumer<T,?> target) {
10 return arg -> {
11 try {
12 target.accept(arg);
13 } catch (Throwable err) {
14 throw new CompletionException(err);
15 }
16 };
17 }
18 }
@@ -0,0 +1,73
1 package org.implab.gradle.common.utils;
2
3 import java.util.Iterator;
4 import java.util.Map;
5 import java.util.Map.Entry;
6 import java.util.function.Function;
7 import java.util.function.Supplier;
8 import java.util.stream.Collectors;
9
10 import org.gradle.api.provider.Provider;
11
12 public final class Values {
13
14 private Values() {
15 }
16
17 /**
18 * Converts values in the specified map
19 * @param <K>
20 * @param <V>
21 * @param <U>
22 * @param map
23 * @param mapper
24 * @return
25 */
26 public static <K, V, U> Map<K, U> mapValues(Map<K, V> map, Function<V, U> mapper) {
27 Function<Entry<K, V>, V> getter = Entry::getValue;
28
29 return map.entrySet().stream()
30 .collect(Collectors.toMap(Entry::getKey, getter.andThen(mapper)));
31 }
32
33 /** Converts the supplied value to a string.
34 */
35 public static String toString(Object value) {
36 if (value == null) {
37 return null;
38 } else if (value instanceof String string) {
39 return string;
40 } else if (value instanceof Provider<?> provider) {
41 return toString(provider.getOrNull());
42 } else if (value instanceof Supplier<?> supplier) {
43 return toString(supplier.get());
44 } else {
45 return value.toString();
46 }
47 }
48
49 public static <T> Iterable<T> iterable(T[] values) {
50 return () -> new ArrayIterator<>(values);
51 }
52
53 private static class ArrayIterator<T> implements Iterator<T> {
54 private final T[] data;
55
56 private int pos = 0;
57
58 ArrayIterator(T[] data) {
59 this.data = data;
60 }
61
62 @Override
63 public boolean hasNext() {
64 return pos < data.length;
65 }
66
67 @Override
68 public T next() {
69 return data[pos++];
70 }
71 }
72
73 }
@@ -1,3 +1,4
1 1 syntax: glob
2 2 .gradle/
3 3 common/build/
4 common/bin/
@@ -1,7 +1,5
1 1 package org.implab.gradle.common.dsl;
2 2
3 import java.io.File;
4 import java.io.InputStream;
5 3 import java.util.Optional;
6 4 import java.util.function.Supplier;
7 5
@@ -19,19 +17,11 public class RedirectFromSpec {
19 17 return streamRedirect != null ? Optional.ofNullable(streamRedirect.get()) : Optional.empty();
20 18 }
21 19
22 public void fromFile(File file) {
23 this.streamRedirect = () -> RedirectFrom.file(file);
24 }
25
26 public void fromFile(Provider<File> file) {
27 this.streamRedirect = file.map(RedirectFrom::file)::get;
28 }
29
30 public void fromStream(InputStream stream) {
31 this.streamRedirect = () -> RedirectFrom.stream(stream);
32 }
33
34 public void fromStream(Provider<InputStream> stream) {
35 this.streamRedirect = stream.map(RedirectFrom::stream)::get;
20 public void from(Object input) {
21 if (input instanceof Provider<?> inputProvider) {
22 this.streamRedirect = inputProvider.map(RedirectFrom::any)::get;
23 } else {
24 this.streamRedirect = () -> RedirectFrom.any(input);
25 }
36 26 }
37 27 }
@@ -6,37 +6,52 import java.util.Optional;
6 6 import java.util.function.Consumer;
7 7 import java.util.function.Supplier;
8 8
9 import org.eclipse.jdt.annotation.NonNullByDefault;
10 import org.eclipse.jdt.annotation.Nullable;
9 11 import org.gradle.api.provider.Provider;
10 12 import org.implab.gradle.common.exec.RedirectTo;
11 13
14 @NonNullByDefault
12 15 public class RedirectToSpec {
13 16 private Supplier<RedirectTo> streamRedirect;
14 17
15 18 public boolean isRedirected() {
16 return streamRedirect != null;
19 return getRedirection().isPresent();
17 20 }
18 21
19 22 public Optional<RedirectTo> getRedirection() {
20 23 return streamRedirect != null ? Optional.ofNullable(streamRedirect.get()) : Optional.empty();
21 24 }
22 25
26 public @Nullable RedirectTo getRedirectionOrNull() {
27 return streamRedirect != null ? streamRedirect.get() : null;
28 }
29
23 30 public void toFile(File file) {
24 31 this.streamRedirect = () -> RedirectTo.file(file);
25 32 }
26 33
27 public void toFile(Provider<File> file) {
28 this.streamRedirect = file.map(RedirectTo::file)::get;
34 public void toFile(Provider<File> fileProvider) {
35 this.streamRedirect = fileProvider.map(RedirectTo::file)::getOrNull;
29 36 }
30 37
31 38 public void toStream(OutputStream stream) {
32 39 this.streamRedirect = () -> RedirectTo.stream(stream);
33 40 }
34 41
35 public void toStream(Provider<OutputStream> stream) {
36 this.streamRedirect = stream.map(RedirectTo::stream)::get;
42 public void toStream(Provider<OutputStream> streamProvider) {
43 this.streamRedirect = streamProvider.map(RedirectTo::stream)::getOrNull;
37 44 }
38 45
39 public void toConsumer(Consumer<String> consumer) {
46 public void to(Object output) {
47 if (output instanceof Provider<?> outputProvider) {
48 this.streamRedirect = outputProvider.map(RedirectTo::any)::get;
49 } else {
50 this.streamRedirect = () -> RedirectTo.any(output);
51 }
52 }
53
54 public void consume(Consumer<String> consumer) {
40 55 this.streamRedirect = () -> RedirectTo.consumer(consumer);
41 56 }
42 57 }
@@ -18,15 +18,25 class EchoExecBuilder extends ExecBuilde
18 18 }
19 19
20 20 @Override
21 protected CompletableFuture<Integer> startInternal(Map<String, String> environment, File directory,
22 List<String> commandLine, Optional<RedirectFrom> stdinRedirect, Optional<RedirectTo> stdoutRedirect,
21 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,
23 27 Optional<RedirectTo> stderrRedirect) throws IOException {
28
24 29 var outputRedirect = echoToStderr ? stderrRedirect : stdoutRedirect;
25 30
26 return outputRedirect.map(to -> {
27 var bytes = String.format("exec: %s", commandLine).getBytes(StandardCharsets.UTF_8);
28 return to.redirect(new ByteArrayInputStream(bytes)).thenApply((x) -> 0);
29 }).orElse(CompletableFuture.completedFuture(0));
31 return outputRedirect
32 .map(to -> {
33 var bytes = String.format("exec: %s", commandLine)
34 .getBytes(StandardCharsets.UTF_8);
35
36 return to.redirect(new ByteArrayInputStream(bytes))
37 .thenApply((x) -> 0);
38 })
39 .orElse(CompletableFuture.completedFuture(0));
30 40 }
31 41
32 42 }
@@ -4,26 +4,32 import java.util.ArrayList;
4 4 import java.util.HashMap;
5 5 import java.util.List;
6 6 import java.util.Map;
7 import java.util.Objects;
8 7 import java.util.Optional;
9 8 import java.util.concurrent.CompletableFuture;
10 import java.util.stream.Stream;
11 import java.util.Collection;
12 9
13 10 import org.eclipse.jdt.annotation.NonNullByDefault;
11 import org.eclipse.jdt.annotation.Nullable;
14 12
15 13 import java.io.File;
16 14 import java.io.IOException;
17 15
16 import static java.util.Objects.requireNonNull;
17
18 18 /** Command line builder */
19 19 @NonNullByDefault
20 public abstract class ExecBuilder {
20 public abstract class ExecBuilder implements
21 CommandBuilder,
22 PipeBuilder,
23 EnvironmentBuilder,
24 Runnable
25 {
26 private String executable;
21 27
22 private final List<String> commandLine = new ArrayList<>();
28 private final List<String> arguments = new ArrayList<>();
23 29
24 30 private final Map<String, String> environment = new HashMap<>();
25 31
26 private File directory;
32 private @Nullable File directory;
27 33
28 34 private RedirectFrom inputRedirect;
29 35
@@ -31,60 +37,83 public abstract class ExecBuilder {
31 37
32 38 private RedirectTo errorRedirect;
33 39
34 /** Sets command line, clears previous one if any */
35 public ExecBuilder command(String cmd, String... args) {
36 commandLine.clear();
37 commandLine.add(cmd);
38 Stream.of(args).forEach(commandLine::add);
40 @Override
41 public ExecBuilder executable(String executable) {
42 requireNonNull(executable, "cmd can't be null");
43 this.executable = executable;
44 return this;
45 }
46
47 @Override
48 public ExecBuilder arguments(Iterable<String> args) {
49 requireNonNull(args, "Args must not be null");
50 arguments.clear();
51 for(var arg: args)
52 arguments.add(arg);
39 53 return this;
40 54 }
41 55
42 public ExecBuilder command(Collection<String> cmd) {
43 this.commandLine.clear();
44 this.commandLine.addAll(cmd);
56 public ExecBuilder commandLine(Iterable<? extends String> cmd) {
57 requireNonNull(cmd, "cmd can't be null");
58
59 this.arguments.clear();
60 for (var arg : cmd)
61 this.arguments.add(arg);
45 62 return this;
46 63 }
47 64
48 /** Adds an argument to the command line */
49 public ExecBuilder argument(String arg) {
50 Objects.requireNonNull(arg, "arg parameter can't be null");
51 commandLine.add(arg);
65 @Override
66 public ExecBuilder arg(String arg0, String... args) {
67 requireNonNull(arg0, "arg0 parameter can't be null");
68 arguments.add(arg0);
69
70 for (var arg : args)
71 arguments.add(arg);
72
52 73 return this;
53 74 }
54 75
55 76 /** Sets the working directory */
56 public ExecBuilder directory(File directory) {
57 Objects.requireNonNull(directory, "directory parameter can't be null");
77 @Override
78 public ExecBuilder workingDirectory(File directory) {
79 requireNonNull(directory, "directory parameter can't be null");
80
58 81 this.directory = directory;
59 82 return this;
60 83 }
61 84
85 @Override
86 public ExecBuilder workingDirectory(Optional<? extends File> directory) {
87 requireNonNull(directory, "directory parameter can't be null");
88
89 this.directory = directory.orElse(null);
90 return this;
91 }
92
62 93 /**
63 94 * Sets the environment value. The value cannot be null.
64 95 *
65 96 * @param envVar The name of the environment variable
66 97 * @param value The value to set,
67 98 */
68 public ExecBuilder setEnvironment(String envVar, String value) {
69 Objects.requireNonNull(value, "Value can't be null");
70 Objects.requireNonNull(envVar, "envVar parameter can't be null");
99 @Override
100 public ExecBuilder setVariable(String envVar, String value) {
101 requireNonNull(value, "Value can't be null");
102 requireNonNull(envVar, "envVar parameter can't be null");
103
71 104 environment.put(envVar, value);
72 105 return this;
73 106 }
74 107
75 public ExecBuilder setEnvironment(Map<String, String> env) {
76 Objects.requireNonNull(env, "env parameter can't be null");
108 @Override
109 public ExecBuilder environment(Map<String,? extends String> env) {
110 requireNonNull(env, "env parameter can't be null");
77 111
78 112 environment.clear();
79 113 environment.putAll(env);
80 114 return this;
81 115 }
82 116
83 public ExecBuilder unsetEnvironment(String envVar) {
84 environment.remove(envVar);
85 return this;
86 }
87
88 117 /**
89 118 * Sets redirection for the stdin, {@link RedirectFrom} will be applied
90 119 * every time the process is started.
@@ -92,36 +121,61 public abstract class ExecBuilder {
92 121 * <p>
93 122 * If redirection
94 123 */
124 @Override
95 125 public ExecBuilder stdin(RedirectFrom from) {
96 Objects.requireNonNull(from, "from parameter can't be null");
126 requireNonNull(from, "from parameter can't be null");
127
97 128 inputRedirect = from;
98 129 return this;
99 130 }
100 131
132 @Override
133 public PipeBuilder stdin(Optional<? extends RedirectFrom> from) {
134 requireNonNull(from, "from parameter can't be null");
135 inputRedirect = from.orElse(null);
136 return this;
137 }
138
101 139 /**
102 140 * Sets redirection for the stdout, {@link RedirectTo} will be applied
103 141 * every time the process is started.
104 142 */
143 @Override
105 144 public ExecBuilder stdout(RedirectTo out) {
106 Objects.requireNonNull(out, "out parameter can't be null");
145 requireNonNull(out, "out parameter can't be null");
107 146 outputRedirect = out;
108 147 return this;
109 148 }
110 149
150 @Override
151 public PipeBuilder stdout(Optional<? extends RedirectTo> to) {
152 requireNonNull(to, "from parameter can't be null");
153 outputRedirect = to.orElse(null);
154 return this;
155 }
156
111 157 /**
112 158 * Sets redirection for the stderr, {@link RedirectTo} will be applied
113 159 * every time the process is started.
114 160 */
161 @Override
115 162 public ExecBuilder stderr(RedirectTo err) {
116 Objects.requireNonNull(err, "err parameter can't be null");
163 requireNonNull(err, "err parameter can't be null");
117 164 errorRedirect = err;
118 165 return this;
119 166 }
120 167
121 /** Implement this function to */
168 @Override
169 public PipeBuilder stderr(Optional<? extends RedirectTo> to) {
170 requireNonNull(to, "from parameter can't be null");
171 errorRedirect = to.orElse(null);
172 return this;
173 }
174
175 /** Implement this function to */
122 176 protected abstract CompletableFuture<Integer> startInternal(
123 177 Map<String, String> environment,
124 File directory,
178 Optional<File> directory,
125 179 List<String> commandLine,
126 180 Optional<RedirectFrom> stdinRedirect,
127 181 Optional<RedirectTo> stdoutRedirect,
@@ -134,10 +188,17 public abstract class ExecBuilder {
134 188 * @return
135 189 * @throws IOException
136 190 */
137 public CompletableFuture<Integer> start() throws IOException {
191 public CompletableFuture<Integer> run() throws IOException {
192 if (executable == null || executable.isEmpty())
193 throw new IllegalStateException("The executable isn't set");
194
195 var commandLine = new ArrayList<String>();
196 commandLine.add(executable);
197 commandLine.addAll(arguments);
198
138 199 return startInternal(
139 200 environment,
140 directory,
201 Optional.ofNullable(directory),
141 202 commandLine,
142 203 Optional.ofNullable(inputRedirect),
143 204 Optional.ofNullable(outputRedirect),
@@ -12,7 +12,7 import java.util.function.Consumer;
12 12 public interface ExecShell {
13 13
14 14 /** Creates new process builder {@link ExecBuilder} */
15 ExecBuilder builder();
15 ExecBuilder executable();
16 16
17 17 /**
18 18 * Creates default process execution "shell" which will execute processes
@@ -47,7 +47,7 public interface ExecShell {
47 47 static ExecShell echo(Consumer<List<String>> consumer) {
48 48 return () -> new ExecBuilder() {
49 49 @Override
50 protected CompletableFuture<Integer> startInternal(Map<String, String> environment, File directory,
50 protected CompletableFuture<Integer> startInternal(Map<String, String> environment, Optional<File> directory,
51 51 List<String> commandLine, Optional<RedirectFrom> stdinRedirect, Optional<RedirectTo> stdoutRedirect,
52 52 Optional<RedirectTo> stderrRedirect) throws IOException {
53 53 consumer.accept(commandLine);
@@ -33,5 +33,17 public interface RedirectFrom {
33 33 });
34 34 }
35 35
36 public static RedirectFrom any(final Object output) {
37 if (output instanceof File f) {
38 return file(f);
39 } else if (output instanceof InputStream stm) {
40 return stream(stm);
41 } else if (output instanceof RedirectFrom self) {
42 return self;
43 } else {
44 throw new IllegalArgumentException("The specified argument type isn't supported: " + output.getClass());
45 }
46 }
47
36 48
37 49 }
@@ -8,14 +8,28 import java.util.Scanner;
8 8 import java.util.concurrent.CompletableFuture;
9 9 import java.util.function.Consumer;
10 10
11 import org.eclipse.jdt.annotation.NonNullByDefault;
12
11 13 /**
12 14 * RedirectSpec
13 15 */
16 @NonNullByDefault
14 17 public interface RedirectTo {
15 18 CompletableFuture<Void> redirect(InputStream from);
16 19
20 public interface StringConsumer extends Consumer<String> {
21 }
22
17 23 public static RedirectTo consumer(final Consumer<String> consumer) {
24 return consumer(new StringConsumer() {
25 @Override
26 public void accept(String s) {
27 consumer.accept(s);
28 }
29 });
30 }
18 31
32 public static RedirectTo consumer(final StringConsumer consumer) {
19 33 return (src) -> CompletableFuture.runAsync(() -> {
20 34 try (Scanner sc = new Scanner(src)) {
21 35 while (sc.hasNextLine()) {
@@ -44,4 +58,18 public interface RedirectTo {
44 58 }
45 59 });
46 60 }
61
62 public static RedirectTo any(final Object output) {
63 if (output instanceof StringConsumer fn) {
64 return consumer(s -> fn.accept(s));
65 } else if (output instanceof File f) {
66 return file(f);
67 } else if (output instanceof OutputStream stm) {
68 return stream(stm);
69 } else if (output instanceof RedirectTo self) {
70 return self;
71 } else {
72 throw new IllegalArgumentException("The specified argument type isn't supported: " + output.getClass());
73 }
74 }
47 75 } No newline at end of file
@@ -11,12 +11,17 import java.util.concurrent.CompletableF
11 11
12 12 class SystemExecBuilder extends ExecBuilder {
13 13 @Override
14 protected CompletableFuture<Integer> startInternal(Map<String, String> environment, File directory,
15 List<String> commandLine, Optional<RedirectFrom> stdinRedirect, Optional<RedirectTo> stdoutRedirect,
16 Optional<RedirectTo> stderrRedirect) throws IOException {
14 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 {
17 21
18 var builder = new ProcessBuilder(commandLine)
19 .directory(directory);
22 var builder = new ProcessBuilder(commandLine);
23
24 directory.ifPresent(builder::directory);
20 25
21 26 builder.environment().putAll(environment);
22 27
@@ -1,12 +1,29
1 1 package org.implab.gradle.common.tasks;
2 2
3 import java.io.IOException;
4 import java.util.Map;
5 import java.util.concurrent.ExecutionException;
6 import java.util.stream.Stream;
7
3 8 import org.gradle.api.DefaultTask;
9 import org.gradle.api.file.DirectoryProperty;
4 10 import org.gradle.api.provider.ListProperty;
11 import org.gradle.api.provider.MapProperty;
5 12 import org.gradle.api.tasks.Internal;
13 import org.gradle.api.tasks.TaskAction;
14 import org.implab.gradle.common.dsl.CommandSpec;
15 import org.implab.gradle.common.dsl.PipeSpec;
6 16 import org.implab.gradle.common.dsl.RedirectFromSpec;
7 17 import org.implab.gradle.common.dsl.RedirectToSpec;
18 import org.implab.gradle.common.dsl.EnvironmentSpec;
19 import org.implab.gradle.common.exec.ExecBuilder;
20 import org.implab.gradle.common.utils.ObjectsMixin;
21 import org.implab.gradle.common.utils.Strings;
22 import org.implab.gradle.common.utils.ThrowingConsumer;
8 23
9 public abstract class ExecuteTask extends DefaultTask {
24 public abstract class ShellExecTask
25 extends DefaultTask
26 implements CommandSpec, PipeSpec, EnvironmentSpec, ObjectsMixin {
10 27
11 28 private final RedirectToSpec redirectStderr = new RedirectToSpec();
12 29
@@ -15,13 +32,22 public abstract class ExecuteTask extend
15 32 private final RedirectFromSpec redirectStdin = new RedirectFromSpec();
16 33
17 34 @Internal
35 @Override
36 public abstract DirectoryProperty getWorkingDirectory();
37
38 @Internal
39 @Override
40 public abstract MapProperty<String, String> getEnvironment();
41
42 @Internal
43 @Override
18 44 public abstract ListProperty<String> getCommandLine();
19 45
20
21 46 /**
22 47 * STDIN redirection, if not specified, no input will be passed to the command
23 48 */
24 49 @Internal
50 @Override
25 51 public RedirectFromSpec getStdin() {
26 52 return redirectStdin;
27 53 }
@@ -30,6 +56,7 public abstract class ExecuteTask extend
30 56 * STDOUT redirection, if not specified, redirected to logger::info
31 57 */
32 58 @Internal
59 @Override
33 60 public RedirectToSpec getStdout() {
34 61 return redirectStdout;
35 62 }
@@ -38,15 +65,46 public abstract class ExecuteTask extend
38 65 * STDERR redirection, if not specified, redirected to logger::error
39 66 */
40 67 @Internal
68 @Override
41 69 public RedirectToSpec getStderr() {
42 70 return redirectStderr;
43 71 }
44 72
73 @Override
74 public void commandLine(Object arg0, Object... args) {
75 getCommandLine().set(provider(() -> Stream.concat(
76 Stream.of(arg0),
77 Stream.of(args))
78 .map(Strings::asString).toList()));
45 79
46 /** Appends specified parameters to the command line */
47 void commandLine(String... args) {
48 getCommandLine().addAll(args);
49 }
50
51
80 }
81
82 @Override
83 public void args(Object arg0, Object... args) {
84 getCommandLine().addAll(provider(() -> Stream.concat(
85 Stream.of(arg0),
86 Stream.of(args))
87 .map(Strings::asString).toList()));
88 }
89
90 protected abstract ExecBuilder execBuilder();
91
92 @TaskAction
93 public final void run() throws IOException, InterruptedException, ExecutionException {
94 var execBuilder = execBuilder()
95 .workingDirectory(getWorkingDirectory().get().getAsFile())
96 .environment(getEnvironment().getOrElse(Map.of()))
97 .commandLine(getCommandLine().get());
98
99 getStdout().getRedirection().ifPresent(execBuilder::stdout);
100 getStderr().getRedirection().ifPresent(execBuilder::stderr);
101 getStdin().getRedirection().ifPresent(execBuilder::stdin);
102
103 execBuilder.run().thenAccept(ThrowingConsumer.guard(this::checkRetCode)).join();
104 }
105
106 protected void checkRetCode(Integer code) throws IOException{
107 throw new IOException(String.format("The process is terminated with code %s", code));
108 }
109
52 110 }
@@ -2,13 +2,31 package org.implab.gradle.common.utils;
2 2
3 3 import java.util.regex.Pattern;
4 4
5 import org.eclipse.jdt.annotation.NonNullByDefault;
6 import org.gradle.api.provider.Provider;
7
8 @NonNullByDefault
5 9 public class Strings {
6 10
7 11 private static final Pattern firstLetter = Pattern.compile("^\\w");
8 12
9 13 public static String capitalize(String string) {
10 if (string == null || string.length() == 0)
11 return "";
12 return firstLetter.matcher(string).replaceFirst(m -> m.group().toUpperCase());
14 return string == null ? null
15 : string.length() == 0 ? string
16 : firstLetter.matcher(string).replaceFirst(m -> m.group().toUpperCase());
17 }
18
19 public static void argumentNotNullOrEmpty(String value, String argumentName) {
20 if (value == null || value.length() == 0)
21 throw new IllegalArgumentException(String.format("Argument %s can't be null or empty", argumentName));
22 }
23
24 public static String asString(Object value) {
25 if (value == null)
26 return null;
27 if (value instanceof Provider<?> provider)
28 return asString(provider.get());
29 else
30 return value.toString();
13 31 }
14 32 }
1 NO CONTENT: modified file, binary diff hidden
@@ -1,6 +1,6
1 1 distributionBase=GRADLE_USER_HOME
2 2 distributionPath=wrapper/dists
3 distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
3 distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
4 4 networkTimeout=10000
5 5 validateDistributionUrl=true
6 6 zipStoreBase=GRADLE_USER_HOME
@@ -43,11 +43,11 set JAVA_EXE=java.exe
43 43 %JAVA_EXE% -version >NUL 2>&1
44 44 if %ERRORLEVEL% equ 0 goto execute
45 45
46 echo.
47 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 echo.
49 echo Please set the JAVA_HOME variable in your environment to match the
50 echo location of your Java installation.
46 echo. 1>&2
47 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
48 echo. 1>&2
49 echo Please set the JAVA_HOME variable in your environment to match the 1>&2
50 echo location of your Java installation. 1>&2
51 51
52 52 goto fail
53 53
@@ -57,11 +57,11 set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 57
58 58 if exist "%JAVA_EXE%" goto execute
59 59
60 echo.
61 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 echo.
63 echo Please set the JAVA_HOME variable in your environment to match the
64 echo location of your Java installation.
60 echo. 1>&2
61 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
62 echo. 1>&2
63 echo Please set the JAVA_HOME variable in your environment to match the 1>&2
64 echo location of your Java installation. 1>&2
65 65
66 66 goto fail
67 67
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now