builder().add("build").addAll(aspectOptions).addAll(targets)
203 | .build(),
204 | // Strip out the artifact list, keeping the e4b-build.json files.
205 | t -> t.startsWith(">>>") ? (t.endsWith(".e4b-build.json") ? t.substring(3) : "") : null);
206 | }
207 |
208 | /**
209 | * Runs the analysis of the given list of targets using the IDE build information aspect and
210 | * returns a map of {@link IdeBuildInfo}-s (key is the label of the target) containing the
211 | * parsed form of the JSON file created by the aspect.
212 | *
213 | *
214 | * This method cache it results and won't recompute a previously computed version unless
215 | * {@link #markAsDirty()} has been called in between.
216 | *
217 | * @throws BazelNotFoundException
218 | */
219 | public synchronized Map getIdeInfo(Collection targets)
220 | throws IOException, InterruptedException, BazelNotFoundException {
221 | String key = NEW_LINE_JOINER.join(targets);
222 | if (!buildInfoCache.containsKey(key)) {
223 | buildInfoCache.put(key, IdeBuildInfo.getInfo(buildIdeInfo(targets)));
224 | }
225 | return buildInfoCache.get(key);
226 | }
227 |
228 | /**
229 | * Clear the IDE build information cache. This cache is filled upon request and never emptied
230 | * unless we call that function.
231 | *
232 | *
233 | * This function totally clear the cache and that might leads to useless rebuilds when several
234 | * eclipse project points to the same workspace but that is a rare case.
235 | */
236 | public synchronized void markAsDirty() {
237 | buildInfoCache.clear();
238 | }
239 |
240 | /**
241 | * Build a list of targets in the current workspace.
242 | *
243 | * @throws BazelNotFoundException
244 | */
245 | public synchronized int build(List targets, String... extraArgs)
246 | throws IOException, InterruptedException, BazelNotFoundException {
247 | return BazelCommand.this.runBazel(workspaceRoot, ImmutableList.builder().add("build")
248 | .addAll(buildOptions).add(extraArgs).add("--").addAll(targets).build());
249 | }
250 |
251 | /**
252 | * Build a list of targets in the current workspace.
253 | *
254 | * @throws BazelNotFoundException
255 | */
256 | public synchronized int build(List targets, List extraArgs)
257 | throws IOException, InterruptedException, BazelNotFoundException {
258 | return BazelCommand.this.runBazel(workspaceRoot, ImmutableList.builder().add("build")
259 | .addAll(buildOptions).addAll(extraArgs).add("--").addAll(targets).build());
260 | }
261 |
262 | /**
263 | * Run test on a list of targets in the current workspace.
264 | *
265 | * @throws BazelNotFoundException
266 | */
267 | public synchronized int tests(List targets, String... extraArgs)
268 | throws IOException, InterruptedException, BazelNotFoundException {
269 | return BazelCommand.this.runBazel(workspaceRoot, ImmutableList.builder().add("test")
270 | .addAll(buildOptions).add(extraArgs).add("--").addAll(targets).build());
271 | }
272 |
273 | /**
274 | * Returns the workspace root corresponding to this object.
275 | */
276 | public File getWorkspaceRoot() {
277 | return workspaceRoot;
278 | }
279 |
280 | /**
281 | * Returns the execution root of the current workspace.
282 | */
283 | public File getExecRoot() {
284 | return execRoot;
285 | }
286 |
287 | /**
288 | * Gives a list of target completions for the given beginning string. The result is the list of
289 | * possible completion for a target pattern starting with string.
290 | *
291 | * @throws BazelNotFoundException
292 | */
293 | public List complete(String string)
294 | throws IOException, InterruptedException, BazelNotFoundException {
295 | if (string.equals("/") || string.isEmpty()) {
296 | return ImmutableList.of("//");
297 | } else if (string.contains(":")) {
298 | // complete targets using `bazel query`
299 | int idx = string.indexOf(':');
300 | final String packageName = string.substring(0, idx);
301 | final String targetPrefix = string.substring(idx + 1);
302 | ImmutableList.Builder builder = ImmutableList.builder();
303 | builder.addAll(
304 | BazelCommand.this.runBazelAndGetOuputLines(ConsoleType.NO_CONSOLE, workspaceRoot,
305 | ImmutableList.builder().add("query", packageName + ":*").build(), line -> {
306 | int i = line.indexOf(':');
307 | String s = line.substring(i + 1);
308 | return !s.isEmpty() && s.startsWith(targetPrefix) ? (packageName + ":" + s)
309 | : null;
310 | }));
311 | if ("all".startsWith(targetPrefix)) {
312 | builder.add(packageName + ":all");
313 | }
314 | if ("*".startsWith(targetPrefix)) {
315 | builder.add(packageName + ":*");
316 | }
317 | return builder.build();
318 | } else {
319 | // complete packages
320 | int lastSlash = string.lastIndexOf('/');
321 | final String prefix = lastSlash > 0 ? string.substring(0, lastSlash + 1) : "";
322 | final String suffix = lastSlash > 0 ? string.substring(lastSlash + 1) : string;
323 | final String directory = (prefix.isEmpty() || prefix.equals("//")) ? ""
324 | : prefix.substring(string.startsWith("//") ? 2 : 0, prefix.length() - 1);
325 | File file = directory.isEmpty() ? workspaceRoot : new File(workspaceRoot, directory);
326 | ImmutableList.Builder builder = ImmutableList.builder();
327 | File[] files = file.listFiles((f) -> {
328 | // Only give directories whose name starts with suffix...
329 | return f.getName().startsWith(suffix) && f.isDirectory()
330 | // ...that does not start with '.'...
331 | && !f.getName().startsWith(".")
332 | // ...and is not a Bazel convenience link
333 | && (!file.equals(workspaceRoot) || !f.getName().startsWith("bazel-"));
334 | });
335 | if (files != null) {
336 | for (File d : files) {
337 | builder.add(prefix + d.getName() + "/");
338 | if (new File(d, "BUILD").exists()) {
339 | builder.add(prefix + d.getName() + ":");
340 | }
341 | }
342 | }
343 | if ("...".startsWith(suffix)) {
344 | builder.add(prefix + "...");
345 | }
346 | return builder.build();
347 | }
348 | }
349 | }
350 |
351 | private File getWorkspaceRoot(File directory)
352 | throws IOException, InterruptedException, BazelNotFoundException {
353 | List result = runBazelAndGetOuputLines(ConsoleType.SYSTEM, directory,
354 | ImmutableList.of("info", "workspace"));
355 | if (result.size() > 0) {
356 | return new File(result.get(0));
357 | }
358 | return null;
359 | }
360 |
361 | private List runBazelAndGetOuputLines(ConsoleType type, File directory, List args)
362 | throws IOException, InterruptedException, BazelNotFoundException {
363 | return runBazelAndGetOuputLines(type, directory, args, (t) -> t);
364 | }
365 |
366 | private synchronized List runBazelAndGetOuputLines(ConsoleType type, File directory,
367 | List args, Function selector)
368 | throws IOException, InterruptedException, BazelNotFoundException {
369 | Command command = Command.builder(consoleFactory)
370 | .setConsoleName(getConsoleName(type, directory)).setDirectory(directory)
371 | .addArguments(getBazelPath()).addArguments(args).setStdoutLineSelector(selector).build();
372 | if (command.run() == 0) {
373 | return command.getSelectedOutputLines();
374 | }
375 | return ImmutableList.of();
376 | }
377 |
378 | private synchronized List runBazelAndGetErrorLines(ConsoleType type, File directory,
379 | List args, Function selector)
380 | throws IOException, InterruptedException, BazelNotFoundException {
381 | Command command = Command.builder(consoleFactory)
382 | .setConsoleName(getConsoleName(type, directory)).setDirectory(directory)
383 | .addArguments(getBazelPath()).addArguments(args).setStderrLineSelector(selector).build();
384 | if (command.run() == 0) {
385 | return command.getSelectedErrorLines();
386 | }
387 | return ImmutableList.of();
388 | }
389 |
390 | private synchronized int runBazel(ConsoleType type, File directory, List args,
391 | OutputStream stdout, OutputStream stderr)
392 | throws IOException, InterruptedException, BazelNotFoundException {
393 | return Command.builder(consoleFactory).setConsoleName(getConsoleName(type, directory))
394 | .setDirectory(directory).addArguments(getBazelPath()).addArguments(args)
395 | .setStandardOutput(stdout).setStandardError(stderr).build().run();
396 | }
397 |
398 | private int runBazel(File directory, List args)
399 | throws IOException, InterruptedException, BazelNotFoundException {
400 | return runBazel(ConsoleType.WORKSPACE, directory, args, null, null);
401 | }
402 |
403 | private String getConsoleName(ConsoleType type, File directory) {
404 | switch (type) {
405 | case SYSTEM:
406 | return "Bazel [system]";
407 | case WORKSPACE:
408 | return "Bazel [" + directory.toString() + "]";
409 | case NO_CONSOLE:
410 | default:
411 | return null;
412 | }
413 | }
414 |
415 | }
416 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/command/BazelNotFoundException.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.command;
16 |
17 | public class BazelNotFoundException extends Exception {
18 | private static final long serialVersionUID = 1L;
19 |
20 | private BazelNotFoundException(String msg) {
21 | super(msg);
22 | }
23 |
24 | public static final class BazelNotSetException extends BazelNotFoundException {
25 | private static final long serialVersionUID = 1L;
26 |
27 | public BazelNotSetException() {
28 | super("Path to Bazel binary is not set, please set it "
29 | + "(Preferences... > Bazel Plugins Preferences)");
30 | }
31 | }
32 |
33 | public static final class BazelNotExecutableException extends BazelNotFoundException {
34 | private static final long serialVersionUID = 1L;
35 |
36 | public BazelNotExecutableException() {
37 | super("Path to Bazel is wrong (does not point to a binary), please set it "
38 | + "(Preferences... > Bazel Plugins Preferences)");
39 | }
40 | }
41 |
42 | public static final class BazelTooOldException extends BazelNotFoundException {
43 | private static final long serialVersionUID = 1L;
44 |
45 | public BazelTooOldException(String version) {
46 | super("Bazel version (" + version + ") is unsupported (too old or development version), "
47 | + "please update your Bazel binary.");
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/command/Command.java:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.command;
16 |
17 | import java.io.File;
18 | import java.io.IOException;
19 | import java.io.InputStream;
20 | import java.io.OutputStream;
21 | import java.util.function.Function;
22 |
23 | import com.google.common.base.Preconditions;
24 | import com.google.common.collect.ImmutableList;
25 | import com.google.devtools.bazel.e4b.command.CommandConsole.CommandConsoleFactory;
26 |
27 | /**
28 | * A utility class to spawn a command and parse its output. It allow to filter the output,
29 | * redirecting part of it to the console and getting the rest in a list of string.
30 | *
31 | *
32 | * This class can only be initialized using a builder created with the {@link #builder()} method.
33 | */
34 | final public class Command {
35 |
36 | private final File directory;
37 | private final ImmutableList args;
38 | private final SelectOutputStream stdout;
39 | private final SelectOutputStream stderr;
40 | private boolean executed = false;
41 |
42 | private Command(CommandConsole console, File directory, ImmutableList args,
43 | Function stdoutSelector, Function stderrSelector,
44 | OutputStream stdout, OutputStream stderr) throws IOException {
45 | this.directory = directory;
46 | this.args = args;
47 | if (console != null) {
48 | if (stdout == null) {
49 | stdout = console.createOutputStream();
50 | }
51 | if (stderr == null) {
52 | stderr = console.createErrorStream();
53 | }
54 | }
55 | this.stderr = new SelectOutputStream(stderr, stderrSelector);
56 | this.stdout = new SelectOutputStream(stdout, stdoutSelector);
57 | }
58 |
59 | /**
60 | * Executes the command represented by this instance, and return the exit code of the command.
61 | * This method should not be called twice on the same object.
62 | */
63 | public int run() throws IOException, InterruptedException {
64 | Preconditions.checkState(!executed);
65 | executed = true;
66 | ProcessBuilder builder = new ProcessBuilder(args);
67 | builder.directory(directory);
68 | builder.redirectOutput(ProcessBuilder.Redirect.PIPE);
69 | builder.redirectError(ProcessBuilder.Redirect.PIPE);
70 | Process process = builder.start();
71 | Thread err = copyStream(process.getErrorStream(), stderr);
72 | // seriously? That's stdout, why is it called getInputStream???
73 | Thread out = copyStream(process.getInputStream(), stdout);
74 | int r = process.waitFor();
75 | if (err != null) {
76 | err.join();
77 | }
78 | if (out != null) {
79 | out.join();
80 | }
81 | synchronized (stderr) {
82 | stderr.close();
83 | }
84 | synchronized (stdout) {
85 | stdout.close();
86 | }
87 | return r;
88 | }
89 |
90 | private static class CopyStreamRunnable implements Runnable {
91 | private InputStream inputStream;
92 | private OutputStream outputStream;
93 |
94 | CopyStreamRunnable(InputStream inputStream, OutputStream outputStream) {
95 | this.inputStream = inputStream;
96 | this.outputStream = outputStream;
97 | }
98 |
99 | @Override
100 | public void run() {
101 | byte[] buffer = new byte[4096];
102 | int read;
103 | try {
104 | while ((read = inputStream.read(buffer)) > 0) {
105 | synchronized (outputStream) {
106 | outputStream.write(buffer, 0, read);
107 | }
108 | }
109 | } catch (IOException ex) {
110 | // we simply terminate the thread on exceptions
111 | }
112 | }
113 | }
114 |
115 | // Launch a thread to copy all data from inputStream to outputStream
116 | private static Thread copyStream(InputStream inputStream, OutputStream outputStream) {
117 | if (outputStream != null) {
118 | Thread t = new Thread(new CopyStreamRunnable(inputStream, outputStream), "CopyStream");
119 | t.start();
120 | return t;
121 | }
122 | return null;
123 | }
124 |
125 | /**
126 | * Returns the list of lines selected from the standard error stream. Lines printed to the
127 | * standard error stream by the executed command can be filtered to be added to that list.
128 | *
129 | * @see {@link Builder#setStderrLineSelector(Function)}
130 | */
131 | public ImmutableList getSelectedErrorLines() {
132 | return stderr.getLines();
133 | }
134 |
135 | /**
136 | * Returns the list of lines selected from the standard output stream. Lines printed to the
137 | * standard output stream by the executed command can be filtered to be added to that list.
138 | *
139 | * @see {@link Builder#setStdoutLineSelector(Function)}
140 | */
141 | public ImmutableList getSelectedOutputLines() {
142 | return stdout.getLines();
143 | }
144 |
145 | /**
146 | * A builder class to generate a Command object.
147 | */
148 | public static class Builder {
149 |
150 | private String consoleName = null;
151 | private File directory;
152 | private ImmutableList.Builder args = ImmutableList.builder();
153 | private OutputStream stdout = null;
154 | private OutputStream stderr = null;
155 | private Function stdoutSelector;
156 | private Function stderrSelector;
157 | private final CommandConsoleFactory consoleFactory;
158 |
159 | private Builder(final CommandConsoleFactory consoleFactory) {
160 | // Default to the current working directory
161 | this.directory = new File(System.getProperty("user.dir"));
162 | this.consoleFactory = consoleFactory;
163 | }
164 |
165 | /**
166 | * Set the console name.
167 | *
168 | *
169 | * The console name is used to print result of the program. Only lines not filtered by
170 | * {@link #setStderrLineSelector(Function)} and {@link #setStdoutLineSelector(Function)} are
171 | * printed to the console. If {@link #setStandardError(OutputStream)} or
172 | * {@link #setStandardOutput(OutputStream)} have been used with a non null value, then they
173 | * intercept all output from being printed to the console.
174 | *
175 | *
176 | * If name is null, no output is written to any console.
177 | */
178 | public Builder setConsoleName(String name) {
179 | this.consoleName = name;
180 | return this;
181 | }
182 |
183 | /**
184 | * Set the working directory for the program, it is set to the current working directory of the
185 | * current java process by default.
186 | */
187 | public Builder setDirectory(File directory) {
188 | this.directory = directory;
189 | return this;
190 | }
191 |
192 | /**
193 | * Set an {@link OutputStream} to receive non selected lines from the standard output stream of
194 | * the program in lieu of the console. If a selector has been set with
195 | * {@link #setStdoutLineSelector(Function)}, only the lines not selected (for which the selector
196 | * returns null) will be printed to the {@link OutputStream}.
197 | */
198 | public Builder setStandardOutput(OutputStream stdout) {
199 | this.stdout = stdout;
200 | return this;
201 | }
202 |
203 | /**
204 | * Set an {@link OutputStream} to receive non selected lines from the standard error stream of
205 | * the program in lieu of the console. If a selector has been set with
206 | * {@link #setStderrLineSelector(Function)}, only the lines not selected (for which the selector
207 | * returns null) will be printed to the {@link OutputStream}.
208 | */
209 | public Builder setStandardError(OutputStream stderr) {
210 | this.stderr = stderr;
211 | return this;
212 | }
213 |
214 | /**
215 | * Add arguments to the command line. The first argument to be added to the builder is the
216 | * program name.
217 | */
218 | public Builder addArguments(String... args) {
219 | this.args.add(args);
220 | return this;
221 | }
222 |
223 | /**
224 | * Add a list of arguments to the command line. The first argument to be added to the builder is
225 | * the program name.
226 | */
227 | public Builder addArguments(Iterable args) {
228 | this.args.addAll(args);
229 | return this;
230 | }
231 |
232 | /**
233 | * Set a selector to accumulate lines that are selected from the standard output stream.
234 | *
235 | *
236 | * The selector is passed all lines that are printed to the standard output. It can either
237 | * returns null to say that the line should be passed to the console or to a non null value that
238 | * will be stored. All values that have been selected (for which the selector returns a non-null
239 | * value) will be stored in a list accessible through {@link Command#getSelectedOutputLines()}.
240 | * The selected lines will not be printed to the console.
241 | */
242 | public Builder setStdoutLineSelector(Function selector) {
243 | this.stdoutSelector = selector;
244 | return this;
245 | }
246 |
247 | /**
248 | * Set a selector to accumulate lines that are selected from the standard error stream.
249 | *
250 | *
251 | * The selector is passed all lines that are printed to the standard error. It can either
252 | * returns null to say that the line should be passed to the console or to a non null value that
253 | * will be stored. All values that have been selected (for which the selector returns a non-null
254 | * value) will be stored in a list accessible through {@link Command#getSelectedErrorLines()}.
255 | * The selected lines will not be printed to the console.
256 | */
257 | public Builder setStderrLineSelector(Function selector) {
258 | this.stderrSelector = selector;
259 | return this;
260 | }
261 |
262 | /**
263 | * Build a Command object.
264 | */
265 | public Command build() throws IOException {
266 | Preconditions.checkNotNull(directory);
267 | ImmutableList args = this.args.build();
268 | CommandConsole console = consoleName == null ? null
269 | : consoleFactory.get(consoleName,
270 | "Running " + String.join(" ", args) + " from " + directory.toString());
271 | return new Command(console, directory, args, stdoutSelector, stderrSelector,
272 | stdout, stderr);
273 | }
274 | }
275 |
276 | /**
277 | * Returns a {@link Builder} object to use to create a {@link Command} object.
278 | */
279 | public static Builder builder(CommandConsoleFactory consoleFactory) {
280 | return new Builder(consoleFactory);
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/command/CommandConsole.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.command;
16 |
17 | import java.io.IOException;
18 | import java.io.OutputStream;
19 |
20 | /**
21 | * An interface to describe an output console to stream output from a command. A command console
22 | * should be able to provide output stream for both normal ouput and error output.
23 | */
24 | public interface CommandConsole {
25 |
26 | /** Create an {@link OuputStream} suitable to print standard output of a command. */
27 | OutputStream createOutputStream();
28 |
29 | /** Create an {@link OuputStream} suitable to print standard error output of a command. */
30 | OutputStream createErrorStream();
31 |
32 | /** A factory that returns a command console by name */
33 | interface CommandConsoleFactory {
34 | /**
35 | * Returns a {@link CommandConsole} that has the name {@code name}. {@code title} will be
36 | * written at the beginning of the console.
37 | */
38 | CommandConsole get(String name, String title) throws IOException;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/command/IdeBuildInfo.java:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.command;
16 |
17 | import java.io.FileInputStream;
18 | import java.io.IOException;
19 | import java.util.List;
20 | import java.util.Objects;
21 |
22 | import org.json.JSONArray;
23 | import org.json.JSONObject;
24 | import org.json.JSONTokener;
25 |
26 | import com.google.common.annotations.VisibleForTesting;
27 | import com.google.common.base.Joiner;
28 | import com.google.common.collect.ImmutableList;
29 | import com.google.common.collect.ImmutableMap;
30 |
31 | /**
32 | * A parsed version of the JSON files returned by the application of the IDE build information
33 | * aspect.
34 | */
35 | public final class IdeBuildInfo {
36 |
37 | private static final Joiner COMMA_JOINER = Joiner.on(",");
38 |
39 | /**
40 | * A structure containing the list of jar files generated by a target (interface, class and source
41 | * jars).
42 | */
43 | public static final class Jars {
44 | private final String ijar; // interface_jar
45 | private final String jar; // jar
46 | private final String srcjar; // source_jar
47 |
48 | Jars(JSONObject obj) {
49 | this.ijar = obj.has("interface_jar") ? obj.getString("interface_jar") : null;
50 | this.jar = obj.getString("jar");
51 | this.srcjar = obj.has("srcjar") ? obj.getString("srcjar") : null;
52 | }
53 |
54 | @Override
55 | public String toString() {
56 | StringBuffer builder = new StringBuffer();
57 | builder.append("Jars(jar = ").append(jar);
58 | if (ijar != null) {
59 | builder.append(", ijar = ").append(ijar);
60 | }
61 | if (srcjar != null) {
62 | builder.append(", srcjar = ").append(srcjar);
63 | }
64 | return builder.append(")").toString();
65 | }
66 |
67 | @Override
68 | public int hashCode() {
69 | return Objects.hash(ijar, jar, srcjar);
70 | }
71 |
72 | public String getInterfaceJar() {
73 | return ijar;
74 | }
75 |
76 | public String getJar() {
77 | return jar;
78 | }
79 |
80 | public String getSrcJar() {
81 | return srcjar;
82 | }
83 | }
84 |
85 | private final String location; // build_file_artifact_location
86 | private final ImmutableList deps; // dependencies
87 | private final String kind; // kind
88 | private final String label; // label
89 |
90 | private final ImmutableList generatedJars; // generated_jars
91 | private final ImmutableList jars; // jars
92 | private final ImmutableList sources; // sources
93 |
94 | /**
95 | * Construct an {@link IdeBuildInfo} object from a {@link JSONObject}.
96 | */
97 | IdeBuildInfo(JSONObject object) {
98 | jars = jsonToJarArray(object.getJSONArray("jars"));
99 | generatedJars = jsonToJarArray(object.getJSONArray("generated_jars"));
100 | location = object.getString("build_file_artifact_location");
101 | kind = object.getString("kind");
102 | label = object.getString("label");
103 | this.deps = jsonToStringArray(object.getJSONArray("dependencies"));
104 | this.sources = jsonToStringArray(object.getJSONArray("sources"));
105 | }
106 |
107 | @Override
108 | public String toString() {
109 | StringBuffer builder = new StringBuffer();
110 | builder.append("IdeBuildInfo(\n");
111 | builder.append(" label = ").append(label).append(",\n");
112 | builder.append(" location = ").append(location).append(",\n");
113 | builder.append(" kind = ").append(kind).append(",\n");
114 | builder.append(" jars = [").append(COMMA_JOINER.join(jars)).append("],\n");
115 | builder.append(" generatedJars = [").append(COMMA_JOINER.join(generatedJars)).append("],\n");
116 | builder.append(" deps = [").append(COMMA_JOINER.join(deps)).append("],\n");
117 | builder.append(" sources = [").append(COMMA_JOINER.join(sources)).append("])");
118 | return builder.toString();
119 | }
120 |
121 | /**
122 | * Constructs a map of label -> {@link IdeBuildInfo} from a list of files, parsing each files into
123 | * a {@link JSONObject} and then converting that {@link JSONObject} to an {@link IdeBuildInfo}
124 | * object.
125 | */
126 | @VisibleForTesting
127 | public static ImmutableMap getInfo(List files)
128 | throws IOException, InterruptedException {
129 | ImmutableMap.Builder infos = ImmutableMap.builder();
130 | for (String s : files) {
131 | if (!s.isEmpty()) {
132 | IdeBuildInfo buildInfo =
133 | new IdeBuildInfo(new JSONObject(new JSONTokener(new FileInputStream(s))));
134 | infos.put(buildInfo.label, buildInfo);
135 | }
136 | }
137 | return infos.build();
138 | }
139 |
140 | private ImmutableList jsonToJarArray(JSONArray array) {
141 | ImmutableList.Builder builder = ImmutableList.builder();
142 | for (Object o : array) {
143 | builder.add(new Jars((JSONObject) o));
144 | }
145 | return builder.build();
146 | }
147 |
148 | private ImmutableList jsonToStringArray(JSONArray array) {
149 | ImmutableList.Builder builder = ImmutableList.builder();
150 | for (Object o : array) {
151 | builder.add(o.toString());
152 | }
153 | return builder.build();
154 | }
155 |
156 | /**
157 | * Location of the target (build file).
158 | */
159 | public String getLocation() {
160 | return location;
161 | }
162 |
163 | /**
164 | * List of dependencies of the target.
165 | */
166 | public List getDeps() {
167 | return deps;
168 | }
169 |
170 | /**
171 | * Kind of the target (e.g., java_test or java_binary).
172 | */
173 | public String getKind() {
174 | return kind;
175 | }
176 |
177 | /**
178 | * Label of the target.
179 | */
180 | public String getLabel() {
181 | return label;
182 | }
183 |
184 | /**
185 | * List of jars generated by annotations processors when building this target.
186 | */
187 | public List getGeneratedJars() {
188 | return generatedJars;
189 | }
190 |
191 | /**
192 | * List of jars generated by building this target.
193 | */
194 | public List getJars() {
195 | return jars;
196 | }
197 |
198 | /**
199 | * List of sources consumed by this target.
200 | */
201 | public List getSources() {
202 | return sources;
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/command/SelectOutputStream.java:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.command;
16 |
17 | import java.io.ByteArrayOutputStream;
18 | import java.io.IOException;
19 | import java.io.OutputStream;
20 | import java.io.UnsupportedEncodingException;
21 | import java.nio.charset.StandardCharsets;
22 | import java.util.LinkedList;
23 | import java.util.List;
24 | import java.util.function.Function;
25 |
26 | import com.google.common.base.Preconditions;
27 | import com.google.common.collect.ImmutableList;
28 |
29 | /**
30 | * A wrapper output stream to output part of the result to a given output and extracting the other
31 | * part with a selector function. The other part is return as a list of string.
32 | */
33 | public class SelectOutputStream extends OutputStream {
34 |
35 | private OutputStream output;
36 | private Function selector;
37 | private boolean closed = false;
38 | private List lines = new LinkedList<>();
39 | private ByteArrayOutputStream stream = new ByteArrayOutputStream();
40 |
41 | /**
42 | * Create a SelectOutputStream. output is the output stream where non-selected lines
43 | * will be printed. selector is a function that will be called on each line. If
44 | * selector
returns a non null value, then the resulting value will be stored in a
45 | * lines buffer that can be consumed with the {@link #getLines()} method. If selector
46 | * returns a null value, the corresponding line will be send to output
.
47 | *
48 | *
49 | * Both output
and selector
can be null. If output
is null,
50 | * unselected lines will be discarded. If selector
is null, all lines will be
51 | * considered as unselected.
52 | */
53 | public SelectOutputStream(OutputStream output, Function selector) {
54 | super();
55 | this.output = output;
56 | this.selector = selector;
57 | }
58 |
59 | @Override
60 | public void write(int b) throws IOException {
61 | Preconditions.checkState(!closed, "Attempted to write on a closed stream");
62 | byte b0 = (byte) b;
63 | if (b0 == '\n') {
64 | select(true);
65 | } else {
66 | stream.write(b);
67 | }
68 | }
69 |
70 | private void select(boolean appendNewLine) throws UnsupportedEncodingException, IOException {
71 | String line = null;
72 | if (selector != null) {
73 | line = selector.apply(stream.toString(StandardCharsets.UTF_8.name()));
74 | }
75 |
76 | if (line != null) {
77 | lines.add(line);
78 | } else if (output != null) {
79 | if (appendNewLine) {
80 | stream.write('\n');
81 | }
82 | output.write(stream.toByteArray());
83 | }
84 | stream.reset();
85 | }
86 |
87 | @Override
88 | public void close() throws IOException {
89 | Preconditions.checkState(!closed);
90 | super.close();
91 | select(false);
92 | closed = true;
93 | }
94 |
95 | /**
96 | * Returns the list of selected lines.
97 | */
98 | ImmutableList getLines() {
99 | return ImmutableList.copyOf(lines);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/preferences/BazelPreferenceInitializer.java:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.preferences;
16 |
17 | import java.io.File;
18 |
19 | import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
20 | import org.eclipse.jface.preference.IPreferenceStore;
21 |
22 | import com.google.devtools.bazel.e4b.Activator;
23 |
24 | /**
25 | * Initialize the preferences of Bazel. The only preferences stored for now is the path to the Bazel
26 | * binary, which is expected to be in /usr/local/bin/bazel by default.
27 | */
28 | public class BazelPreferenceInitializer extends AbstractPreferenceInitializer {
29 |
30 | private static String which(String name, String def) {
31 | for (String dirname : System.getenv("PATH").split(File.pathSeparator)) {
32 | File file = new File(dirname, name);
33 | if (file.isFile() && file.canExecute()) {
34 | return file.getAbsolutePath();
35 | }
36 | }
37 | return def;
38 | }
39 |
40 | @Override
41 | public void initializeDefaultPreferences() {
42 | IPreferenceStore store = Activator.getDefault().getPreferenceStore();
43 | store.setDefault("BAZEL_PATH", which("bazel", "/usr/local/bin/bazel"));
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/preferences/BazelPreferencePage.java:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.preferences;
16 |
17 | import com.google.devtools.bazel.e4b.Activator;
18 | import com.google.devtools.bazel.e4b.command.BazelNotFoundException;
19 | import org.eclipse.jface.preference.FieldEditorPreferencePage;
20 | import org.eclipse.jface.preference.FileFieldEditor;
21 | import org.eclipse.swt.widgets.Composite;
22 | import org.eclipse.ui.IWorkbench;
23 | import org.eclipse.ui.IWorkbenchPreferencePage;
24 |
25 | /**
26 | * Page to configure the e4b plugin. The only configuration parameter is the path to the Bazel
27 | * binary so this page provide a file field to specify it.
28 | */
29 | public class BazelPreferencePage extends FieldEditorPreferencePage
30 | implements
31 | IWorkbenchPreferencePage {
32 |
33 | private static class BazelBinaryFieldEditor extends FileFieldEditor {
34 | BazelBinaryFieldEditor(Composite parent) {
35 | super("BAZEL_PATH", "Path to the &Bazel binary:", true, parent);
36 | setValidateStrategy(VALIDATE_ON_KEY_STROKE);
37 | }
38 |
39 | @Override
40 | protected boolean doCheckState() {
41 | try {
42 | Activator.getDefault().getCommand().checkVersion(getTextControl().getText());
43 | return true;
44 | } catch (BazelNotFoundException e) {
45 | setErrorMessage(e.getMessage());
46 | return false;
47 | }
48 | }
49 | }
50 |
51 | public BazelPreferencePage() {
52 | super(GRID);
53 | }
54 |
55 | public void createFieldEditors() {
56 | addField(new BazelBinaryFieldEditor(getFieldEditorParent()));
57 | }
58 |
59 | @Override
60 | public void init(IWorkbench workbench) {
61 | setPreferenceStore(Activator.getDefault().getPreferenceStore());
62 | setDescription("Bazel plugin settings");
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/projectviews/BUILD:
--------------------------------------------------------------------------------
1 | java_library(
2 | name = "projectviews",
3 | srcs = glob(["*.java"]),
4 | visibility = [
5 | "//:__pkg__",
6 | "//javatests/com/google/devtools/bazel/e4b/projectviews:__pkg__",
7 | ],
8 | deps = [
9 | "@com_google_guava//jar",
10 | ],
11 | )
12 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/projectviews/Builder.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.projectviews;
16 |
17 | import java.io.BufferedReader;
18 | import java.io.File;
19 | import java.io.IOException;
20 | import java.io.InputStreamReader;
21 | import java.net.URL;
22 | import java.nio.file.Files;
23 |
24 | import com.google.common.base.Preconditions;
25 | import com.google.common.collect.ImmutableList;
26 |
27 | /** A class to create a {@link ProjectView} */
28 | public class Builder {
29 | private ImmutableList.Builder buildFlags = ImmutableList.builder();
30 | private ImmutableList.Builder directories = ImmutableList.builder();
31 | private ImmutableList.Builder targets = ImmutableList.builder();
32 | private int javaLanguageLevel = 0; // <= 0 means take it from java_toolchain.
33 |
34 | Builder() {}
35 |
36 | public Builder addBuildFlag(String... flag) {
37 | buildFlags.add(flag);
38 | return this;
39 | }
40 |
41 | public Builder addDirectory(String... dir) {
42 | directories.add(dir);
43 | return this;
44 | }
45 |
46 | public Builder addTarget(String... target) {
47 | targets.add(target);
48 | return this;
49 | }
50 |
51 | public Builder setJavaLanguageLevel(int level) {
52 | Preconditions.checkArgument(level > 0, "Can only set java language level to a value > 0");
53 | Preconditions.checkArgument(javaLanguageLevel == 0, "Java language level was already set");
54 | javaLanguageLevel = level;
55 | return this;
56 | }
57 |
58 | // State object using while parsing a stream
59 | private String currentSection = null;
60 |
61 | /**
62 | * Parse the project view given in view, following also imports.
63 | *
64 | * @throws IOException
65 | * @throws ProjectViewParseException
66 | */
67 | public Builder parseView(File view) throws IOException, ProjectViewParseException {
68 | int linenb = 0;
69 | for (String line : Files.readAllLines(view.toPath())) {
70 | linenb++;
71 | parseLine(view.getPath(), view.getParentFile(), line, linenb);
72 | }
73 | currentSection = null;
74 | return this;
75 | }
76 |
77 | /**
78 | * Parse the project view at the given {@code url}.
79 | *
80 | * @throws IOException
81 | * @throws ProjectViewParseException
82 | */
83 | public Builder parseView(URL url) throws IOException, ProjectViewParseException {
84 | int linenb = 0;
85 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
86 | for (String line : reader.lines().toArray(String[]::new)) {
87 | linenb++;
88 | parseLine(url.toString(), null, line, linenb);
89 | }
90 | }
91 | currentSection = null;
92 | return this;
93 | }
94 |
95 | private void parseLine(String fileName, File parentFile, String line, int linenb)
96 | throws ProjectViewParseException, IOException {
97 | if (line.isEmpty()) {
98 | currentSection = null;
99 | } else if (line.startsWith(" ")) {
100 | if (currentSection == null) {
101 | throw new ProjectViewParseException(
102 | "Line " + linenb + " of project view " + fileName + " is not in a section");
103 | }
104 | if (currentSection.equals("directories")) {
105 | directories.add(line.substring(2));
106 | } else if (currentSection.equals("targets")) {
107 | targets.add(line.substring(2));
108 | } else if (currentSection.equals("build_flags")) {
109 | buildFlags.add(line.substring(2));
110 | } // else ignoring other sections
111 | } else if (line.startsWith("import ")) {
112 | // imports
113 | String path = line.substring(7);
114 | if (path.startsWith("/")) {
115 | parseView(new File(path));
116 | } else {
117 | parseView(new File(parentFile, path));
118 | }
119 | } else if (line.contains(":")) {
120 | // section declaration
121 | line = line.trim();
122 | if (line.endsWith(":")) {
123 | currentSection = line.substring(0, line.length() - 1);
124 | if (currentSection.equals("java_language_level")) {
125 | throw new ProjectViewParseException("Line " + linenb + " of project view "
126 | + fileName + ": java_language_level cannot be a section name");
127 | }
128 | } else {
129 | int colonIndex = line.indexOf(':');
130 | String label = line.substring(0, colonIndex).trim();
131 | String value = line.substring(colonIndex + 1).trim();
132 | if (label.equals("directories") || label.equals("import") || label.equals("targets")
133 | || label.equals("build_flags")) {
134 | throw new ProjectViewParseException("Line " + linenb + " of project view "
135 | + fileName + ": " + label + " cannot be a label name");
136 | }
137 | if (label.equals("java_language_level")) {
138 | if (!value.matches("^[0-9]+$")) {
139 | throw new ProjectViewParseException("Line " + linenb + " of project view "
140 | + fileName + ": java_language_level should be an integer.");
141 | }
142 | javaLanguageLevel = Integer.parseInt(value);
143 | }
144 | }
145 | } else if (!line.trim().startsWith("#")) {
146 | throw new ProjectViewParseException(
147 | "Project view " + fileName + " contains a syntax error at line " + linenb);
148 | }
149 | }
150 |
151 | public ProjectView build() {
152 | return new ProjectViewImpl(directories.build(), targets.build(), javaLanguageLevel,
153 | buildFlags.build());
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/projectviews/ProjectView.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.projectviews;
16 |
17 | import java.util.List;
18 |
19 | /**
20 | * This is an interface to defined a project view from the IntelliJ plugin for Bazel
21 | * (https://ij.bazel.build) so that project view can be shared between IntelliJ and Eclipse users.
22 | *
23 | *
24 | * See http://ij.bazel.build/docs/project-views.html for the specification of the project view. This
25 | * project view support only a subset relevant for the Eclipse plugin.
26 | */
27 | public interface ProjectView {
28 | /**
29 | * List of directories defined in the {@code directories} section of the project view. These are
30 | * the directories to include as source directories.
31 | */
32 | public List getDirectories();
33 |
34 | /**
35 | * List of targets to build defined in the {@code targets} section of the project view.
36 | */
37 | public List getTargets();
38 |
39 | /**
40 | * Return a number (e.g. 7) giving the java version that the IDE should support (section
41 | * {@code java_language_level} of the project view).
42 | */
43 | public int getJavaLanguageLevel();
44 |
45 | /**
46 | * List of build flags to pass to Bazel defined in the {@code build_flags} section of the project
47 | * view.
48 | */
49 | public List getBuildFlags();
50 |
51 | /** Returns a builder to construct an project view object. */
52 | public static Builder builder() {
53 | return new Builder();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/projectviews/ProjectViewImpl.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.projectviews;
16 |
17 | import com.google.common.collect.ImmutableList;
18 | import java.util.List;
19 |
20 | /** Implementation of the ProjectView object */
21 | final class ProjectViewImpl implements ProjectView {
22 |
23 | private final ImmutableList directories;
24 | private final ImmutableList targets;
25 | private final int javaLanguageLevel;
26 | private final ImmutableList buildFlags;
27 |
28 | public ProjectViewImpl(List directories, List targets, int javaLanguageLevel,
29 | List buildFlags) {
30 | this.directories = ImmutableList.copyOf(directories);
31 | this.targets = ImmutableList.copyOf(targets);
32 | this.javaLanguageLevel = javaLanguageLevel;
33 | this.buildFlags = ImmutableList.copyOf(buildFlags);
34 | }
35 |
36 | @Override
37 | public List getDirectories() {
38 | return directories;
39 | }
40 |
41 | @Override
42 | public List getTargets() {
43 | return targets;
44 | }
45 |
46 | @Override
47 | public int getJavaLanguageLevel() {
48 | return javaLanguageLevel;
49 | }
50 |
51 | @Override
52 | public List getBuildFlags() {
53 | return buildFlags;
54 | }
55 |
56 | @Override
57 | public String toString() {
58 | StringBuffer result = new StringBuffer();
59 | addList(result, directories, "directories");
60 | addList(result, targets, "targets");
61 | if (javaLanguageLevel > 0) {
62 | result.append("java_language_level: ").append(javaLanguageLevel).append("\n\n");
63 | }
64 | addList(result, buildFlags, "build_flags");
65 | return result.toString();
66 | }
67 |
68 | private static void addList(StringBuffer result, List list, String header) {
69 | if (!list.isEmpty()) {
70 | result.append(header).append(":\n");
71 | for (String el : list) {
72 | result.append(" ").append(el).append("\n");
73 | }
74 | result.append("\n");
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/projectviews/ProjectViewParseException.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.projectviews;
16 |
17 | /**
18 | * An exception thrown when failing to parse a project view.
19 | */
20 | public class ProjectViewParseException extends Exception {
21 | private static final long serialVersionUID = 1L;
22 |
23 | ProjectViewParseException(String msg) {
24 | super(msg);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/wizard/BazelTargetCompletionContentProposalProvider.java:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.wizard;
16 |
17 | import java.io.IOException;
18 | import java.util.List;
19 |
20 | import org.eclipse.jface.fieldassist.ContentProposal;
21 | import org.eclipse.jface.fieldassist.IContentProposal;
22 | import org.eclipse.jface.fieldassist.IContentProposalProvider;
23 |
24 | import com.google.devtools.bazel.e4b.Activator;
25 | import com.google.devtools.bazel.e4b.command.BazelCommand.BazelInstance;
26 | import com.google.devtools.bazel.e4b.command.BazelNotFoundException;
27 |
28 | /**
29 | * A {@link IContentProposalProvider} to provide completion for Bazel. Use the
30 | * {@link #setBazelInstance(BazelInstance)} method to provide with the {@link BazelInstance}
31 | * interface to Bazel.
32 | */
33 | public class BazelTargetCompletionContentProposalProvider implements IContentProposalProvider {
34 |
35 | private BazelInstance bazel = null;
36 |
37 | @Override
38 | public IContentProposal[] getProposals(String contents, int position) {
39 | if (bazel == null) {
40 | return null;
41 | }
42 | try {
43 | List completions = bazel.complete(contents.substring(0, position));
44 | if (completions != null) {
45 | IContentProposal[] result = new IContentProposal[completions.size()];
46 | int i = 0;
47 | for (String s : completions) {
48 | result[i] = new ContentProposal(s);
49 | i++;
50 | }
51 | return result;
52 | }
53 | } catch (IOException e) {
54 | Activator.error("Failed to run Bazel to get completion information", e);
55 | } catch (InterruptedException e) {
56 | Activator.error("Bazel was interrupted", e);
57 | } catch (BazelNotFoundException e) {
58 | Activator.error("Bazel not found: " + e.getMessage());
59 | }
60 | return null;
61 | }
62 |
63 | /**
64 | * Set the {@link BazelInstance} to use to query for completion targets.
65 | */
66 | public void setBazelInstance(BazelInstance bazel) {
67 | this.bazel = bazel;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/wizard/BazelWizard.java:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.wizard;
16 |
17 | import org.eclipse.jface.viewers.IStructuredSelection;
18 | import org.eclipse.jface.wizard.Wizard;
19 | import org.eclipse.ui.IWorkbench;
20 | import org.eclipse.ui.IWorkbenchWizard;
21 | import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
22 |
23 | import com.google.devtools.bazel.e4b.BazelProjectSupport;
24 |
25 | /**
26 | * A wizard to create a Bazel import project.
27 | */
28 | public class BazelWizard extends Wizard implements IWorkbenchWizard {
29 |
30 | protected WorkspaceWizardPage page2;
31 | protected WizardNewProjectCreationPage page1;
32 |
33 | @Override
34 | public void addPages() {
35 | page1 = new WizardNewProjectCreationPage("Creating a new Bazel import project");
36 | page2 = new WorkspaceWizardPage();
37 | addPage(page1);
38 | addPage(page2);
39 | }
40 |
41 | @Override
42 | public String getWindowTitle() {
43 | return "Import Bazel Workspace...";
44 | }
45 |
46 | @Override
47 | public boolean performFinish() {
48 | BazelProjectSupport.createProject(page1.getProjectName(), page1.getLocationURI(),
49 | page2.getWorkspaceRoot(), page2.getDirectories(), page2.getTargets(),
50 | page2.getJavaLanguageVersion());
51 | return true;
52 | }
53 |
54 | @Override
55 | public void init(IWorkbench workbench, IStructuredSelection selection) {}
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/wizard/DirectoryTreeContentProvider.java:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.wizard;
16 |
17 | import java.io.File;
18 |
19 | import org.eclipse.jface.viewers.CheckboxTreeViewer;
20 | import org.eclipse.jface.viewers.ILabelProvider;
21 | import org.eclipse.jface.viewers.ILabelProviderListener;
22 | import org.eclipse.jface.viewers.ITreeContentProvider;
23 | import org.eclipse.jface.viewers.Viewer;
24 | import org.eclipse.swt.SWT;
25 | import org.eclipse.swt.graphics.Image;
26 | import org.eclipse.swt.widgets.Composite;
27 |
28 | import com.google.common.collect.ImmutableList;
29 |
30 | /**
31 | * A tree content provider that enable selecting a list of sub-directories of a directory root (the
32 | * Bazel workspace root).
33 | */
34 | public class DirectoryTreeContentProvider implements ITreeContentProvider {
35 |
36 | private File root;
37 |
38 | public DirectoryTreeContentProvider(File root) {
39 | this.root = root;
40 | }
41 |
42 | @Override
43 | public void dispose() {}
44 |
45 | @Override
46 | public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {}
47 |
48 | @Override
49 | public Object[] getElements(Object inputElement) {
50 | if (root == null) {
51 | return new Object[] {};
52 | }
53 | // We only have one root
54 | return new Object[] {root};
55 | }
56 |
57 | @Override
58 | public Object[] getChildren(Object parentElement) {
59 | return ((File) parentElement).listFiles((f) -> (f.isDirectory() && !f.getName().startsWith(".")
60 | && !f.getName().startsWith("bazel-")));
61 | }
62 |
63 | @Override
64 | public Object getParent(Object element) {
65 | File file = (File) element;
66 | if (file.equals(root)) {
67 | return null;
68 | }
69 | return file.getParentFile();
70 | }
71 |
72 | @Override
73 | public boolean hasChildren(Object element) {
74 | Object[] childrens = getChildren(element);
75 | return (childrens != null) && (childrens.length > 0);
76 | }
77 |
78 | public File getRoot() {
79 | return root;
80 | }
81 |
82 | public void setRoot(File root) {
83 | this.root = root;
84 | }
85 |
86 |
87 | /**
88 | * Create a tree view that use a FileTreeContentProvider for its content. The created tree view
89 | * can be used to select branches of a directory tree.
90 | *
91 | * @param container The parent composite for the created tree view.
92 | * @return A checkbox tree view
93 | */
94 | static CheckboxTreeViewer createTreeView(Composite container) {
95 | final CheckboxTreeViewer tv = new CheckboxTreeViewer(container, SWT.BORDER);
96 | tv.setContentProvider(new DirectoryTreeContentProvider(null));
97 | tv.setLabelProvider(new ILabelProvider() {
98 |
99 | @Override
100 | public void removeListener(ILabelProviderListener listener) {
101 | // we do not have event notifying listeners, ignore.
102 | }
103 |
104 | @Override
105 | public boolean isLabelProperty(Object element, String property) {
106 | return false;
107 | }
108 |
109 |
110 | @Override
111 | public void dispose() {}
112 |
113 | @Override
114 | public void addListener(ILabelProviderListener listener) {
115 | // we do not have event notifying listeners, ignore.
116 | }
117 |
118 | @Override
119 | public Image getImage(Object element) {
120 | return null;
121 | }
122 |
123 | @Override
124 | public String getText(Object element) {
125 | return ((File) element).getName();
126 | }
127 | });
128 | tv.setInput("root"); // pass a non-null that will be ignored
129 |
130 | tv.addCheckStateListener(event -> setChecked(tv, event.getElement()));
131 |
132 | return tv;
133 | }
134 |
135 | private static void setChecked(final CheckboxTreeViewer tv, Object element) {
136 | // When user checks a checkbox in the tree, check all its children and mark parent as greyed
137 | // When a user uncheck a checkbox, mark the subtree as unchecked and ungrayed and if unique
138 | // sibling parent as grayed.
139 | DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider();
140 |
141 | boolean isChecked = tv.getChecked(element);
142 | if (tv.getGrayed(element)) {
143 | isChecked = !isChecked;
144 | }
145 | tv.setChecked(element, isChecked);
146 | tv.setGrayed(element, false);
147 | if (isChecked) {
148 | tv.setSubtreeChecked(element, true);
149 | } else {
150 | tv.setSubtreeChecked(element, false);
151 | }
152 | setGrayed(tv, provider.getParent(element));
153 | }
154 |
155 | private static void setGrayed(CheckboxTreeViewer tv, Object element) {
156 | if (element == null) {
157 | return;
158 | }
159 | DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider();
160 | boolean checked = tv.getChecked(element);
161 | boolean grayed = false;
162 | for (Object object : provider.getChildren(element)) {
163 | grayed = grayed || tv.getGrayed(object) || tv.getChecked(object);
164 | checked = checked && tv.getChecked(object) && !tv.getGrayed(element);
165 | }
166 | if (checked) {
167 | tv.setChecked(element, true);
168 | tv.setGrayed(element, false);
169 | } else if (grayed) {
170 | tv.setGrayChecked(element, true);
171 | } else {
172 | tv.setChecked(element, false);
173 | tv.setGrayed(element, false);
174 | }
175 | setGrayed(tv, provider.getParent(element));
176 | }
177 |
178 | /**
179 | * Set the root of the directory tree view and refresh the view if appropriate
180 | */
181 | static void setFileTreeRoot(CheckboxTreeViewer tv, File root) {
182 | DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider();
183 | if ((root == null && provider.getRoot() != null) || !root.equals(provider.getRoot())) {
184 | provider.setRoot(root);
185 | tv.refresh();
186 | }
187 | }
188 |
189 | /**
190 | * Returns the list of path selected in tv
. It returns the list of checked path
191 | * without the children of the checked path. Each path is returned as a string giving the relative
192 | * path from the root of the tree.
193 | */
194 | static ImmutableList getSelectPathsRelativeToRoot(CheckboxTreeViewer tv) {
195 | DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider();
196 | String root = provider.root.getAbsolutePath();
197 | ImmutableList.Builder builder = ImmutableList.builder();
198 | for (Object element : tv.getCheckedElements()) {
199 | if (!tv.getGrayed(element)) {
200 | Object parent = provider.getParent(element);
201 | if (parent == null || tv.getGrayed(parent)) {
202 | // Only add this element if its parent is not selected (so it's the root).
203 | String path = ((File) element).getAbsolutePath();
204 | // Strip root from path
205 | if (path.startsWith(root)) {
206 | path = path.substring(root.length());
207 | if (path.startsWith("/")) {
208 | path = path.substring(1);
209 | }
210 | builder.add(path);
211 | }
212 | }
213 | }
214 | }
215 | return builder.build();
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/java/com/google/devtools/bazel/e4b/wizard/WorkspaceWizardPage.java:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.wizard;
16 |
17 | import java.io.File;
18 | import java.io.IOException;
19 | import java.util.function.Consumer;
20 |
21 | import org.eclipse.jface.bindings.keys.KeyStroke;
22 | import org.eclipse.jface.dialogs.MessageDialog;
23 | import org.eclipse.jface.fieldassist.ContentProposalAdapter;
24 | import org.eclipse.jface.fieldassist.TextContentAdapter;
25 | import org.eclipse.jface.viewers.CheckboxTreeViewer;
26 | import org.eclipse.jface.wizard.WizardPage;
27 | import org.eclipse.swt.SWT;
28 | import org.eclipse.swt.events.KeyAdapter;
29 | import org.eclipse.swt.events.KeyEvent;
30 | import org.eclipse.swt.events.SelectionAdapter;
31 | import org.eclipse.swt.events.SelectionEvent;
32 | import org.eclipse.swt.events.SelectionListener;
33 | import org.eclipse.swt.layout.GridData;
34 | import org.eclipse.swt.layout.GridLayout;
35 | import org.eclipse.swt.widgets.Button;
36 | import org.eclipse.swt.widgets.Composite;
37 | import org.eclipse.swt.widgets.Control;
38 | import org.eclipse.swt.widgets.DirectoryDialog;
39 | import org.eclipse.swt.widgets.Label;
40 | import org.eclipse.swt.widgets.List;
41 | import org.eclipse.swt.widgets.Text;
42 |
43 | import com.google.common.collect.ImmutableList;
44 | import com.google.devtools.bazel.e4b.Activator;
45 | import com.google.devtools.bazel.e4b.command.BazelNotFoundException;
46 |
47 | /**
48 | * This is a quick wizard page that ask the user for the various targets and source path he wants to
49 | * include.
50 | */
51 | public class WorkspaceWizardPage extends WizardPage {
52 |
53 | private Label workspaceRoot;
54 | private List targets;
55 | private Text target;
56 | private Button addTargetButton;
57 | private Button removeTargetButton;
58 | private CheckboxTreeViewer directories;
59 | private Composite container;
60 | private Button workspaceRootButton;
61 | private DirectoryDialog dialog;
62 | private BazelTargetCompletionContentProposalProvider completionProvider;
63 |
64 | protected WorkspaceWizardPage() {
65 | super("Import Bazel project");
66 | setTitle("Import Bazel project");
67 | }
68 |
69 | /**
70 | * Returns the list of targets selected by the user.
71 | */
72 | ImmutableList getTargets() {
73 | return ImmutableList.copyOf(targets.getItems());
74 | }
75 |
76 | /**
77 | * Returns the list of directories selected by the user.
78 | */
79 | ImmutableList getDirectories() {
80 | return DirectoryTreeContentProvider.getSelectPathsRelativeToRoot(directories);
81 | }
82 |
83 | /**
84 | * Returns the workspace root selected by the user.
85 | */
86 | public String getWorkspaceRoot() {
87 | return workspaceRoot.getText();
88 | }
89 |
90 | /**
91 | * Returns the language version for the new project.
92 | */
93 | int getJavaLanguageVersion() {
94 | return 8;
95 | }
96 |
97 |
98 | @Override
99 | public void createControl(Composite parent) {
100 | container = new Composite(parent, SWT.NONE);
101 | GridLayout layout = new GridLayout();
102 | layout.numColumns = 3;
103 | container.setLayout(layout);
104 |
105 | createWorkspaceSelectionControls();
106 |
107 | addLabel("Directories: ", 1, 5);
108 | directories = DirectoryTreeContentProvider.createTreeView(container);
109 | setControlGridData(directories.getTree(), 2, 5, true);
110 | directories.addCheckStateListener(e -> updateControls());
111 |
112 | new Label(container, SWT.NONE).setText("Targets:");
113 | createTargetTextField();
114 | setControlGridData(target, 1, 1, false);
115 | addTargetButton = createButton("+", e -> addTarget());
116 |
117 | targets = new List(container, SWT.SINGLE | SWT.BORDER);
118 | setControlGridData(targets, 2, 5, true);
119 | removeTargetButton = createButton("-", e -> deleteTarget());
120 | targets.addSelectionListener(createSelectionListener(
121 | e -> removeTargetButton.setEnabled(targets.getSelectionCount() > 0)));
122 |
123 | setControl(container);
124 | updateControls();
125 | }
126 |
127 | // A simple helper to use lambdas to create a SelectionListener that does the same action on all
128 | // cases.
129 | private static SelectionListener createSelectionListener(Consumer f) {
130 | return new SelectionListener() {
131 | @Override
132 | public void widgetSelected(SelectionEvent e) {
133 | f.accept(e);
134 |
135 | }
136 |
137 | @Override
138 | public void widgetDefaultSelected(SelectionEvent e) {
139 | f.accept(e);
140 | }
141 | };
142 | }
143 |
144 | private Button createButton(String text, Consumer handler) {
145 | Button result = new Button(container, SWT.DEFAULT);
146 | result.setText(text);
147 | result.addSelectionListener(createSelectionListener(handler));
148 | result.setEnabled(false);
149 | return result;
150 | }
151 |
152 | private void createWorkspaceSelectionControls() {
153 | Label label = new Label(container, SWT.NONE);
154 | label.setText("Workpsace root: ");
155 |
156 | workspaceRoot = new Label(container, SWT.BORDER);
157 | workspaceRoot.setText("");
158 | workspaceRoot.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false));
159 |
160 | dialog = new DirectoryDialog(getShell(), SWT.OPEN);
161 | workspaceRootButton = new Button(container, SWT.DEFAULT);
162 | workspaceRootButton.setText("...");
163 | workspaceRootButton.addSelectionListener(new SelectionAdapter() {
164 | @Override
165 | public void widgetSelected(SelectionEvent e) {
166 | String wr = dialog.open();
167 | if (wr != null) {
168 | workspaceRoot.setText(wr);
169 | DirectoryTreeContentProvider.setFileTreeRoot(directories, new File(wr));
170 | try {
171 | completionProvider.setBazelInstance(
172 | Activator.getDefault().getCommand().getInstance(new File(getWorkspaceRoot())));
173 | } catch (IOException e1) {
174 | MessageDialog.openError(getShell(), "Error",
175 | getWorkspaceRoot() + " does not seems to be a Bazel workspace");
176 | } catch (InterruptedException e1) {
177 | Activator.error("Bazel was interrupted", e1);
178 | } catch (BazelNotFoundException e1) {
179 | MessageDialog.openError(getShell(), "Error", "Cannot found Bazel: " + e1.getMessage());
180 | }
181 |
182 | }
183 | updateControls();
184 | }
185 | });
186 | workspaceRootButton.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false));
187 | }
188 |
189 | private void setControlGridData(Control control, int horizontalSpan, int verticalSpan,
190 | boolean verticalGrow) {
191 | GridData data = new GridData(GridData.FILL, verticalGrow ? GridData.FILL : GridData.CENTER,
192 | true, verticalGrow);
193 | data.horizontalSpan = horizontalSpan;
194 | data.verticalSpan = verticalSpan;
195 | control.setLayoutData(data);
196 | }
197 |
198 | private void addLabel(String labelText, int horizontalSpan, int verticalSpan) {
199 | Label label = new Label(container, SWT.NONE);
200 | label.setText(labelText);
201 | GridData data = new GridData(GridData.BEGINNING, GridData.FILL, false, true);
202 | data.horizontalSpan = horizontalSpan;
203 | data.verticalSpan = verticalSpan;
204 | label.setLayoutData(data);
205 | }
206 |
207 | private void updateControls() {
208 | boolean enabled = !workspaceRoot.getText().isEmpty();
209 | directories.getTree().setEnabled(enabled);
210 | targets.setEnabled(enabled);
211 | target.setEnabled(enabled);
212 | addTargetButton.setEnabled(enabled && !target.getText().isEmpty());
213 | removeTargetButton.setEnabled(enabled && targets.getSelectionCount() > 0);
214 | setPageComplete(
215 | enabled && (directories.getCheckedElements().length > 0) && (targets.getItemCount() > 0));
216 | }
217 |
218 | private void setAutoCompletion() {
219 | try {
220 | ContentProposalAdapter adapter = null;
221 | completionProvider = new BazelTargetCompletionContentProposalProvider();
222 | KeyStroke ks = KeyStroke.getInstance("Ctrl+Space");
223 | adapter = new ContentProposalAdapter(target, new TextContentAdapter(), completionProvider, ks,
224 | null);
225 | adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
226 | } catch (Exception e) {
227 | e.printStackTrace();
228 | }
229 | }
230 |
231 |
232 | private void createTargetTextField() {
233 | target = new Text(container, SWT.BORDER);
234 | setAutoCompletion();
235 | target.addKeyListener(new KeyAdapter() {
236 | @Override
237 | public void keyReleased(KeyEvent ke) {
238 | if (ke.keyCode == '\r' && (ke.stateMask & SWT.SHIFT) != 0 && !target.getText().isEmpty()) {
239 | addTarget();
240 | }
241 | }
242 | });
243 | target.addModifyListener(e -> updateControls());
244 | }
245 |
246 | private void addTarget() {
247 | targets.add(target.getText());
248 | target.setText("");
249 | setAutoCompletion();
250 | updateControls();
251 | }
252 |
253 | private void deleteTarget() {
254 | targets.remove(targets.getSelectionIndices());
255 | updateControls();
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/javatests/com/google/devtools/bazel/e4b/command/BUILD:
--------------------------------------------------------------------------------
1 | java_test(
2 | name = "CommandTest",
3 | srcs = ["CommandTest.java"],
4 | deps = [
5 | "//java/com/google/devtools/bazel/e4b/command",
6 | "@com_google_truth//jar",
7 | "@org_hamcrest_core//jar",
8 | "@org_junit//jar",
9 | ],
10 | )
11 |
--------------------------------------------------------------------------------
/javatests/com/google/devtools/bazel/e4b/command/CommandTest.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.command;
16 |
17 | import static com.google.common.truth.Truth.assertThat;
18 |
19 | import com.google.devtools.bazel.e4b.command.CommandConsole.CommandConsoleFactory;
20 | import java.io.ByteArrayOutputStream;
21 | import java.io.IOException;
22 | import java.io.OutputStream;
23 | import java.nio.charset.StandardCharsets;
24 | import java.util.LinkedList;
25 | import java.util.List;
26 | import java.util.function.Function;
27 | import org.junit.Before;
28 | import org.junit.Rule;
29 | import org.junit.Test;
30 | import org.junit.rules.TemporaryFolder;
31 |
32 | /** @{link Command}Test */
33 | public class CommandTest {
34 |
35 | private static Function NON_EMPTY_LINES_SELECTOR =
36 | (x) -> x.trim().isEmpty() ? null : x;
37 |
38 | @Rule
39 | public TemporaryFolder folder = new TemporaryFolder();
40 |
41 | private class MockCommandConsole implements CommandConsole {
42 | final ByteArrayOutputStream stdout = new ByteArrayOutputStream();
43 | final ByteArrayOutputStream stderr = new ByteArrayOutputStream();
44 | final String name;
45 | final String title;
46 |
47 | public MockCommandConsole(String name, String title) {
48 | this.title = title;
49 | this.name = name;
50 | }
51 |
52 | @Override
53 | public OutputStream createOutputStream() {
54 | return stdout;
55 | }
56 |
57 | @Override
58 | public OutputStream createErrorStream() {
59 | return stderr;
60 | }
61 | }
62 |
63 | private class MockConsoleFactory implements CommandConsoleFactory {
64 | final List consoles = new LinkedList<>();
65 |
66 | @Override
67 | public CommandConsole get(String name, String title) throws IOException {
68 | MockCommandConsole console = new MockCommandConsole(name, title);
69 | consoles.add(console);
70 | return console;
71 | }
72 | }
73 |
74 | public MockConsoleFactory mockConsoleFactory;
75 |
76 | @Before
77 | public void setup() {
78 | mockConsoleFactory = new MockConsoleFactory();
79 | }
80 |
81 | @Test
82 | public void testCommandWithStream() throws IOException, InterruptedException {
83 | ByteArrayOutputStream stdout = new ByteArrayOutputStream();
84 | ByteArrayOutputStream stderr = new ByteArrayOutputStream();
85 | Function stdoutSelector =
86 | (x) -> (x.trim().isEmpty() || x.equals("a")) ? null : x;
87 | Function stderrSelector =
88 | (x) -> (x.trim().isEmpty() || x.equals("b")) ? null : x;
89 |
90 | Command.Builder builder = Command.builder(mockConsoleFactory)
91 | .setConsoleName("test")
92 | .setDirectory(folder.getRoot())
93 | .setStandardError(stderr)
94 | .setStandardOutput(stdout)
95 | .setStderrLineSelector(stderrSelector)
96 | .setStdoutLineSelector(stdoutSelector);
97 | builder.addArguments("bash", "-c", "echo a; echo b; echo a >&2; echo b >&2");
98 | Command cmd = builder.build();
99 | assertThat(cmd.run()).isEqualTo(0);
100 | String stdoutStr = new String(stdout.toByteArray(), StandardCharsets.UTF_8).trim();
101 | String stderrStr = new String(stderr.toByteArray(), StandardCharsets.UTF_8).trim();
102 |
103 | assertThat(stdoutStr).isEqualTo("a");
104 | assertThat(stderrStr).isEqualTo("b");
105 | assertThat(cmd.getSelectedErrorLines()).containsExactly("a");
106 | assertThat(cmd.getSelectedOutputLines()).containsExactly("b");
107 | assertThat(mockConsoleFactory.consoles).hasSize(1);
108 | MockCommandConsole console = mockConsoleFactory.consoles.get(0);
109 | assertThat(console.name).isEqualTo("test");
110 | assertThat(console.title).isEqualTo(
111 | "Running bash -c echo a; echo b; echo a >&2; echo b >&2 " + "from " + folder.getRoot());
112 | stdoutStr = new String(console.stdout.toByteArray(), StandardCharsets.UTF_8).trim();
113 | stderrStr = new String(console.stderr.toByteArray(), StandardCharsets.UTF_8).trim();
114 | assertThat(stdoutStr).isEmpty();
115 | assertThat(stderrStr).isEmpty();
116 | }
117 |
118 | @Test
119 | public void testCommandNoStream() throws IOException, InterruptedException {
120 | Command.Builder builder =
121 | Command.builder(mockConsoleFactory).setConsoleName(null).setDirectory(folder.getRoot());
122 | builder.addArguments("bash", "-c", "echo a; echo b; echo a >&2; echo b >&2");
123 | builder.setStderrLineSelector(NON_EMPTY_LINES_SELECTOR)
124 | .setStdoutLineSelector(NON_EMPTY_LINES_SELECTOR);
125 | Command cmd = builder.build();
126 | assertThat(cmd.run()).isEqualTo(0);
127 | assertThat(cmd.getSelectedErrorLines()).containsExactly("a", "b");
128 | assertThat(cmd.getSelectedOutputLines()).containsExactly("a", "b");
129 | }
130 |
131 | @Test
132 | public void testCommandStreamAllToConsole() throws IOException, InterruptedException {
133 | Command.Builder builder =
134 | Command.builder(mockConsoleFactory).setConsoleName("test").setDirectory(folder.getRoot());
135 | builder.addArguments("bash", "-c", "echo a; echo b; echo a >&2; echo b >&2");
136 | Command cmd = builder.build();
137 | assertThat(cmd.run()).isEqualTo(0);
138 | MockCommandConsole console = mockConsoleFactory.consoles.get(0);
139 | assertThat(console.name).isEqualTo("test");
140 | assertThat(console.title).isEqualTo(
141 | "Running bash -c echo a; echo b; echo a >&2; echo b >&2 " + "from " + folder.getRoot());
142 | String stdoutStr = new String(console.stdout.toByteArray(), StandardCharsets.UTF_8).trim();
143 | String stderrStr = new String(console.stderr.toByteArray(), StandardCharsets.UTF_8).trim();
144 | assertThat(stdoutStr).isEqualTo("a\nb");
145 | assertThat(stderrStr).isEqualTo("a\nb");
146 | }
147 |
148 | @Test
149 | public void testCommandWorkDir() throws IOException, InterruptedException {
150 | Command.Builder builder =
151 | Command.builder(mockConsoleFactory).setConsoleName(null).setDirectory(folder.getRoot());
152 | builder.setStderrLineSelector(NON_EMPTY_LINES_SELECTOR)
153 | .setStdoutLineSelector(NON_EMPTY_LINES_SELECTOR);
154 | builder.addArguments("pwd");
155 | Command cmd = builder.build();
156 | assertThat(cmd.run()).isEqualTo(0);
157 | assertThat(cmd.getSelectedErrorLines()).isEmpty();
158 | assertThat(cmd.getSelectedOutputLines()).containsExactly(folder.getRoot().getCanonicalPath());
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/javatests/com/google/devtools/bazel/e4b/integration/AspectIntegrationTest.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.integration;
16 |
17 | import static com.google.common.truth.Truth.assertThat;
18 | import static org.junit.Assert.assertEquals;
19 |
20 | import java.io.File;
21 | import java.io.IOException;
22 | import java.nio.file.Path;
23 |
24 | import org.junit.Before;
25 | import org.junit.BeforeClass;
26 | import org.junit.Test;
27 |
28 | import build.bazel.tests.integration.Command;
29 | import build.bazel.tests.integration.WorkspaceDriver;
30 |
31 | import com.google.common.base.Joiner;
32 | import com.google.common.collect.ImmutableList;
33 | import com.google.common.collect.ImmutableMap;
34 | import com.google.devtools.bazel.e4b.command.IdeBuildInfo;
35 |
36 | /** Integration test for the aspect used by the plugin. */
37 | public final class AspectIntegrationTest {
38 |
39 | private WorkspaceDriver driver = new WorkspaceDriver();
40 | private Path aspectWorkspace;
41 |
42 | @BeforeClass
43 | public static void setUpClass() throws IOException {
44 | WorkspaceDriver.setUpClass();
45 | }
46 |
47 | @Before
48 | public void setUp() throws Exception {
49 | driver.setUp();
50 | aspectWorkspace = driver.currentWorkspace();
51 | driver.copyFromRunfiles("build_bazel_eclipse/resources/e4b_aspect.bzl", "e4b_aspect.bzl");
52 | driver.scratchFile("BUILD");
53 | driver.newWorkspace();
54 | createJavaProgram();
55 | }
56 |
57 | private void createJavaProgram() throws Exception {
58 | driver.scratchFile("java/my/pkg/Main.java", // force-new-line
59 | "package my.pkg;", // force-new-line
60 | "import my.other.pkg.Annex;", // force-new-line
61 | "public class Main {", // force-new-line
62 | " public static void main(String[] args) {", // force-new-line
63 | " System.out.println(new Annex().helloWorld());", // force-new-line
64 | " }", // force-new-line
65 | "}");
66 | driver.scratchFile("java/my/pkg/BUILD", // force-new-line
67 | "java_binary(name='pkg',", // force-new-line
68 | " srcs=['Main.java'],", // force-new-line
69 | " deps=['//java/my/other/pkg:Annex'])");
70 | driver.scratchFile("java/my/other/pkg/Annex.java", // force-new-line
71 | "package my.other.pkg;", // force-new-line
72 |
73 |
74 | "public class Annex {", // force-new-line
75 | " public Annex() {}", // force-new-line
76 |
77 | " public String helloWorld() {", // force-new-line
78 | " return \"Hello, World!\";", // force-new-line
79 | " }", // force-new-line
80 | "}");
81 | driver.scratchFile("java/my/other/pkg/BUILD", // force-new-line
82 | "java_library(name='Annex',", // force-new-line
83 | " srcs=['Annex.java'],", // force-new-line
84 | " visibility = ['//visibility:public'])");
85 | driver.scratchFile("javatests/my/other/pkg/AnnexTest.java", // force-new-line
86 | "package my.other.pkg;", // force-new-line
87 |
88 | "import static org.junit.Assert.assertEquals;", // force-new-line
89 | "import org.junit.Test;", // force-new-line
90 |
91 |
92 | "public class AnnexTest {", // force-new-line
93 | " @Test", // force-new-line
94 | " public void testAnnex() {", // force-new-line
95 | " assertEquals(\"Hello, World!\", new Annex().helloWorld());", // force-new-line
96 | " }", // force-new-line
97 | "}");
98 | driver.scratchFile("javatests/my/other/pkg/BUILD", // force-new-line
99 | "java_test(name='AnnexTest',", // force-new-line
100 | " srcs=['AnnexTest.java'],", // force-new-line
101 | " deps=['//java/my/other/pkg:Annex'])");
102 | }
103 |
104 | @Test
105 | public void testAspectGenerateJson() throws Exception {
106 | Command cmd = driver.bazelCommand("build", "--override_repository=local_eclipse_aspect=" + aspectWorkspace,
107 | "--aspects=@local_eclipse_aspect//:e4b_aspect.bzl%e4b_aspect", "-k",
108 | "--output_groups=ide-info-text,ide-resolve,-_,-defaults", "--experimental_show_artifacts",
109 | "//...").build();
110 | int retCode = cmd.run();
111 | assertEquals("Bazel failed to build, stderr: " + Joiner.on("\n").join(cmd.getErrorLines()),
112 | 0, retCode);
113 | String[] jsonFiles = cmd.getErrorLines().stream().filter((s) -> {
114 | return s.startsWith(">>>") && s.endsWith(".json");
115 | }).map((s) -> {
116 | return s.substring(3);
117 | }).toArray(String[]::new);
118 | assertThat(jsonFiles).hasLength(3);
119 |
120 | ImmutableMap infos =
121 | IdeBuildInfo.getInfo(ImmutableList.copyOf(jsonFiles));
122 |
123 | assertThat(infos).hasSize(3);
124 |
125 | assertThat(infos.get("//java/my/pkg:pkg").getLabel()).isEqualTo("//java/my/pkg:pkg");
126 | assertThat(infos.get("//java/my/pkg:pkg").getLocation()).isEqualTo("java/my/pkg/BUILD");
127 | assertThat(infos.get("//java/my/pkg:pkg").getKind()).isEqualTo("java_binary");
128 | assertThat(infos.get("//java/my/pkg:pkg").getGeneratedJars()).isEmpty();
129 | assertThat(infos.get("//java/my/pkg:pkg").getDeps())
130 | .containsExactly("//java/my/other/pkg:Annex");
131 | assertThat(infos.get("//java/my/pkg:pkg").getSources())
132 | .containsExactly("java/my/pkg/Main.java");
133 |
134 | assertThat(infos.get("//java/my/other/pkg:Annex").getLabel())
135 | .isEqualTo("//java/my/other/pkg:Annex");
136 | assertThat(infos.get("//java/my/other/pkg:Annex").getLocation())
137 | .isEqualTo("java/my/other/pkg/BUILD");
138 | assertThat(infos.get("//java/my/other/pkg:Annex").getKind()).isEqualTo("java_library");
139 | assertThat(infos.get("//java/my/other/pkg:Annex").getGeneratedJars()).isEmpty();
140 | assertThat(infos.get("//java/my/other/pkg:Annex").getDeps()).isEmpty();
141 | assertThat(infos.get("//java/my/other/pkg:Annex").getSources())
142 | .containsExactly("java/my/other/pkg/Annex.java");
143 |
144 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getLabel())
145 | .isEqualTo("//javatests/my/other/pkg:AnnexTest");
146 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getLocation())
147 | .isEqualTo("javatests/my/other/pkg/BUILD");
148 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getKind()).isEqualTo("java_test");
149 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getGeneratedJars()).isEmpty();
150 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getDeps())
151 | .containsExactly("//java/my/other/pkg:Annex");
152 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getSources())
153 | .containsExactly("javatests/my/other/pkg/AnnexTest.java");
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/javatests/com/google/devtools/bazel/e4b/integration/BUILD:
--------------------------------------------------------------------------------
1 | load("@build_bazel_integration_testing//tools:bazel_java_integration_test.bzl", "bazel_java_integration_test")
2 |
3 | bazel_java_integration_test(
4 | name = "AspectIntegrationTest",
5 | srcs = ["AspectIntegrationTest.java"],
6 | data = ["//resources:srcs"],
7 | deps = [
8 | "//java/com/google/devtools/bazel/e4b/command",
9 | "@org_junit//jar",
10 | "@org_hamcrest_core//jar",
11 | "@com_google_guava//jar",
12 | "@com_google_truth//jar",
13 | ],
14 | )
15 |
--------------------------------------------------------------------------------
/javatests/com/google/devtools/bazel/e4b/projectviews/BUILD:
--------------------------------------------------------------------------------
1 | java_test(
2 | name = "ProjectViewTest",
3 | srcs = ["ProjectViewTest.java"],
4 | resources = ["bazel.projectview"],
5 | deps = [
6 | "//java/com/google/devtools/bazel/e4b/projectviews",
7 | "@com_google_truth//jar",
8 | "@org_hamcrest_core//jar",
9 | "@org_junit//jar",
10 | ],
11 | )
12 |
--------------------------------------------------------------------------------
/javatests/com/google/devtools/bazel/e4b/projectviews/ProjectViewTest.java:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Bazel Authors. All rights reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.devtools.bazel.e4b.projectviews;
16 |
17 | import static com.google.common.truth.Truth.assertThat;
18 |
19 | import java.io.IOException;
20 | import java.net.URISyntaxException;
21 |
22 | import org.junit.Test;
23 |
24 | /** {@link ProjectView}Test */
25 | public class ProjectViewTest {
26 |
27 | @Test
28 | public void testBazelView() throws IOException, ProjectViewParseException, URISyntaxException {
29 | ProjectView view = ProjectView.builder()
30 | .parseView(ProjectViewTest.class.getResource("bazel.projectview")).build();
31 | assertThat(view.getBuildFlags()).isEmpty();
32 | assertThat(view.getDirectories()).containsExactly(".");
33 | assertThat(view.getJavaLanguageLevel()).isEqualTo(0);
34 | assertThat(view.getTargets()).containsAllOf("//src:bazel", "//src/test/...");
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/javatests/com/google/devtools/bazel/e4b/projectviews/bazel.projectview:
--------------------------------------------------------------------------------
1 | # Setup IntelliJ for Bazel development using the IntelliJ Bazel Plugin.
2 | # From github.com/bazelbuild/bazel's scripts/ij.projectview at commit 815bd634df0350a227133244c734b5c3672776ab.
3 | directories:
4 | .
5 |
6 | test_sources:
7 | src/java_tools/buildjar/javatests/*
8 | src/java_tools/junitrunner/javatests/*
9 | src/java_tools/singlejar/javatests/*
10 | src/test/*
11 |
12 | targets:
13 | //src:bazel
14 | //src/java_tools/buildjar:JavaBuilder
15 | //src/java_tools/buildjar:VanillaJavaBuilder
16 | //src/java_tools/buildjar/javatests/...
17 | //src/java_tools/junitrunner/java/com/google/testing/junit/runner:Runner
18 | //src/java_tools/junitrunner/javatests/...
19 | //src/java_tools/singlejar:SingleJar
20 | //src/java_tools/singlejar:tests
21 | //src/test/...
22 | //src/tools/remote_worker
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
9 |
10 |
11 |
13 |
19 | Import a list of sources from a Bazel workspace into a Java project
20 |
21 |
22 |
25 |
26 |
28 |
29 |
30 |
31 |
33 |
37 |
38 |
39 |
41 |
43 |
44 |
45 |
48 |
53 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/resources/BUILD:
--------------------------------------------------------------------------------
1 | # This is a build file for creating a corresponding package. This package will be a local repository
2 | # added with --override_repository when using the aspect so we can load the e4b_aspect.bzl Skylark
3 | # aspect extension when calling build from Eclipse.
4 |
5 | filegroup(
6 | name = "srcs",
7 | srcs = glob(["**"]),
8 | visibility = ["//visibility:public"],
9 | )
10 |
--------------------------------------------------------------------------------
/resources/WORKSPACE:
--------------------------------------------------------------------------------
1 | # This is a workspace file for creating a local repository added with --override_repository when
2 | # using the aspect so we can load the e4b_aspect.bzl Skylark aspect extension when calling build
3 | # from Eclipse.
--------------------------------------------------------------------------------
/resources/e4b_aspect.bzl:
--------------------------------------------------------------------------------
1 | # Copyright 2016 The Bazel Authors. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # Aspect for e4b, taken from intellij_info.bzl
16 |
17 | DEPENDENCY_ATTRIBUTES = [
18 | "deps",
19 | "runtime_deps",
20 | "exports",
21 | ]
22 |
23 | def struct_omit_none(**kwargs):
24 | d = {name: kwargs[name] for name in kwargs if kwargs[name] != None}
25 | return struct(**d)
26 |
27 | def artifact_location(file):
28 | return None if file == None else file.path
29 |
30 | def library_artifact(java_output):
31 | if java_output == None or java_output.class_jar == None:
32 | return None
33 | return struct_omit_none(
34 | jar = artifact_location(java_output.class_jar),
35 | interface_jar = artifact_location(java_output.ijar),
36 | source_jar = artifact_location(java_output.source_jar),
37 | )
38 |
39 | def annotation_processing_jars(annotation_processing):
40 | return struct_omit_none(
41 | jar = artifact_location(annotation_processing.class_jar),
42 | source_jar = artifact_location(annotation_processing.source_jar),
43 | )
44 |
45 | def jars_from_output(output):
46 | """ Collect jars for ide-resolve-files from Java output.
47 | """
48 | if output == None:
49 | return []
50 | return [jar
51 | for jar in [output.class_jar, output.ijar, output.source_jar]
52 | if jar != None and not jar.is_source]
53 |
54 | def java_rule_ide_info(target, ctx):
55 | if hasattr(ctx.rule.attr, "srcs"):
56 | sources = [artifact_location(file)
57 | for src in ctx.rule.attr.srcs
58 | for file in src.files]
59 | else:
60 | sources = []
61 |
62 | jars = [library_artifact(output) for output in target.java.outputs.jars]
63 | ide_resolve_files = depset([jar
64 | for output in target.java.outputs.jars
65 | for jar in jars_from_output(output)])
66 |
67 | gen_jars = []
68 | if target.java.annotation_processing and target.java.annotation_processing.enabled:
69 | gen_jars = [annotation_processing_jars(target.java.annotation_processing)]
70 | ide_resolve_files = ide_resolve_files + depset([ jar
71 | for jar in [target.java.annotation_processing.class_jar,
72 | target.java.annotation_processing.source_jar]
73 | if jar != None and not jar.is_source])
74 |
75 | return (struct_omit_none(
76 | sources = sources,
77 | jars = jars,
78 | generated_jars = gen_jars
79 | ),
80 | ide_resolve_files)
81 |
82 |
83 | def _aspect_impl(target, ctx):
84 | kind = ctx.rule.kind
85 | rule_attrs = ctx.rule.attr
86 |
87 | ide_info_text = depset()
88 | ide_resolve_files = depset()
89 | all_deps = []
90 |
91 | for attr_name in DEPENDENCY_ATTRIBUTES:
92 | if hasattr(rule_attrs, attr_name):
93 | deps = getattr(rule_attrs, attr_name)
94 | if type(deps) == 'list':
95 | for dep in deps:
96 | if hasattr(dep, "intellij_info_files"):
97 | ide_info_text = ide_info_text + dep.intellij_info_files.ide_info_text
98 | ide_resolve_files = ide_resolve_files + dep.intellij_info_files.ide_resolve_files
99 | all_deps += [str(dep.label) for dep in deps]
100 |
101 | if hasattr(target, "java"):
102 | (java_rule_ide_info_struct, java_ide_resolve_files) = java_rule_ide_info(target, ctx)
103 | info = struct(
104 | label = str(target.label),
105 | kind = kind,
106 | dependencies = all_deps,
107 | build_file_artifact_location = ctx.build_file_path,
108 | ) + java_rule_ide_info_struct
109 | ide_resolve_files = ide_resolve_files + java_ide_resolve_files
110 | output = ctx.new_file(target.label.name + ".e4b-build.json")
111 | ctx.file_action(output, info.to_json())
112 | ide_info_text += depset([output])
113 |
114 | return struct(
115 | output_groups = {
116 | "ide-info-text" : ide_info_text,
117 | "ide-resolve" : ide_resolve_files,
118 | },
119 | intellij_info_files = struct(
120 | ide_info_text = ide_info_text,
121 | ide_resolve_files = ide_resolve_files,
122 | )
123 | )
124 |
125 | e4b_aspect = aspect(implementation = _aspect_impl,
126 | attr_aspects = DEPENDENCY_ATTRIBUTES
127 | )
128 | """Aspect for Eclipse 4 Bazel plugin.
129 |
130 | This aspect produces information for IDE integration with Eclipse. This only
131 | produces information for Java targets.
132 |
133 | This aspect has two output groups:
134 | - ide-info-text produces .e4b-build.json files that contains information
135 | about target dependencies and sources files for the IDE.
136 | - ide-resolve build the dependencies needed for the build (i.e., artifacts
137 | generated by Java annotation processors).
138 |
139 | An e4b-build.json file is a json blob with the following keys:
140 | ```javascript
141 | {
142 | // Label of the corresponding target
143 | "label": "//package:target",
144 | // Kind of the corresponding target, e.g., java_test, java_binary, ...
145 | "kind": "java_library",
146 | // List of dependencies of this target
147 | "dependencies": ["//package1:dep1", "//package2:dep2"],
148 | "Path, relative to the workspace root, of the build file containing the target.
149 | "build_file_artifact_location": "package/BUILD",
150 | // List of sources file, relative to the execroot
151 | "sources": ["package/Test.java"],
152 | // List of jars created when building this target.
153 | "jars": [jar1, jar2],
154 | // List of jars generated by java annotation processors when building this target.
155 | "generated_jars": [genjar1, genjar2]
156 | }
157 | ```
158 |
159 | Jar files structure has the following keys:
160 | ```javascript
161 | {
162 | // Location, relative to the execroot, of the jar file or null
163 | "jar": "bazel-out/host/package/libtarget.jar",
164 | // Location, relative to the execroot, of the interface jar file,
165 | // containing only the interfaces of the target jar or null.
166 | "interface_jar": "bazel-out/host/package/libtarget.interface-jar",
167 | // Location, relative to the execroot, of the source jar file,
168 | // containing the sources used to generate the target jar or null.
169 | "source_jar": "bazel-out/host/package/libtarget.interface-jar",
170 | }
171 | ```
172 | """
173 |
--------------------------------------------------------------------------------
/resources/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bazelbuild/eclipse/055ccebf7221dfbcab67204ca210f98cba5bec6a/resources/icon.ico
--------------------------------------------------------------------------------
/runfiles:
--------------------------------------------------------------------------------
1 | bazel-bin/javatests/com/google/devtools/bazel/e4b/integration/AspectIntegrationTest/bazel0.10.0.runfiles
--------------------------------------------------------------------------------
/tools/build_defs/BUILD:
--------------------------------------------------------------------------------
1 | package(default_visibility = ["//visibility:public"])
2 |
3 | py_binary(
4 | name = "feature_builder",
5 | srcs = ["feature_builder.py"],
6 | deps = ["@com_google_python_gflags//:gflags"],
7 | )
8 |
9 | py_binary(
10 | name = "site_builder",
11 | srcs = ["site_builder.py"],
12 | deps = ["@com_google_python_gflags//:gflags"],
13 | )
14 |
--------------------------------------------------------------------------------
/tools/build_defs/eclipse.bzl:
--------------------------------------------------------------------------------
1 | # Copyright 2017 The Bazel Authors. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # TODO(dmarting): Provide checksums for those files.
16 | _EQUINOX_MIRROR_URL="https://storage.googleapis.com/bazel-mirror/download.eclipse.org/eclipse/updates"
17 | _ECLIPSE_VERSION="4.5.2-201602121500"
18 | _DOWNLOAD_URL = "%s/%s/R-%s/plugins/%s_%s.jar" % (
19 | _EQUINOX_MIRROR_URL,
20 | ".".join(_ECLIPSE_VERSION.split(".", 3)[0:2]),
21 | _ECLIPSE_VERSION,
22 | "%s",
23 | "%s")
24 |
25 | # TODO(dmarting): make this configurable?
26 | _DECLARED_DEPS = [
27 | "org.eclipse.ui.console",
28 | "org.eclipse.ui",
29 | "org.eclipse.core.resources",
30 | "org.eclipse.ui.ide",
31 | "org.eclipse.jdt.core",
32 | "org.eclipse.core.runtime",
33 | "javax.inject",
34 | ]
35 |
36 | _ECLIPSE_PLUGIN_DEPS = {
37 | # Declared deps.
38 | "org.eclipse.ui.console": "3.6.100.v20150822-1912",
39 | "javax.inject": "1.0.0.v20091030",
40 | "org.eclipse.core.runtime": "3.11.1.v20150903-1804",
41 | "org.eclipse.ui": "3.107.0.v20150507-1945",
42 | "org.eclipse.jdt.core": "3.11.2.v20160128-0629",
43 | "org.eclipse.core.resources": "3.10.1.v20150725-1910",
44 | "org.eclipse.ui.ide": "3.11.0.v20150825-2158",
45 | # implicit deps
46 | "org.eclipse.swt": "3.104.2.v20160212-1350",
47 | # TODO(dmarting): make it works cross platform. This is not a problem while
48 | # we are using the dependency to compile but this might become an issue if
49 | # we need to run the plugin (e.g. to test it).
50 | #
51 | # Available platforms: cocoa.macosx.x86_64 gtk.aix.ppc gtk.aix.ppc64
52 | # gtk.hpux.ia64 gtk.linux.ppc gtk.linux.ppc64 gtk.linux.ppc64le gtk.linux.s390
53 | # gtk.linux.s390x gtk.linux.x86 gtk.linux.x86_64 gtk.solaris.sparc
54 | # gtk.solaris.x86 win32.win32.x86 win32.win32.x86_64
55 | "org.eclipse.swt.gtk.linux.ppc": "3.104.2.v20160212-1350",
56 | "org.eclipse.jface": "3.11.1.v20160128-1644",
57 | "org.eclipse.core.commands": "3.7.0.v20150422-0725",
58 | "org.eclipse.ui.workbench": "3.107.1.v20160120-2131",
59 | "org.eclipse.e4.ui.workbench3": "0.13.0.v20150422-0725",
60 | "org.eclipse.jdt.compiler.apt": "1.2.0.v20150514-0146",
61 | "org.eclipse.jdt.compiler.tool": "1.1.0.v20150513-2007",
62 | "javax.annotation": "1.2.0.v201401042248",
63 | "org.eclipse.osgi": "3.10.102.v20160118-1700",
64 | "org.eclipse.osgi.compatibility.state": "1.0.100.v20150402-1551",
65 | "org.eclipse.equinox.common": "3.7.0.v20150402-1709",
66 | "org.eclipse.core.jobs": "3.7.0.v20150330-2103",
67 | "org.eclipse.core.runtime.compatibility.registry": "3.6.0.v20150318-1505",
68 | "org.eclipse.equinox.registry": "3.6.0.v20150318-1503",
69 | "org.eclipse.equinox.preferences": "3.5.300.v20150408-1437",
70 | "org.eclipse.core.contenttype": "3.5.0.v20150421-2214",
71 | "org.eclipse.equinox.app": "1.3.300.v20150423-1356",
72 | "org.eclipse.ui.views": "3.8.0.v20150422-0725",
73 | }
74 |
75 |
76 | def _load_eclipse_dep(plugin, version):
77 | native.http_file(
78 | name = plugin.replace(".", "_"),
79 | url = _DOWNLOAD_URL % (plugin, version),
80 | )
81 |
82 | load("//tools/build_defs:eclipse_platform.bzl", "eclipse_platform")
83 |
84 | def load_eclipse_deps():
85 | """Load dependencies of the Eclipse plugin."""
86 | for plugin in _ECLIPSE_PLUGIN_DEPS:
87 | _load_eclipse_dep(plugin, _ECLIPSE_PLUGIN_DEPS[plugin])
88 | eclipse_platform(name="org_eclipse_equinox", version=_ECLIPSE_VERSION)
89 |
90 |
91 | def eclipse_plugin(name, version, bundle_name, activator=None,
92 | vendor=None, **kwargs):
93 | """A macro to generate an eclipse plugin (see java_binary)."""
94 | jars = ["@%s//file" % plugin.replace(".", "_")
95 | for plugin in _ECLIPSE_PLUGIN_DEPS]
96 | native.java_import(
97 | name = name + "-deps",
98 | neverlink = 1,
99 | jars = jars,
100 | )
101 | deps = [name + "-deps"]
102 | if "deps" in kwargs:
103 | deps = deps + kwargs["deps"]
104 | args = {k: kwargs[k]
105 | for k in kwargs
106 | if k not in [
107 | "deps",
108 | "classpath_resources",
109 | "deploy_manifest_lines",
110 | "visibility",
111 | "main_class"]}
112 | visibility = kwargs["visibility"] if "visibility" in kwargs else None
113 | # Generate the .api_description to put in the final jar
114 | native.genrule(
115 | name = name + ".api_description",
116 | srcs = [],
117 | outs = [name + "/.api_description"],
118 | cmd = """
119 | cat <$@
120 |
121 |
122 |
123 |
124 | EOF
125 | """ % (name, version, name, version))
126 | # Generate the final jar (a deploy jar)
127 | native.java_binary(
128 | name = name + "-bin",
129 | main_class = "does.not.exist",
130 | classpath_resources = [
131 | ":%s/.api_description" % name,
132 | # TODO(dmarting): this add the plugin.xml dependency here, maybe we
133 | # should move that to the BUILD file to avoid surprise?
134 | "plugin.xml",
135 | ] + (kwargs["classpath_resources"]
136 | if "classpath_resources" in kwargs else []),
137 | deploy_manifest_lines = [
138 | "Bundle-ManifestVersion: 2",
139 | "Bundle-Name: " + bundle_name,
140 | # TODO(dmarting): We mark always as singleton, make it configurable?
141 | "Bundle-SymbolicName: %s;singleton:=true" % name,
142 | "Bundle-Version: " + version,
143 | "Require-Bundle: " + ", ".join(_DECLARED_DEPS),
144 | # TODO(dmarting): Take the java version from java_toolchain.
145 | "Bundle-RequiredExecutionEnvironment: JavaSE-1.8",
146 | "Bundle-ActivationPolicy: lazy",
147 | "Bundle-ClassPath: .",
148 | ] + (
149 | ["Bundle-Activator: " + activator] if activator else []
150 | ) + (
151 | ["Bundle-Vendor: " + vendor] if vendor else []
152 | ) + (kwargs["deploy_manifest_lines"]
153 | if "deploy_manifest_lines" in kwargs else []),
154 | deps = deps,
155 | **args)
156 | # Rename the output to the correct name
157 | native.genrule(
158 | name = name,
159 | srcs = [":%s-bin_deploy.jar" % name],
160 | outs = ["%s_%s.jar" % (name, version)],
161 | cmd = "cp $< $@",
162 | output_to_bindir = 1,
163 | visibility = visibility,
164 | )
165 |
166 |
167 | def _eclipse_feature_impl(ctx):
168 | feature_xml = ctx.new_file(ctx.outputs.out, ctx.label.name + ".xml")
169 | ctx.action(
170 | outputs = [feature_xml],
171 | inputs = [ctx.file.license],
172 | executable = ctx.executable._builder,
173 | arguments = [
174 | "--output=" + feature_xml.path,
175 | "--id=" + ctx.label.name,
176 | "--label=" + ctx.attr.label,
177 | "--version=" + ctx.attr.version,
178 | "--provider=" + ctx.attr.provider,
179 | "--url=" + ctx.attr.url,
180 | "--description=" + ctx.attr.description,
181 | "--copyright=" + ctx.attr.copyright,
182 | "--license_url=" + ctx.attr.license_url,
183 | "--license=" + ctx.file.license.path] + [
184 | "--site=%s=%s" % (site, ctx.attr.sites[site])
185 | for site in ctx.attr.sites] + [
186 | "--plugin=" + p.basename for p in ctx.files.plugins])
187 | ctx.action(
188 | outputs = [ctx.outputs.out],
189 | inputs = [feature_xml],
190 | executable = ctx.executable._zipper,
191 | arguments = ["c",
192 | ctx.outputs.out.path,
193 | "feature.xml=" + feature_xml.path],
194 | )
195 | return struct(
196 | eclipse_feature=struct(
197 | file=ctx.outputs.out,
198 | id=ctx.label.name,
199 | version=ctx.attr.version,
200 | plugins=ctx.files.plugins
201 | )
202 | )
203 |
204 |
205 | eclipse_feature = rule(
206 | implementation=_eclipse_feature_impl,
207 | attrs = {
208 | "label": attr.string(mandatory=True),
209 | "version": attr.string(mandatory=True),
210 | "provider": attr.string(mandatory=True),
211 | "description": attr.string(mandatory=True),
212 | "url": attr.string(mandatory=True),
213 | "copyright": attr.string(mandatory=True),
214 | "license_url": attr.string(mandatory=True),
215 | "license": attr.label(mandatory=True, allow_single_file=True),
216 | "sites": attr.string_dict(),
217 | # TODO(dmarting): restrict what can be passed to the plugins attribute.
218 | "plugins": attr.label_list(),
219 | "_zipper": attr.label(default=Label("@bazel_tools//tools/zip:zipper"),
220 | executable=True,
221 | cfg="host"),
222 | "_builder": attr.label(default=Label("//tools/build_defs:feature_builder"),
223 | executable=True,
224 | cfg="host"),
225 | },
226 | outputs = {"out": "%{name}_%{version}.jar"})
227 | """Create an eclipse feature jar."""
228 |
229 |
230 | def _eclipse_p2updatesite_impl(ctx):
231 | feat_files = [f.eclipse_feature.file for f in ctx.attr.eclipse_features]
232 | args = [
233 | "--output=" + ctx.outputs.out.path,
234 | "--java=" + ctx.executable._java.path,
235 | "--eclipse_launcher=" + ctx.file._eclipse_launcher.path,
236 | "--name=" + ctx.attr.label,
237 | "--url=" + ctx.attr.url,
238 | "--description=" + ctx.attr.description]
239 |
240 | _plugins = {}
241 | for f in ctx.attr.eclipse_features:
242 | args.append("--feature=" + f.eclipse_feature.file.path)
243 | args.append("--feature_id=" + f.eclipse_feature.id)
244 | args.append("--feature_version=" + f.eclipse_feature.version)
245 | for p in f.eclipse_feature.plugins:
246 | if p.path not in _plugins:
247 | _plugins[p.path] = p
248 | plugins = [_plugins[p] for p in _plugins]
249 |
250 | ctx.action(
251 | outputs=[ctx.outputs.out],
252 | inputs=[
253 | ctx.executable._java,
254 | ctx.file._eclipse_launcher,
255 | ] + ctx.files._jdk + ctx.files._eclipse_platform + feat_files + plugins,
256 | executable = ctx.executable._site_builder,
257 | arguments = args + ["--bundle=" + p.path for p in plugins])
258 |
259 |
260 | eclipse_p2updatesite = rule(
261 | implementation=_eclipse_p2updatesite_impl,
262 | attrs = {
263 | "label": attr.string(mandatory=True),
264 | "description": attr.string(mandatory=True),
265 | "url": attr.string(mandatory=True),
266 | "eclipse_features": attr.label_list(providers=["eclipse_feature"]),
267 | "_site_builder": attr.label(
268 | default=Label("//tools/build_defs:site_builder"),
269 | executable=True,
270 | cfg="host"),
271 | "_zipper": attr.label(
272 | default=Label("@bazel_tools//tools/zip:zipper"),
273 | executable=True,
274 | cfg="host"),
275 | "_java": attr.label(
276 | default=Label("@bazel_tools//tools/jdk:java"),
277 | executable=True,
278 | cfg="host"),
279 | "_jdk": attr.label(default=Label("@bazel_tools//tools/jdk:jdk")),
280 | "_eclipse_launcher": attr.label(
281 | default=Label("@org_eclipse_equinox//:launcher"),
282 | allow_single_file=True),
283 | "_eclipse_platform": attr.label(default=Label("@org_eclipse_equinox//:platform")),
284 | },
285 | outputs = {"out": "%{name}.zip"})
286 | """Create an eclipse p2update site inside a ZIP file."""
287 |
--------------------------------------------------------------------------------
/tools/build_defs/eclipse_platform.bzl:
--------------------------------------------------------------------------------
1 | # Copyright 2017 The Bazel Authors. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # The Eclipse website provides SHA-512 but Bazel only support SHA256.
16 | # Really Bazel should start supporting all "safe" checksum (and also
17 | # drop support for SHA-1).
18 | SHA256_SUM={
19 | # TODO(dmarting): we only support 4.5.2 right now because we need to
20 | # download all version of eclipse to provide those checksums...
21 | "4.5.2": {
22 | "macosx-cocoa-x86_64": "755f8a75075f6310a8d0453b5766a84aca2fcc687808341b7a657259230b490f",
23 | "linux-gtk-x86_64": "87f82b0c13c245ee20928557dbc4435657d1e029f72d9135683c8d585c69ba8d"
24 | }
25 | }
26 |
27 | def _get_file_url(version, platform, t):
28 | drop = "drops"
29 | if int(version.split(".", 1)[0]) >= 4:
30 | drop = "drops4"
31 | short_version = version.split("-", 1)[0]
32 | sha256 = ""
33 | if short_version in SHA256_SUM:
34 | if platform in SHA256_SUM[short_version]:
35 | sha256 = SHA256_SUM[short_version][platform]
36 |
37 | filename = "eclipse-SDK-%s-%s.%s" % (short_version, platform, t)
38 | file = "/eclipse/downloads/%s/R-%s/%s" % (
39 | drop,
40 | version,
41 | filename)
42 | # This is a mirror, original base url is http://www.eclipse.org/downloads/download.php?file=
43 | base_url = "https://storage.googleapis.com/bazel-mirror/download.eclipse.org"
44 | return (base_url + file, sha256)
45 |
46 |
47 | def _eclipse_platform_impl(rctx):
48 | version = rctx.attr.version
49 | os_name = rctx.os.name.lower()
50 | if os_name.startswith("mac os"):
51 | platform = "macosx-cocoa-x86_64"
52 | t = "tar.gz"
53 | elif os_name.startswith("linux"):
54 | platform = "linux-gtk-x86_64"
55 | t = "tar.gz"
56 | else:
57 | fail("Cannot fetch Eclipse for platform %s" % rctx.os.name)
58 | url, sha256 = _get_file_url(version, platform, t)
59 | rctx.download_and_extract(url=url, type=t, sha256=sha256)
60 | rctx.file("BUILD.bazel", """
61 | package(default_visibility = ["//visibility:public"])
62 | filegroup(name = "platform", srcs = glob(["**"], exclude = ["BUILD.bazel", "BUILD"]))
63 | filegroup(name = "launcher", srcs = glob(["**/plugins/org.eclipse.equinox.launcher_*.jar"]))
64 | """)
65 |
66 |
67 | eclipse_platform = repository_rule(
68 | implementation = _eclipse_platform_impl,
69 | attrs = {
70 | "version": attr.string(mandatory=True),
71 | }, local=False)
72 | """A repository for downloading the good version eclipse depending on the platform."""
73 |
--------------------------------------------------------------------------------
/tools/build_defs/feature_builder.py:
--------------------------------------------------------------------------------
1 | # Copyright 2017 The Bazel Authors. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | """This tool build feature.xml files describing an Eclipse feature."""
15 |
16 | from xml.etree import ElementTree
17 | from xml.dom import minidom
18 | import gflags
19 | import sys
20 |
21 | gflags.DEFINE_string("output", None, "The output file, mandatory")
22 | gflags.MarkFlagAsRequired("output")
23 |
24 | gflags.DEFINE_string("id", None, "The feature ID, mandatory")
25 | gflags.MarkFlagAsRequired("id")
26 |
27 | gflags.DEFINE_string(
28 | "label",
29 | None,
30 | "The feature label (i.e. short description), mandatory")
31 | gflags.MarkFlagAsRequired("label")
32 |
33 | gflags.DEFINE_string("version", None, "The feature version, mandatory")
34 | gflags.MarkFlagAsRequired("version")
35 |
36 | gflags.DEFINE_string(
37 | "provider", None, "The provider (i.e. the vendor) of the feature, mandatory")
38 | gflags.MarkFlagAsRequired("provider")
39 |
40 | gflags.DEFINE_string(
41 | "url", None, "A URL associated to the description, optional")
42 |
43 | gflags.DEFINE_string(
44 | "description", None, "Description of the feature, mandatory")
45 | gflags.MarkFlagAsRequired("description")
46 |
47 | gflags.DEFINE_string(
48 | "copyright", None, "Copyright line for the repository, mandatory")
49 | gflags.MarkFlagAsRequired("copyright")
50 |
51 | gflags.DEFINE_string(
52 | "license_url", None, "URL pointing to the license, mandatory")
53 | gflags.MarkFlagAsRequired("license_url")
54 |
55 | gflags.DEFINE_string(
56 | "license", None, "Text file of the license of the feature, mandatory")
57 | gflags.MarkFlagAsRequired("license")
58 |
59 | gflags.DEFINE_multistring(
60 | "site", [], "Sites related to the plugin, in the form `label=url`")
61 | gflags.DEFINE_multistring(
62 | "plugin", [], "List of plugins that this feature contains (filename).")
63 |
64 | FLAGS=gflags.FLAGS
65 |
66 | def _plugins(parent, plugins):
67 | for plugin in plugins:
68 | if plugin.endswith(".jar"):
69 | id, version = plugin[:-4].split("_", 1)
70 | p = ElementTree.SubElement(parent, "plugin")
71 | p.set("id", id)
72 | p.set("download-size", "0")
73 | p.set("install-size", "0")
74 | p.set("version", version)
75 |
76 |
77 | def _sites(parent, sites):
78 | for site in sites:
79 | label, url = site.split("=", 1)
80 | p = ElementTree.SubElement(parent, "discovery")
81 | p.set("label", label)
82 | p.set("url", url)
83 |
84 |
85 | def main(unused_argv):
86 | feature = ElementTree.Element("feature")
87 | feature.set("id", FLAGS.id)
88 | feature.set("label", FLAGS.label)
89 | feature.set("version", FLAGS.version)
90 | feature.set("provider-name", FLAGS.provider)
91 | description = ElementTree.SubElement(feature, "description")
92 | if FLAGS.url:
93 | description.set("url", FLAGS.url)
94 | description.text = FLAGS.description
95 | copyright = ElementTree.SubElement(feature, "copyright")
96 | copyright.text = FLAGS.copyright
97 | license = ElementTree.SubElement(feature, "license")
98 | license.set("url", FLAGS.license_url)
99 | with open(FLAGS.license, "r") as f:
100 | license.text = f.read()
101 | _sites(ElementTree.SubElement(feature, "url"), FLAGS.site)
102 | _plugins(feature, FLAGS.plugin)
103 |
104 | # Pretty print the resulting tree
105 | output = ElementTree.tostring(feature, "utf-8")
106 | reparsed = minidom.parseString(output)
107 | with open(FLAGS.output, "w") as f:
108 | f.write(reparsed.toprettyxml(indent=" ", encoding="UTF-8"))
109 |
110 | if __name__ == "__main__":
111 | main(FLAGS(sys.argv))
112 |
--------------------------------------------------------------------------------
/tools/build_defs/site_builder.py:
--------------------------------------------------------------------------------
1 | # Copyright 2017 The Bazel Authors. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | """This tool build a zipped Eclipse p2 update site from features and plugins."""
15 |
16 | import gflags
17 | import os
18 | import os.path
19 | import shutil
20 | import subprocess
21 | import sys
22 | import tempfile
23 | import zipfile
24 |
25 | from xml.etree import ElementTree
26 | from xml.dom import minidom
27 |
28 | gflags.DEFINE_string("output", None, "The output files, mandatory")
29 | gflags.MarkFlagAsRequired("output")
30 |
31 | gflags.DEFINE_multistring(
32 | "feature_id",
33 | [],
34 | "Feature id to include in the site, should come "
35 | "along with --feature and --feature_version.")
36 | gflags.DEFINE_multistring(
37 | "feature",
38 | [],
39 | "Feature file to include in the site, should come "
40 | "along with --feature_id and --feature_version")
41 | gflags.DEFINE_multistring(
42 | "feature_version",
43 | [],
44 | "Version of a feature to include in the site, should "
45 | "come along with --feature and --feature_id")
46 |
47 | gflags.DEFINE_multistring(
48 | "bundle",
49 | [],
50 | "Bundle file to include in the sit")
51 |
52 | gflags.DEFINE_string(
53 | "name",
54 | None,
55 | "The site name (i.e. short description), mandatory")
56 | gflags.MarkFlagAsRequired("name")
57 |
58 | gflags.DEFINE_string("url", None, "A URL for the site, mandatory")
59 | gflags.MarkFlagAsRequired("url")
60 |
61 | gflags.DEFINE_string(
62 | "description", None, "Description of the site, mandatory")
63 | gflags.MarkFlagAsRequired("description")
64 |
65 | gflags.DEFINE_string(
66 | "java",
67 | "java",
68 | "Path to java, optional")
69 |
70 | gflags.DEFINE_string(
71 | "eclipse_launcher",
72 | None,
73 | "Path to the eclipse launcher, mandatory")
74 | gflags.MarkFlagAsRequired("eclipse_launcher")
75 |
76 | FLAGS=gflags.FLAGS
77 |
78 | def _features(parent):
79 | if (len(FLAGS.feature) != len(FLAGS.feature_id)) or (
80 | len(FLAGS.feature) != len(FLAGS.feature_version)):
81 | raise Exception(
82 | "Should provide the same number of "
83 | "time --feature, --feature_id and "
84 | "--feature_version")
85 | for i in range(0, len(FLAGS.feature)):
86 | p = ElementTree.SubElement(parent, "feature")
87 | p.set("url", "feature/%s" % os.path.basename(FLAGS.feature[i]))
88 | p.set("id", FLAGS.feature_id[i])
89 | p.set("version", FLAGS.feature_version[i])
90 |
91 |
92 | def create_site_xml(tmp_dir):
93 | site = ElementTree.Element("site")
94 | description = ElementTree.SubElement(site, "description")
95 | description.set("name", FLAGS.name)
96 | description.set("url", FLAGS.url)
97 | description.text = FLAGS.description
98 | _features(site)
99 |
100 | # Pretty print the resulting tree
101 | output = ElementTree.tostring(site, "utf-8")
102 | reparsed = minidom.parseString(output)
103 | with open(os.path.join(tmp_dir, "site.xml"), "w") as f:
104 | f.write(reparsed.toprettyxml(indent=" ", encoding="UTF-8"))
105 |
106 |
107 | def copy_artifacts(tmp_dir):
108 | feature_dir = os.path.join(tmp_dir, "features")
109 | bundle_dir = os.path.join(tmp_dir, "plugins")
110 | os.mkdir(feature_dir)
111 | os.mkdir(bundle_dir)
112 | for f in FLAGS.feature:
113 | shutil.copyfile(f, os.path.join(feature_dir, os.path.basename(f)))
114 | for p in FLAGS.bundle:
115 | shutil.copyfile(p, os.path.join(bundle_dir, os.path.basename(p)))
116 |
117 |
118 | def generate_metadata(tmp_dir):
119 | tmp_dir2 = tempfile.mkdtemp()
120 |
121 | args = [
122 | FLAGS.java,
123 | "-jar", FLAGS.eclipse_launcher,
124 | "-application", "org.eclipse.equinox.p2.publisher.FeaturesAndBundlesPublisher",
125 | "-metadataRepository", "file:/" + tmp_dir,
126 | "-artifactRepository", "file:/" + tmp_dir,
127 | "-configuration", tmp_dir2,
128 | "-source", tmp_dir,
129 | "-compress", "-publishArtifacts"]
130 | process = subprocess.Popen(args, stdout=subprocess.PIPE)
131 | stdout, _ = process.communicate()
132 | if process.returncode:
133 | sys.stdout.write(stdout)
134 | for root, dirs, files in os.walk(tmp_dir2):
135 | for f in files:
136 | if f.endswith(".log"):
137 | with open(os.path.join(root, f), "r") as fi:
138 | sys.stderr.write("Log %s: %s\n" % (f, fi.read()))
139 | shutil.rmtree(tmp_dir)
140 | sys.exit(process.returncode)
141 | shutil.rmtree(tmp_dir2)
142 |
143 |
144 | def _zipinfo(filename):
145 | result = zipfile.ZipInfo(filename, (1980, 1, 1, 0, 0, 0))
146 | result.external_attr = 0o644 << 16L
147 | return result
148 |
149 | def zip_all(tmp_dir):
150 | with zipfile.ZipFile(FLAGS.output, "w", zipfile.ZIP_DEFLATED) as zf:
151 | for root, dirs, files in os.walk(tmp_dir):
152 | reldir = os.path.relpath(root, tmp_dir)
153 | if reldir == ".":
154 | reldir = ""
155 | for f in files:
156 | with open(os.path.join(root, f), "r") as fi:
157 | zf.writestr(_zipinfo(os.path.join(reldir, f)), fi.read())
158 |
159 |
160 | def main(unused_argv):
161 | tmp_dir = tempfile.mkdtemp()
162 | create_site_xml(tmp_dir)
163 | copy_artifacts(tmp_dir)
164 | generate_metadata(tmp_dir)
165 | zip_all(tmp_dir)
166 | shutil.rmtree(tmp_dir)
167 |
168 |
169 | if __name__ == "__main__":
170 | main(FLAGS(sys.argv))
171 |
--------------------------------------------------------------------------------
/tools/release/BUILD:
--------------------------------------------------------------------------------
1 | sh_binary(
2 | name = "unzip-updatesite",
3 | srcs = ["unzip-updatesite.sh"],
4 | data = ["//:p2updatesite.zip"],
5 | )
6 |
--------------------------------------------------------------------------------
/tools/release/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ROOTDIR="$(dirname $(dirname "$(cd "$(dirname "$0")" && pwd -P)"))"
4 | cd "$ROOTDIR"
5 | RELEASE_ORIGIN=${RELEASE_ORIGIN:-"git@github.com:bazelbuild/eclipse"}
6 |
7 | source version.bzl
8 | # Remove qualifier from VERSION if present
9 | VERSION="${VERSION//\.qualifier}"
10 |
11 | make_version_bzl() {
12 | cat <version.bzl
13 | # Note: do not edit, use tools/release/release.sh script instead.
14 | # This file is both a shell script and a skylark file.
15 | VERSION="$1"
16 | EOF
17 | }
18 |
19 | # Create the release tag
20 | make_release_tag() {
21 | # Detach head
22 | git checkout -q --detach
23 | # Update the version.bzl file
24 | make_version_bzl "${VERSION}"
25 | # Create the commit
26 | git commit -q -m "Release ${VERSION}" version.bzl
27 | # Create the tag
28 | git tag "${VERSION}"
29 | # Checkout back master
30 | git checkout -q master
31 | }
32 |
33 | # Create a commit for increasing the version number
34 | make_release_commit() {
35 | git checkout -q master
36 | make_version_bzl "${1}.qualifier"
37 | git commit -q -m "Update version to ${1}.qualifer after release ${2}" \
38 | version.bzl
39 | }
40 |
41 | # Read the version number from the input
42 | read_version() {
43 | while true; do
44 | echo -n "$1 [$VERSION] "
45 | read ans
46 | if [ -n "$ans" ]; then
47 | if [[ "$ans" =~ [0-9]+(\.[0-9]+)* ]]; then
48 | VERSION="$ans"
49 | return
50 | else
51 | echo "Please enter a version number (e.g. 0.3.0)." >&2
52 | fi
53 | else
54 | return
55 | fi
56 | done
57 | }
58 |
59 | # Produces all possible new versions
60 | incr_version() {
61 | local v=(${1//./ })
62 | for (( i=${#v[@]}-1; $i >= 0; i=$i-1 )); do
63 | local new_v=""
64 | for (( j=0; $j < ${#v[@]}; j=$j+1 )); do
65 | local vj=${v[$j]}
66 | if (( $j == $i )); then
67 | vj=$(( ${v[$j]} + 1))
68 | fi
69 | if [ -n "${new_v}" ]; then
70 | new_v="${new_v}.${vj}"
71 | else
72 | new_v="${vj}"
73 | fi
74 | done
75 | echo "${new_v}"
76 | done
77 | }
78 |
79 | # Push the master branch and the given tag
80 | push_master_and_tag() {
81 | git push "${RELEASE_ORIGIN}" master
82 | git push "${RELEASE_ORIGIN}" "$1"
83 | }
84 |
85 | # Do the release itself, the update site is build on GCCB
86 | release() {
87 | read_version "About to release, which version?"
88 | make_release_tag
89 | local old_version="${VERSION}"
90 | local new_versions=($(incr_version "${VERSION}"))
91 | VERSION=${new_versions[0]}
92 | echo "Possible versions for next releases: ${new_versions[@]}"
93 | read_version "Next version will be?"
94 | make_release_commit "${VERSION}" "${old_version}"
95 | push_master_and_tag "${old_version}"
96 | }
97 |
98 | release
99 |
--------------------------------------------------------------------------------
/tools/release/release.yaml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: gcr.io/cloud-builders/bazel
3 | args: ['run', '//tools/release:unzip-updatesite', '--', '/workspace/bazel-updatesite']
4 | - name: gcr.io/cloud-builders/gsutil
5 | args: ['-m', 'rsync', '-r', '-c', '-d', '/workspace/bazel-updatesite', 'gs://eclipse.bazel.build/updatesite']
6 | - name: gcr.io/cloud-builders/gsutil
7 | args: ['web', 'set', '-m', 'index.html', '-e', '404.html', 'gs://eclipse.bazel.build']
8 | - name: gcr.io/cloud-builders/gsutil
9 | args: ['-m', 'acl', 'ch', '-R', '-u', 'AllUsers:R', 'gs://eclipse.bazel.build']
10 |
11 | timeout: 3600s
12 |
--------------------------------------------------------------------------------
/tools/release/unzip-updatesite.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # A small target to run in Google Cloud Container Builder so the result is unzipped.
3 |
4 | OUTPUT_DIR="${1:-bazel-updatesite}"
5 | RUNFILES="${JAVA_RUNFILES:-$0.runfiles}"
6 |
7 | unzip -d "${OUTPUT_DIR}" "${RUNFILES}/build_bazel_eclipse/p2updatesite.zip"
8 |
--------------------------------------------------------------------------------
/version.bzl:
--------------------------------------------------------------------------------
1 | # Note: do not edit, use tools/release/release.sh script instead.
2 | # This file is both a shell script and a skylark file.
3 | VERSION="0.0.4.qualifier"
4 |
--------------------------------------------------------------------------------