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