Library for parsing IntelliJ with Bazel project views
This is the first step towards #44.
Change-Id: Ie1943a701eb42687046a7a2e7a797bc812b1c3da
diff --git a/BUILD b/BUILD
index 6142439..932e07a 100644
--- a/BUILD
+++ b/BUILD
@@ -18,6 +18,7 @@
visibility = ["//visibility:public"],
deps = [
"//java/com/google/devtools/bazel/e4b/command",
+ "//java/com/google/devtools/bazel/e4b/projectviews",
"@com_google_guava//jar",
],
)
diff --git a/java/com/google/devtools/bazel/e4b/projectviews/BUILD b/java/com/google/devtools/bazel/e4b/projectviews/BUILD
new file mode 100644
index 0000000..710faf0
--- /dev/null
+++ b/java/com/google/devtools/bazel/e4b/projectviews/BUILD
@@ -0,0 +1,11 @@
+java_library(
+ name = "projectviews",
+ srcs = glob(["*.java"]),
+ visibility = [
+ "//:__pkg__",
+ "//javatests/com/google/devtools/bazel/e4b/projectviews:__pkg__",
+ ],
+ deps = [
+ "@com_google_guava//jar",
+ ],
+)
diff --git a/java/com/google/devtools/bazel/e4b/projectviews/Builder.java b/java/com/google/devtools/bazel/e4b/projectviews/Builder.java
new file mode 100644
index 0000000..9733eda
--- /dev/null
+++ b/java/com/google/devtools/bazel/e4b/projectviews/Builder.java
@@ -0,0 +1,155 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.projectviews;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.nio.file.Files;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+/** A class to create a {@link ProjectView} */
+public class Builder {
+ private ImmutableList.Builder<String> buildFlags = ImmutableList.builder();
+ private ImmutableList.Builder<String> directories = ImmutableList.builder();
+ private ImmutableList.Builder<String> targets = ImmutableList.builder();
+ private int javaLanguageLevel = 0; // <= 0 means take it from java_toolchain.
+
+ Builder() {}
+
+ public Builder addBuildFlag(String... flag) {
+ buildFlags.add(flag);
+ return this;
+ }
+
+ public Builder addDirectory(String... dir) {
+ directories.add(dir);
+ return this;
+ }
+
+ public Builder addTarget(String... target) {
+ targets.add(target);
+ return this;
+ }
+
+ public Builder setJavaLanguageLevel(int level) {
+ Preconditions.checkArgument(level > 0, "Can only set java language level to a value > 0");
+ Preconditions.checkArgument(javaLanguageLevel == 0, "Java language level was already set");
+ javaLanguageLevel = level;
+ return this;
+ }
+
+ // State object using while parsing a stream
+ private String currentSection = null;
+
+ /**
+ * Parse the project view given in view, following also imports.
+ *
+ * @throws IOException
+ * @throws ProjectViewParseException
+ */
+ public Builder parseView(File view) throws IOException, ProjectViewParseException {
+ int linenb = 0;
+ for (String line : Files.readAllLines(view.toPath())) {
+ linenb++;
+ parseLine(view.getPath(), view.getParentFile(), line, linenb);
+ }
+ currentSection = null;
+ return this;
+ }
+
+ /**
+ * Parse the project view at the given {@code url}.
+ *
+ * @throws IOException
+ * @throws ProjectViewParseException
+ */
+ public Builder parseView(URL url) throws IOException, ProjectViewParseException {
+ int linenb = 0;
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
+ for (String line : reader.lines().toArray(String[]::new)) {
+ linenb++;
+ parseLine(url.toString(), null, line, linenb);
+ }
+ }
+ currentSection = null;
+ return this;
+ }
+
+ private void parseLine(String fileName, File parentFile, String line, int linenb)
+ throws ProjectViewParseException, IOException {
+ if (line.isEmpty()) {
+ currentSection = null;
+ } else if (line.startsWith(" ")) {
+ if (currentSection == null) {
+ throw new ProjectViewParseException(
+ "Line " + linenb + " of project view " + fileName + " is not in a section");
+ }
+ if (currentSection.equals("directories")) {
+ directories.add(line.substring(2));
+ } else if (currentSection.equals("targets")) {
+ targets.add(line.substring(2));
+ } else if (currentSection.equals("build_flags")) {
+ buildFlags.add(line.substring(2));
+ } // else ignoring other sections
+ } else if (line.startsWith("import ")) {
+ // imports
+ String path = line.substring(7);
+ if (path.startsWith("/")) {
+ parseView(new File(path));
+ } else {
+ parseView(new File(parentFile, path));
+ }
+ } else if (line.contains(":")) {
+ // section declaration
+ line = line.trim();
+ if (line.endsWith(":")) {
+ currentSection = line.substring(0, line.length() - 1);
+ if (currentSection.equals("java_language_level")) {
+ throw new ProjectViewParseException("Line " + linenb + " of project view "
+ + fileName + ": java_language_level cannot be a section name");
+ }
+ } else {
+ int colonIndex = line.indexOf(':');
+ String label = line.substring(0, colonIndex).trim();
+ String value = line.substring(colonIndex + 1).trim();
+ if (label.equals("directories") || label.equals("import") || label.equals("targets")
+ || label.equals("build_flags")) {
+ throw new ProjectViewParseException("Line " + linenb + " of project view "
+ + fileName + ": " + label + " cannot be a label name");
+ }
+ if (label.equals("java_language_level")) {
+ if (!value.matches("^[0-9]+$")) {
+ throw new ProjectViewParseException("Line " + linenb + " of project view "
+ + fileName + ": java_language_level should be an integer.");
+ }
+ javaLanguageLevel = Integer.parseInt(value);
+ }
+ }
+ } else if (!line.trim().startsWith("#")) {
+ throw new ProjectViewParseException(
+ "Project view " + fileName + " contains a syntax error at line " + linenb);
+ }
+ }
+
+ public ProjectView build() {
+ return new ProjectViewImpl(directories.build(), targets.build(), javaLanguageLevel,
+ buildFlags.build());
+ }
+}
diff --git a/java/com/google/devtools/bazel/e4b/projectviews/ProjectView.java b/java/com/google/devtools/bazel/e4b/projectviews/ProjectView.java
new file mode 100644
index 0000000..8b0cf77
--- /dev/null
+++ b/java/com/google/devtools/bazel/e4b/projectviews/ProjectView.java
@@ -0,0 +1,55 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.projectviews;
+
+import java.util.List;
+
+/**
+ * This is an interface to defined a project view from the IntelliJ plugin for Bazel
+ * (https://ij.bazel.build) so that project view can be shared between IntelliJ and Eclipse users.
+ *
+ * <p>
+ * See http://ij.bazel.build/docs/project-views.html for the specification of the project view. This
+ * project view support only a subset relevant for the Eclipse plugin.
+ */
+public interface ProjectView {
+ /**
+ * List of directories defined in the {@code directories} section of the project view. These are
+ * the directories to include as source directories.
+ */
+ public List<String> getDirectories();
+
+ /**
+ * List of targets to build defined in the {@code targets} section of the project view.
+ */
+ public List<String> getTargets();
+
+ /**
+ * Return a number (e.g. 7) giving the java version that the IDE should support (section
+ * {@code java_language_level} of the project view).
+ */
+ public int getJavaLanguageLevel();
+
+ /**
+ * List of build flags to pass to Bazel defined in the {@code build_flags} section of the project
+ * view.
+ */
+ public List<String> getBuildFlags();
+
+ /** Returns a builder to construct an project view object. */
+ public static Builder builder() {
+ return new Builder();
+ }
+}
diff --git a/java/com/google/devtools/bazel/e4b/projectviews/ProjectViewImpl.java b/java/com/google/devtools/bazel/e4b/projectviews/ProjectViewImpl.java
new file mode 100644
index 0000000..fc5ad25
--- /dev/null
+++ b/java/com/google/devtools/bazel/e4b/projectviews/ProjectViewImpl.java
@@ -0,0 +1,77 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.projectviews;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+/** Implementation of the ProjectView object */
+final class ProjectViewImpl implements ProjectView {
+
+ private final ImmutableList<String> directories;
+ private final ImmutableList<String> targets;
+ private final int javaLanguageLevel;
+ private final ImmutableList<String> buildFlags;
+
+ public ProjectViewImpl(List<String> directories, List<String> targets, int javaLanguageLevel,
+ List<String> buildFlags) {
+ this.directories = ImmutableList.copyOf(directories);
+ this.targets = ImmutableList.copyOf(targets);
+ this.javaLanguageLevel = javaLanguageLevel;
+ this.buildFlags = ImmutableList.copyOf(buildFlags);
+ }
+
+ @Override
+ public List<String> getDirectories() {
+ return directories;
+ }
+
+ @Override
+ public List<String> getTargets() {
+ return targets;
+ }
+
+ @Override
+ public int getJavaLanguageLevel() {
+ return javaLanguageLevel;
+ }
+
+ @Override
+ public List<String> getBuildFlags() {
+ return buildFlags;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer result = new StringBuffer();
+ addList(result, directories, "directories");
+ addList(result, targets, "targets");
+ if (javaLanguageLevel > 0) {
+ result.append("java_language_level: ").append(javaLanguageLevel).append("\n\n");
+ }
+ addList(result, buildFlags, "build_flags");
+ return result.toString();
+ }
+
+ private static void addList(StringBuffer result, List<String> list, String header) {
+ if (!list.isEmpty()) {
+ result.append(header).append(":\n");
+ for (String el : list) {
+ result.append(" ").append(el).append("\n");
+ }
+ result.append("\n");
+ }
+ }
+}
diff --git a/java/com/google/devtools/bazel/e4b/projectviews/ProjectViewParseException.java b/java/com/google/devtools/bazel/e4b/projectviews/ProjectViewParseException.java
new file mode 100644
index 0000000..a3f663d
--- /dev/null
+++ b/java/com/google/devtools/bazel/e4b/projectviews/ProjectViewParseException.java
@@ -0,0 +1,26 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.projectviews;
+
+/**
+ * An exception thrown when failing to parse a project view.
+ */
+public class ProjectViewParseException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ ProjectViewParseException(String msg) {
+ super(msg);
+ }
+}
diff --git a/javatests/com/google/devtools/bazel/e4b/projectviews/BUILD b/javatests/com/google/devtools/bazel/e4b/projectviews/BUILD
new file mode 100644
index 0000000..84b2aac
--- /dev/null
+++ b/javatests/com/google/devtools/bazel/e4b/projectviews/BUILD
@@ -0,0 +1,11 @@
+java_test(
+ name = "ProjectViewTest",
+ srcs = ["ProjectViewTest.java"],
+ resources = ["bazel.projectview"],
+ deps = [
+ "//java/com/google/devtools/bazel/e4b/projectviews",
+ "@com_google_truth//jar",
+ "@org_hamcrest_core//jar",
+ "@org_junit//jar",
+ ],
+)
diff --git a/javatests/com/google/devtools/bazel/e4b/projectviews/ProjectViewTest.java b/javatests/com/google/devtools/bazel/e4b/projectviews/ProjectViewTest.java
new file mode 100644
index 0000000..1d4b3a5
--- /dev/null
+++ b/javatests/com/google/devtools/bazel/e4b/projectviews/ProjectViewTest.java
@@ -0,0 +1,37 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.projectviews;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.junit.Test;
+
+/** {@link ProjectView}Test */
+public class ProjectViewTest {
+
+ @Test
+ public void testBazelView() throws IOException, ProjectViewParseException, URISyntaxException {
+ ProjectView view = ProjectView.builder()
+ .parseView(ProjectViewTest.class.getResource("bazel.projectview")).build();
+ assertThat(view.getBuildFlags()).isEmpty();
+ assertThat(view.getDirectories()).containsExactly(".");
+ assertThat(view.getJavaLanguageLevel()).isEqualTo(0);
+ assertThat(view.getTargets()).containsAllOf("//src:bazel", "//src/test/...");
+ }
+
+}
diff --git a/javatests/com/google/devtools/bazel/e4b/projectviews/bazel.projectview b/javatests/com/google/devtools/bazel/e4b/projectviews/bazel.projectview
new file mode 100644
index 0000000..9b1c8bf
--- /dev/null
+++ b/javatests/com/google/devtools/bazel/e4b/projectviews/bazel.projectview
@@ -0,0 +1,22 @@
+# Setup IntelliJ for Bazel development using the IntelliJ Bazel Plugin.
+# From github.com/bazelbuild/bazel's scripts/ij.projectview at commit 815bd634df0350a227133244c734b5c3672776ab.
+directories:
+ .
+
+test_sources:
+ src/java_tools/buildjar/javatests/*
+ src/java_tools/junitrunner/javatests/*
+ src/java_tools/singlejar/javatests/*
+ src/test/*
+
+targets:
+ //src:bazel
+ //src/java_tools/buildjar:JavaBuilder
+ //src/java_tools/buildjar:VanillaJavaBuilder
+ //src/java_tools/buildjar/javatests/...
+ //src/java_tools/junitrunner/java/com/google/testing/junit/runner:Runner
+ //src/java_tools/junitrunner/javatests/...
+ //src/java_tools/singlejar:SingleJar
+ //src/java_tools/singlejar:tests
+ //src/test/...
+ //src/tools/remote_worker
\ No newline at end of file