| @@ -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,37 +1,27 | |||
|
|
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 | |
|
|
8 | 6 | import org.gradle.api.provider.Provider; |
|
|
9 | 7 | import org.implab.gradle.common.exec.RedirectFrom; |
|
|
10 | 8 | |
|
|
11 | 9 | public class RedirectFromSpec { |
|
|
12 | 10 | private Supplier<RedirectFrom> streamRedirect; |
|
|
13 | 11 | |
|
|
14 | 12 | public boolean isRedirected() { |
|
|
15 | 13 | return streamRedirect != null; |
|
|
16 | 14 | } |
|
|
17 | 15 | |
|
|
18 | 16 | public Optional<RedirectFrom> getRedirection() { |
|
|
19 | 17 | return streamRedirect != null ? Optional.ofNullable(streamRedirect.get()) : Optional.empty(); |
|
|
20 | 18 | } |
|
|
21 | 19 | |
|
|
22 |
public void from |
|
|
|
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 | } |
| @@ -1,42 +1,57 | |||
|
|
1 | 1 | package org.implab.gradle.common.dsl; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.io.File; |
|
|
4 | 4 | import java.io.OutputStream; |
|
|
5 | 5 | 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 |
|
|
|
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 | } |
| @@ -1,32 +1,42 | |||
|
|
1 | 1 | package org.implab.gradle.common.exec; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.io.ByteArrayInputStream; |
|
|
4 | 4 | import java.io.File; |
|
|
5 | 5 | import java.io.IOException; |
|
|
6 | 6 | import java.nio.charset.StandardCharsets; |
|
|
7 | 7 | import java.util.List; |
|
|
8 | 8 | import java.util.Map; |
|
|
9 | 9 | import java.util.Optional; |
|
|
10 | 10 | import java.util.concurrent.CompletableFuture; |
|
|
11 | 11 | |
|
|
12 | 12 | class EchoExecBuilder extends ExecBuilder { |
|
|
13 | 13 | |
|
|
14 | 14 | private final boolean echoToStderr; |
|
|
15 | 15 | |
|
|
16 | 16 | public EchoExecBuilder(boolean echoToStderr) { |
|
|
17 | 17 | this.echoToStderr = echoToStderr; |
|
|
18 | 18 | } |
|
|
19 | 19 | |
|
|
20 | 20 | @Override |
|
|
21 |
protected CompletableFuture<Integer> startInternal( |
|
|
|
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 |
|
|
|
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 | } |
| @@ -1,147 +1,208 | |||
|
|
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 | 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> |
|
|
|
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 | |
|
|
30 | 36 | private RedirectTo outputRedirect; |
|
|
31 | 37 | |
|
|
32 | 38 | private RedirectTo errorRedirect; |
|
|
33 | 39 | |
|
|
34 | /** Sets command line, clears previous one if any */ | |
|
|
35 |
public ExecBuilder |
|
|
|
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( |
|
|
|
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 arg |
|
|
|
50 |
|
|
|
|
51 |
|
|
|
|
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 |
|
|
|
|
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. |
|
|
91 | 120 | * |
|
|
92 | 121 | * <p> |
|
|
93 | 122 | * If redirection |
|
|
94 | 123 | */ |
|
|
124 | @Override | |
|
|
95 | 125 | public ExecBuilder stdin(RedirectFrom from) { |
|
|
96 |
|
|
|
|
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 |
|
|
|
|
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 |
|
|
|
|
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, |
|
|
128 | 182 | Optional<RedirectTo> stderrRedirect) throws IOException; |
|
|
129 | 183 | |
|
|
130 | 184 | /** |
|
|
131 | 185 | * Creates and starts new process and returns {@link CompletableFuture}. The |
|
|
132 | 186 | * process may be a remote process, or a shell process or etc. |
|
|
133 | 187 | * |
|
|
134 | 188 | * @return |
|
|
135 | 189 | * @throws IOException |
|
|
136 | 190 | */ |
|
|
137 |
public CompletableFuture<Integer> |
|
|
|
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), |
|
|
144 | 205 | Optional.ofNullable(errorRedirect)); |
|
|
145 | 206 | } |
|
|
146 | 207 | |
|
|
147 | 208 | } |
| @@ -1,58 +1,58 | |||
|
|
1 | 1 | package org.implab.gradle.common.exec; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.io.File; |
|
|
4 | 4 | import java.io.IOException; |
|
|
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 | import java.util.function.Consumer; |
|
|
10 | 10 | |
|
|
11 | 11 | /** Shell used to start processes */ |
|
|
12 | 12 | public interface ExecShell { |
|
|
13 | 13 | |
|
|
14 | 14 | /** Creates new process builder {@link ExecBuilder} */ |
|
|
15 |
ExecBuilder b |
|
|
|
15 | ExecBuilder executable(); | |
|
|
16 | 16 | |
|
|
17 | 17 | /** |
|
|
18 | 18 | * Creates default process execution "shell" which will execute processes |
|
|
19 | 19 | * directly. |
|
|
20 | 20 | */ |
|
|
21 | 21 | static ExecShell system() { |
|
|
22 | 22 | return () -> new SystemExecBuilder(); |
|
|
23 | 23 | } |
|
|
24 | 24 | |
|
|
25 | 25 | /** |
|
|
26 | 26 | * Creates a stub shell which will print command to STDERR and return 0 (no |
|
|
27 | 27 | * error). |
|
|
28 | 28 | */ |
|
|
29 | 29 | static ExecShell echoStderr() { |
|
|
30 | 30 | return () -> new EchoExecBuilder(true); |
|
|
31 | 31 | } |
|
|
32 | 32 | |
|
|
33 | 33 | /** |
|
|
34 | 34 | * Creates a stub shell which will print command to STDOUT and return 0 (no |
|
|
35 | 35 | * error). |
|
|
36 | 36 | */ |
|
|
37 | 37 | |
|
|
38 | 38 | static ExecShell echo() { |
|
|
39 | 39 | return () -> new EchoExecBuilder(false); |
|
|
40 | 40 | } |
|
|
41 | 41 | |
|
|
42 | 42 | /** Creates a stub shell which will call the specified consumer and will supply a command line to it. |
|
|
43 | 43 | * The command line will be passed as a list of strings. |
|
|
44 | 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(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); |
|
|
54 | 54 | return CompletableFuture.completedFuture(0); |
|
|
55 | 55 | } |
|
|
56 | 56 | }; |
|
|
57 | 57 | } |
|
|
58 | 58 | } |
| @@ -1,37 +1,49 | |||
|
|
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 | 9 | /** Describes how to redirect input streams. This interface is used to configure |
|
|
10 | 10 | * lazy redirection. {@link #redirect(OutputStream)} is called when the process |
|
|
11 | 11 | * is started. |
|
|
12 | 12 | */ |
|
|
13 | 13 | public interface RedirectFrom { |
|
|
14 | 14 | CompletableFuture<Void> redirect(OutputStream to); |
|
|
15 | 15 | |
|
|
16 | 16 | public static RedirectFrom file(final File file) { |
|
|
17 | 17 | return to -> CompletableFuture.runAsync(() -> { |
|
|
18 | 18 | try (var from = new FileInputStream(file); to) { |
|
|
19 | 19 | from.transferTo(to); |
|
|
20 | 20 | } catch (Exception e) { |
|
|
21 | 21 | // silence! |
|
|
22 | 22 | } |
|
|
23 | 23 | }); |
|
|
24 | 24 | } |
|
|
25 | 25 | |
|
|
26 | 26 | public static RedirectFrom stream(final InputStream from) { |
|
|
27 | 27 | return to -> CompletableFuture.runAsync(() -> { |
|
|
28 | 28 | try (from; to) { |
|
|
29 | 29 | from.transferTo(to); |
|
|
30 | 30 | } catch (Exception e) { |
|
|
31 | 31 | // silence! |
|
|
32 | 32 | } |
|
|
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 | } |
| @@ -1,47 +1,75 | |||
|
|
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 | 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()) { |
|
|
22 | 36 | consumer.accept(sc.nextLine()); |
|
|
23 | 37 | } |
|
|
24 | 38 | } |
|
|
25 | 39 | }); |
|
|
26 | 40 | } |
|
|
27 | 41 | |
|
|
28 | 42 | public static RedirectTo file(final File file) { |
|
|
29 | 43 | return src -> CompletableFuture.runAsync(() -> { |
|
|
30 | 44 | try (OutputStream out = new FileOutputStream(file)) { |
|
|
31 | 45 | src.transferTo(out); |
|
|
32 | 46 | } catch (Exception e) { |
|
|
33 | 47 | // silence! |
|
|
34 | 48 | } |
|
|
35 | 49 | }); |
|
|
36 | 50 | } |
|
|
37 | 51 | |
|
|
38 | 52 | public static RedirectTo stream(final OutputStream dest) { |
|
|
39 | 53 | return src -> CompletableFuture.runAsync(() -> { |
|
|
40 | 54 | try (dest; src) { |
|
|
41 | 55 | src.transferTo(dest); |
|
|
42 | 56 | } catch (Exception e) { |
|
|
43 | 57 | // silence! |
|
|
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 |
| @@ -1,44 +1,49 | |||
|
|
1 | 1 | package org.implab.gradle.common.exec; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.io.File; |
|
|
4 | 4 | import java.io.IOException; |
|
|
5 | 5 | import java.lang.ProcessBuilder.Redirect; |
|
|
6 | 6 | import java.util.ArrayList; |
|
|
7 | 7 | import java.util.List; |
|
|
8 | 8 | import java.util.Map; |
|
|
9 | 9 | import java.util.Optional; |
|
|
10 | 10 | import java.util.concurrent.CompletableFuture; |
|
|
11 | 11 | |
|
|
12 | 12 | class SystemExecBuilder extends ExecBuilder { |
|
|
13 | 13 | @Override |
|
|
14 |
protected CompletableFuture<Integer> startInternal( |
|
|
|
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 | |
|
|
23 | 28 | // TODO Auto-generated method stub |
|
|
24 | 29 | var tasks = new ArrayList<CompletableFuture<?>>(); |
|
|
25 | 30 | |
|
|
26 | 31 | if (!stdoutRedirect.isPresent()) |
|
|
27 | 32 | builder.redirectOutput(Redirect.DISCARD); |
|
|
28 | 33 | if (!stderrRedirect.isPresent()) |
|
|
29 | 34 | builder.redirectError(Redirect.DISCARD); |
|
|
30 | 35 | |
|
|
31 | 36 | // run process |
|
|
32 | 37 | var proc = builder.start(); |
|
|
33 | 38 | |
|
|
34 | 39 | tasks.add(proc.onExit()); |
|
|
35 | 40 | |
|
|
36 | 41 | stdinRedirect.map(from -> from.redirect(proc.getOutputStream())).ifPresent(tasks::add); |
|
|
37 | 42 | stdoutRedirect.map(to -> to.redirect(proc.getInputStream())).ifPresent(tasks::add); |
|
|
38 | 43 | stderrRedirect.map(to -> to.redirect(proc.getErrorStream())).ifPresent(tasks::add); |
|
|
39 | 44 | |
|
|
40 | 45 | return CompletableFuture |
|
|
41 | 46 | .allOf(tasks.toArray(new CompletableFuture<?>[0])) |
|
|
42 | 47 | .thenApply(t -> proc.exitValue()); |
|
|
43 | 48 | } |
|
|
44 | 49 | } |
| @@ -1,52 +1,110 | |||
|
|
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 Exec |
|
|
|
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 | |
|
|
13 | 30 | private final RedirectToSpec redirectStdout = new RedirectToSpec(); |
|
|
14 | 31 | |
|
|
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 | } |
|
|
28 | 54 | |
|
|
29 | 55 | /** |
|
|
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 | } |
|
|
36 | 63 | |
|
|
37 | 64 | /** |
|
|
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 | } |
| @@ -1,14 +1,32 | |||
|
|
1 | 1 | 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 |
|
|
|
|
11 | return ""; | |
|
|
12 |
|
|
|
|
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,7 +1,7 | |||
|
|
1 | 1 | distributionBase=GRADLE_USER_HOME |
|
|
2 | 2 | distributionPath=wrapper/dists |
|
|
3 |
distributionUrl=https\://services.gradle.org/distributions/gradle-8. |
|
|
|
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 |
|
|
7 | 7 | zipStorePath=wrapper/dists |
| @@ -1,92 +1,92 | |||
|
|
1 | 1 | @rem |
|
|
2 | 2 | @rem Copyright 2015 the original author or authors. |
|
|
3 | 3 | @rem |
|
|
4 | 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
5 | 5 | @rem you may not use this file except in compliance with the License. |
|
|
6 | 6 | @rem You may obtain a copy of the License at |
|
|
7 | 7 | @rem |
|
|
8 | 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 |
|
|
9 | 9 | @rem |
|
|
10 | 10 | @rem Unless required by applicable law or agreed to in writing, software |
|
|
11 | 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, |
|
|
12 | 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
|
13 | 13 | @rem See the License for the specific language governing permissions and |
|
|
14 | 14 | @rem limitations under the License. |
|
|
15 | 15 | @rem |
|
|
16 | 16 | |
|
|
17 | 17 | @if "%DEBUG%"=="" @echo off |
|
|
18 | 18 | @rem ########################################################################## |
|
|
19 | 19 | @rem |
|
|
20 | 20 | @rem Gradle startup script for Windows |
|
|
21 | 21 | @rem |
|
|
22 | 22 | @rem ########################################################################## |
|
|
23 | 23 | |
|
|
24 | 24 | @rem Set local scope for the variables with windows NT shell |
|
|
25 | 25 | if "%OS%"=="Windows_NT" setlocal |
|
|
26 | 26 | |
|
|
27 | 27 | set DIRNAME=%~dp0 |
|
|
28 | 28 | if "%DIRNAME%"=="" set DIRNAME=. |
|
|
29 | 29 | @rem This is normally unused |
|
|
30 | 30 | set APP_BASE_NAME=%~n0 |
|
|
31 | 31 | set APP_HOME=%DIRNAME% |
|
|
32 | 32 | |
|
|
33 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. |
|
|
34 | 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi |
|
|
35 | 35 | |
|
|
36 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
|
|
37 | 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" |
|
|
38 | 38 | |
|
|
39 | 39 | @rem Find java.exe |
|
|
40 | 40 | if defined JAVA_HOME goto findJavaFromJavaHome |
|
|
41 | 41 | |
|
|
42 | 42 | 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 | |
|
|
54 | 54 | :findJavaFromJavaHome |
|
|
55 | 55 | set JAVA_HOME=%JAVA_HOME:"=% |
|
|
56 | 56 | 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 | |
|
|
68 | 68 | :execute |
|
|
69 | 69 | @rem Setup the command line |
|
|
70 | 70 | |
|
|
71 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar |
|
|
72 | 72 | |
|
|
73 | 73 | |
|
|
74 | 74 | @rem Execute Gradle |
|
|
75 | 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* |
|
|
76 | 76 | |
|
|
77 | 77 | :end |
|
|
78 | 78 | @rem End local scope for the variables with windows NT shell |
|
|
79 | 79 | if %ERRORLEVEL% equ 0 goto mainEnd |
|
|
80 | 80 | |
|
|
81 | 81 | :fail |
|
|
82 | 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of |
|
|
83 | 83 | rem the _cmd.exe /c_ return code! |
|
|
84 | 84 | set EXIT_CODE=%ERRORLEVEL% |
|
|
85 | 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 |
|
|
86 | 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% |
|
|
87 | 87 | exit /b %EXIT_CODE% |
|
|
88 | 88 | |
|
|
89 | 89 | :mainEnd |
|
|
90 | 90 | if "%OS%"=="Windows_NT" endlocal |
|
|
91 | 91 | |
|
|
92 | 92 | :omega |
|
|
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
