diff --git a/com.salesforce.b2eclipse.jdt.ls/resources/bzleclipse_aspect.bzl b/com.salesforce.b2eclipse.jdt.ls/resources/bzleclipse_aspect.bzl
index f1d1766..3d4dce8 100644
--- a/com.salesforce.b2eclipse.jdt.ls/resources/bzleclipse_aspect.bzl
+++ b/com.salesforce.b2eclipse.jdt.ls/resources/bzleclipse_aspect.bzl
@@ -14,7 +14,6 @@
# limitations under the License.
#
-
# Aspect for Bazel Eclipse Feature, taken from an early version of intellij_info.bzl
# TODO upgrade this to their latest work
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/config/BazelEclipseProjectFactory.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/config/BazelEclipseProjectFactory.java
index 8f425cd..2c3f3d7 100644
--- a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/config/BazelEclipseProjectFactory.java
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/config/BazelEclipseProjectFactory.java
@@ -77,7 +77,7 @@
import com.salesforce.b2eclipse.model.AspectPackageInfo;
import com.salesforce.b2eclipse.model.AspectPackageInfos;
import com.salesforce.b2eclipse.model.BazelLabel;
-import com.salesforce.b2eclipse.model.BazelPackageInfo;
+import com.salesforce.bazel.sdk.model.BazelPackageInfo;
import com.salesforce.b2eclipse.runtime.api.ResourceHelper;
/**
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/config/ImportOrderResolver.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/config/ImportOrderResolver.java
index b61e951..e2909a5 100644
--- a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/config/ImportOrderResolver.java
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/config/ImportOrderResolver.java
@@ -31,7 +31,7 @@
import com.google.common.graph.Traverser;
import com.salesforce.b2eclipse.model.AspectPackageInfo;
import com.salesforce.b2eclipse.model.AspectPackageInfos;
-import com.salesforce.b2eclipse.model.BazelPackageInfo;
+import com.salesforce.bazel.sdk.model.BazelPackageInfo;
final class ImportOrderResolver {
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/importer/BazelProjectImportScanner.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/importer/BazelProjectImportScanner.java
index 1ad0003..7882e7a 100644
--- a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/importer/BazelProjectImportScanner.java
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/importer/BazelProjectImportScanner.java
@@ -46,7 +46,7 @@
import com.salesforce.b2eclipse.BazelJdtPlugin;
import com.salesforce.b2eclipse.command.BazelCommandManager;
import com.salesforce.b2eclipse.command.BazelWorkspaceCommandRunner;
-import com.salesforce.b2eclipse.model.BazelPackageInfo;
+import com.salesforce.bazel.sdk.model.BazelPackageInfo;
import com.salesforce.b2eclipse.runtime.impl.EclipseWorkProgressMonitor;
/**
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/managers/BazelProjectImporter.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/managers/BazelProjectImporter.java
index 26a9790..efbfdc1 100644
--- a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/managers/BazelProjectImporter.java
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/managers/BazelProjectImporter.java
@@ -34,9 +34,14 @@
package com.salesforce.b2eclipse.managers;
import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.core.runtime.CoreException;
@@ -44,18 +49,19 @@
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.ls.core.internal.AbstractProjectImporter;
-import com.salesforce.b2eclipse.BazelJdtPlugin;
import com.salesforce.b2eclipse.abstractions.WorkProgressMonitor;
-import com.salesforce.b2eclipse.command.BazelCommandManager;
import com.salesforce.b2eclipse.config.BazelEclipseProjectFactory;
-import com.salesforce.b2eclipse.importer.BazelProjectImportScanner;
-import com.salesforce.b2eclipse.model.BazelPackageInfo;
+import com.salesforce.bazel.sdk.model.BazelPackageInfo;
import com.salesforce.b2eclipse.runtime.impl.EclipseWorkProgressMonitor;
+import com.salesforce.bazel.sdk.project.ProjectView;
+import com.salesforce.bazel.sdk.workspace.BazelWorkspaceScanner;
@SuppressWarnings("restriction")
public final class BazelProjectImporter extends AbstractProjectImporter {
private static final String WORKSPACE_FILE_NAME = "WORKSPACE";
+
+ private static final String BAZELPROJECT_FILE_NAME = ".bazelproject";
@Override
public boolean applies(IProgressMonitor monitor) throws OperationCanceledException, CoreException {
@@ -79,26 +85,52 @@ public boolean applies(IProgressMonitor monitor) throws OperationCanceledExcepti
@Override
public void importToWorkspace(IProgressMonitor monitor) throws OperationCanceledException, CoreException {
- BazelCommandManager bazelCommandManager = BazelJdtPlugin.getBazelCommandManager();
- BazelProjectImportScanner scanner = new BazelProjectImportScanner(bazelCommandManager, rootFolder);
-
- BazelPackageInfo workspaceRootPackage = scanner.getProjects(monitor);
-
+ try {
+ BazelWorkspaceScanner workspaceScanner = new BazelWorkspaceScanner();
+ BazelPackageInfo workspaceRootPackage = workspaceScanner.getPackages(rootFolder);
+
if (workspaceRootPackage == null) {
throw new IllegalArgumentException();
}
- List bazelPackagesToImport =
- workspaceRootPackage.getChildPackageInfos().stream().collect(Collectors.toList());
+
+ List allBazelPackages = new ArrayList<>(
+ workspaceRootPackage.getChildPackageInfos()
+ );
+
+ List bazelPackagesToImport = allBazelPackages;
+
+ File targetsFile = new File(rootFolder, BAZELPROJECT_FILE_NAME);
+
+ if (targetsFile.exists()) {
+ ProjectView projectView = new ProjectView(rootFolder, readFile(targetsFile.getPath()));
+
+ Set projectViewPaths = projectView.getDirectories().stream()
+ .map(p -> p.getBazelPackageFSRelativePath()).collect(Collectors.toSet());
+
+ bazelPackagesToImport = allBazelPackages.stream().filter(bpi -> projectViewPaths.contains(bpi.getBazelPackageFSRelativePath()))
+ .collect(Collectors.toList());
+ }
WorkProgressMonitor progressMonitor = new EclipseWorkProgressMonitor(null);
BazelEclipseProjectFactory.importWorkspace(workspaceRootPackage, bazelPackagesToImport, progressMonitor,
monitor);
+ } catch (IOException e) {
+ // TODO: proper handling here
+ }
}
@Override
public void reset() {
}
+
+ private static String readFile(String path) {
+ try {
+ return new String(Files.readAllBytes(Paths.get(path)));
+ } catch (IOException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/logging/BasicLoggerFacade.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/logging/BasicLoggerFacade.java
new file mode 100644
index 0000000..1398d96
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/logging/BasicLoggerFacade.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2019, Salesforce.com, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Copyright 2016 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.salesforce.bazel.sdk.logging;
+
+// we will revisit this later, see https://github.com/salesforce/bazel-eclipse/issues/10
+// import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * Default facade that crudely logs to stdout/stderr.
+ *
+ */
+public class BasicLoggerFacade extends LoggerFacade {
+
+ @Override
+ public void error(Class> from, String message, Object... args) {
+ // LoggerFactory.getLogger(from).error(message, args);
+ System.err.println(formatMsg(from, message, args));
+ }
+
+ @Override
+ public void error(Class> from, String message, Throwable exception, Object... args) {
+ //LoggerFactory.getLogger(from).error(message, exception, args);
+ System.err.println(formatMsg(from, message, args));
+ }
+
+ @Override
+ public void warn(Class> from, String message, Object... args) {
+ // LoggerFactory.getLogger(from).warn(message, args);
+ System.out.println(formatMsg(from, message, args));
+ }
+
+ @Override
+ public void info(Class> from, String message, Object... args) {
+ // LoggerFactory.getLogger(from).info(message, args);
+ System.out.println(formatMsg(from, message, args));
+ }
+
+ @Override
+ public void debug(Class> from, String message, Object... args) {
+ // LoggerFactory.getLogger(from).debug(message, args);
+ // System.out.println(formatMsg(from, message, args));
+ }
+
+ private String formatMsg(Class> from, String message, Object... args) {
+ return "[" + from.getName() + "] " + message;
+ }
+
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/logging/LogHelper.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/logging/LogHelper.java
new file mode 100644
index 0000000..b7fa48e
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/logging/LogHelper.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2019, Salesforce.com, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Copyright 2016 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.salesforce.bazel.sdk.logging;
+
+/**
+ * Helper to log messages. Doesn't cache anything but class name for common logging frameworks. This allows the
+ * LoggerFacade to be changed and without having to constantly give the class.
+ *
+ * This is the preferred way to log.
+ *
+ * @author Blaine Buxton
+ *
+ */
+public class LogHelper {
+ final Class> from;
+
+ public static LogHelper log(Class> from) {
+ return new LogHelper(from);
+ }
+
+ private LogHelper(Class> from) {
+ this.from = from;
+ }
+
+ public void error(String message, Object... args) {
+ getFacade().error(from, message, args);
+ }
+
+ public void error(String message, Throwable exception, Object... args) {
+ getFacade().error(from, message, exception, args);
+ }
+
+ public void warn(String message, Object... args) {
+ getFacade().warn(from, message, args);
+ }
+
+ public void info(String message, Object... args) {
+ getFacade().info(from, message, args);
+ }
+
+ public void debug(String message, Object... args) {
+ getFacade().debug(from, message, args);
+ }
+
+ private LoggerFacade getFacade() {
+ return LoggerFacade.instance();
+ }
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/logging/LoggerFacade.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/logging/LoggerFacade.java
new file mode 100644
index 0000000..af89c05
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/logging/LoggerFacade.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright (c) 2019, Salesforce.com, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Copyright 2016 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.salesforce.bazel.sdk.logging;
+
+/**
+ * Logger facade.
+ *
+ * This is an interface so that tests can use it and verify logging
+ */
+public abstract class LoggerFacade {
+ static LoggerFacade instance = new BasicLoggerFacade();
+
+ /**
+ * Default instance, this can change - DO NOT CACHE or STORE
+ *
+ * @return
+ */
+ public static LoggerFacade instance() {
+ return instance;
+ }
+
+ /**
+ * log error
+ *
+ * @param from
+ * @param message
+ * @param args
+ */
+ public abstract void error(Class> from, String message, Object... args);
+
+ /**
+ * Log error with exception stack trace
+ *
+ * @param from
+ * @param message
+ * @param exception
+ * @param args
+ */
+ public abstract void error(Class> from, String message, Throwable exception, Object... args);
+
+ /**
+ * Log warn message
+ *
+ * @param from
+ * @param message
+ * @param args
+ */
+ public abstract void warn(Class> from, String message, Object... args);
+
+ /**
+ * Log info message
+ *
+ * @param from
+ * @param message
+ * @param args
+ */
+ public abstract void info(Class> from, String message, Object... args);
+
+ /**
+ * Log debug message
+ *
+ * @param from
+ * @param message
+ * @param args
+ */
+ public abstract void debug(Class> from, String message, Object... args);
+
+ public static void setInstance(LoggerFacade newFacade) {
+ instance = newFacade;
+ }
+
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelBuildFileHelper.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelBuildFileHelper.java
new file mode 100644
index 0000000..8274b1e
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelBuildFileHelper.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2019, Salesforce.com, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.salesforce.bazel.sdk.model;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.salesforce.bazel.sdk.logging.LogHelper;
+
+public class BazelBuildFileHelper {
+ static final LogHelper LOG = LogHelper.log(BazelBuildFileHelper.class);
+ /**
+ * List of Strings that can be found in BUILD files that will indicate a Bazel package that is supported by the
+ * Eclipse plugin. Currently, only Java packages are supported.
+ *
+ * The line must begin with one of these tokens (leading whitespace is ignored). This prevents false positives when
+ * comments include one of these tokens. Also, this means that just loading a Java rule in a load() statement is not
+ * enough to trigger the detector.
+ */
+ public static final String[] JAVA_PROJECT_INDICATORS =
+ { "java_binary", "java_library", "java_test", "java_web_test_suite", "springboot", "springboot_test",
+ "java_proto_library", "java_lite_proto_library", "java_grpc_library" };
+
+ /**
+ * Parses a File, presumed to be a Bazel BUILD file, looking for indications that it contains Java rules.
+ *
+ * @param buildFile
+ * @return true if it contains at least one Java rule, false if not
+ */
+ public static boolean hasJavaRules(File buildFile) {
+ boolean hasJavaRules = false;
+
+ if (!buildFile.exists() || !buildFile.canRead()) {
+ return false;
+ }
+
+ try (InputStream is = new FileInputStream(buildFile)) {
+ hasJavaRules = hasJavaRules(is);
+ } catch (Exception anyE) {
+ LOG.error(anyE.getMessage(), anyE);
+ }
+ return hasJavaRules;
+ }
+
+ /**
+ * Parses an InputStream, presumed to be the contents of a Bazel BUILD file, looking for indications that it
+ * contains Java rules.
+ *
+ * @param is
+ * @return true if it contains at least one Java rule, false if not
+ */
+ public static boolean hasJavaRules(InputStream is) {
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
+ String buildFileLine = br.readLine();
+ while (buildFileLine != null) {
+ if (hasJavaRulesInLine(buildFileLine)) {
+ return true;
+ }
+ buildFileLine = br.readLine();
+ }
+
+ } catch (IOException e) {
+ LOG.error(e.getMessage(), e);
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ static boolean hasJavaRulesInLine(String buildFileLine) {
+ buildFileLine = buildFileLine.trim();
+ if (Arrays.stream(JAVA_PROJECT_INDICATORS).parallel().anyMatch(buildFileLine::startsWith)) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelLabel.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelLabel.java
new file mode 100644
index 0000000..d8a28e0
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelLabel.java
@@ -0,0 +1,317 @@
+/**
+ * Copyright (c) 2019, Salesforce.com, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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
+ * 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.salesforce.bazel.sdk.model;
+
+/**
+ * Answers to everything you've always wanted to ask a Bazel Label.
+ *
+ * Pass this around in code instead of String primitives.
+ *
+ * IMPORTANT NOTE internally this class assumes that '/' is the path separator used in label/target names. This
+ * will most likely need to get fixed to support running on Windows.
+ *
+ *
+ * @author stoens
+ * @since Hawaii 2019
+ */
+public class BazelLabel {
+
+ private final String localLabelPart;
+ private final String repositoryName;
+ private final String fullLabel;
+
+ /**
+ * A BazelLabel instance can be created with any syntactically valid Bazel Label String.
+ *
+ * Examples:
+ * //foo/blah:t1
+ * //foo
+ * blah/...
+ */
+ public BazelLabel(String label) {
+ if (label == null) {
+ throw new IllegalArgumentException("label cannot be null");
+ }
+ if (label.startsWith("@")) {
+ int i = label.indexOf("//");
+ this.repositoryName = label.substring(1, i);
+ label = label.substring(i);
+ } else {
+ this.repositoryName = null;
+ }
+ label = sanitizeLabel(label);
+ this.localLabelPart = label;
+ this.fullLabel = getFullLabel(this.repositoryName, this.localLabelPart);
+ }
+
+ /**
+ * Instantiates a BazelLabel instance with the Bazel package path and the label
+ * name specified separately. For example: "a/b/c" and "my-target-name".
+ */
+ public BazelLabel(String packagePath, String targetName) {
+ this(sanitizePackagePath(packagePath) + ":" + sanitizeTargetName(targetName));
+ }
+
+ /**
+ * Returns the label as a String.
+ *
+ * @return the label
+ */
+ public String getLabel() {
+ return fullLabel;
+ }
+
+ /**
+ * Returns the repository of this label, null if no repository was specified for this label.
+ *
+ * @return the repository name, without the leading '@' and trailing "//"
+ */
+ public String getRepositoryName() {
+ return repositoryName;
+ }
+
+
+ /**
+ * If a label omits the target name it refers to and it doesn't use wildcard syntax, it refers to the
+ * package-default target. This is that target that has the same name as the Bazel Package it lives in.
+ *
+ * @return true if this instance points to the package default target, false otherwise
+ */
+ public boolean isPackageDefault() {
+ if (!isConcrete()) {
+ return false;
+ }
+ int i = this.localLabelPart.lastIndexOf(":");
+ return i == -1;
+ }
+
+ /**
+ * If a label refers to a single Bazel Target, is it concrete. If it using wildcard syntax, it is not concrete.
+ *
+ * @return true if this instance represents a concrete label, false otherwise
+ */
+ public boolean isConcrete() {
+ return !(this.localLabelPart.endsWith("*") ||
+ this.localLabelPart.endsWith("...") ||
+ this.localLabelPart.endsWith("all"));
+ }
+
+ /**
+ * Returns the package path of this label, which is the "path part" of the label, excluding any specific target or
+ * target wildcard pattern.
+ *
+ * For example, given a label //foo/blah/goo:t1, the package path is foo/blah/goo.
+ *
+ * @return the package path of this label
+ */
+ public String getPackagePath() {
+ String packagePath = this.localLabelPart;
+ int i = packagePath.lastIndexOf("...");
+ if (i != -1) {
+ packagePath = packagePath.substring(0, i);
+ if (packagePath.endsWith("/")) {
+ packagePath = packagePath.substring(0, packagePath.length() - 1);
+ }
+ } else {
+ i = this.localLabelPart.lastIndexOf(":");
+ if (i != -1) {
+ packagePath = packagePath.substring(0, i);
+ }
+ }
+ return packagePath;
+ }
+
+ /**
+ * Returns the default package label for this label. The default package label does not specify an explicit target
+ * and only corresponds to the package path.
+ *
+ * For example, given //foo/blah/goo:t1, the corresponding default package label is //foo/blah/goo.
+ *
+ * @return BazelLabel instance representing the default package label.
+ * @throws IllegalArgumentException
+ * if this label is a root-level label (//...) and therefore doesn't have a package path.
+ */
+ public BazelLabel toDefaultPackageLabel() {
+ return withRepositoryNameAndLocalLabelPart(repositoryName, getPackagePath());
+ }
+
+ /**
+ * Returns the package name of this label, which is the right-most path component of the package path.
+ *
+ * For example, given a label //foo/blah/goo:t1, the package name is goo.
+ *
+ * @return the package name of this label
+ */
+ public String getPackageName() {
+ String packagePath = getPackagePath();
+ int i = packagePath.lastIndexOf("/");
+ return i == -1 ? packagePath : packagePath.substring(i + 1);
+ }
+
+ /**
+ * Returns the target part of this label.
+ *
+ * @return the target name this label refers to, null if this label uses "..." syntax.
+ */
+ public String getTargetName() {
+ if (localLabelPart.endsWith("...")) {
+ return null;
+ }
+ if (isPackageDefault()) {
+ return getPackageName();
+ } else {
+ int i = localLabelPart.lastIndexOf(":");
+ // label cannot end with ":", so this is ok
+ return localLabelPart.substring(i + 1);
+ }
+ }
+
+ /**
+ * Some Bazel Target names use a path-like syntax. This method returns the last component of that path. If the
+ * target name doesn't use a path-like syntax, this method returns the target name.
+ *
+ * For example: if the target name is "a/b/c/d", this method returns "d". if the target name is "a/b/c/", this
+ * method returns "c". if the target name is "foo", this method returns "foo".
+ *
+ * @return the last path component of the target name if the target name is path-like
+ */
+ public String getLastComponentOfTargetName() {
+ String targetName = getTargetName();
+ if (targetName == null) {
+ return null;
+ }
+ int i = targetName.lastIndexOf("/");
+ if (i != -1) {
+ return targetName.substring(i + 1); // ok because target name cannot end with '/'
+ }
+ return targetName;
+ }
+
+ /**
+ * Adds package wildcard syntax to a package default label.
+ *
+ * For example: //foo/blah -> //foo:blah:*
+ *
+ * @return a new BazelLabel instance, with added package wildcard syntax
+ * @throws IllegalStateException
+ * if this is not a package default label
+ */
+ public BazelLabel toPackageWildcardLabel() {
+ if (!isPackageDefault()) {
+ throw new IllegalStateException("label " + this.localLabelPart + " is not package default");
+ }
+ return withRepositoryNameAndLocalLabelPart(repositoryName, getPackagePath() + ":*");
+ }
+
+ @Override
+ public int hashCode() {
+ return fullLabel.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof BazelLabel) {
+ BazelLabel o = (BazelLabel) other;
+ return fullLabel.equals(o.fullLabel);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return this.fullLabel;
+ }
+
+ private static BazelLabel withRepositoryNameAndLocalLabelPart(String repositoryName, String localLabelPart) {
+ return repositoryName == null ?
+ new BazelLabel(localLabelPart) :
+ new BazelLabel("@" + repositoryName + "//" + localLabelPart);
+ }
+
+ private static String sanitizePackagePath(String path) {
+ if (path == null) {
+ throw new IllegalAccessError(path);
+ }
+ path = path.trim();
+ if (path.endsWith("/")) {
+ path = path.substring(0, path.length() - 1);
+ }
+ return path;
+ }
+
+ private static String sanitizeTargetName(String target) {
+ if (target == null) {
+ throw new IllegalArgumentException(target);
+ }
+ target = target.trim();
+ if (target.startsWith(":")) {
+ target = target.substring(1);
+ }
+ return target;
+ }
+
+ private static String sanitizeLabel(String label) {
+ if (label == null) {
+ throw new IllegalArgumentException(label);
+ }
+ label = label.trim();
+ if (label.length() == 0) {
+ throw new IllegalArgumentException(label);
+ }
+ if (label.endsWith(":")) {
+ throw new IllegalArgumentException(label);
+ }
+ if (label.endsWith("/")) {
+ throw new IllegalArgumentException(label);
+ }
+ if (label.equals("//")) {
+ throw new IllegalArgumentException(label);
+ }
+ if (label.startsWith("//")) {
+ label = label.substring(2);
+ }
+ if (label.startsWith("/")) {
+ label = label.substring(1);
+ }
+ return label;
+ }
+
+ private static String getFullLabel(String repositoryName, String localLabelPart) {
+ return (repositoryName == null ? "" : "@" + repositoryName) + "//" + localLabelPart;
+ }
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/model/BazelPackageInfo.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelPackageInfo.java
similarity index 86%
rename from com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/model/BazelPackageInfo.java
rename to com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelPackageInfo.java
index 72acd71..53f4a88 100644
--- a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/b2eclipse/model/BazelPackageInfo.java
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelPackageInfo.java
@@ -20,7 +20,7 @@
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
+ *
* 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
*
@@ -31,15 +31,14 @@
* specific language governing permissions and limitations under the License.
*
*/
-package com.salesforce.b2eclipse.model;
+package com.salesforce.bazel.sdk.model;
import java.io.File;
-import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
/**
* Model class for a Bazel Java package. It is a node in a tree of the hierarchy of packages. The root node in this tree
@@ -56,21 +55,21 @@
* WORKSPACE root (BazelPackageInfo instance 1)
* //projects/libs/apple (BazelPackageInfo instance 2)
* //projects/libs/banana (BazelPackageInfo instance 3)
- *
+ *
* @author plaird
*/
-public class BazelPackageInfo {
+public class BazelPackageInfo implements BazelPackageLocation {
private final String relativeWorkspacePath;
private final File directory;
private BazelPackageInfo parent;
private final boolean isWorkspaceRoot;
- private final File workspaceRoot;
- private BazelPackageInfo workspaceRootNode;
+ protected final File workspaceRoot;
+ protected BazelPackageInfo workspaceRootNode;
public static final String WORKSPACE_FILENAME = "WORKSPACE";
- public static final String BUILD_FILENAME = "BUILD";
+ public static final String WORKSPACE_FILENAME_ALT = "WORKSPACE.bazel";
private String computedPackageName = null;
private String computedPackageNameLastSegment = null;
@@ -80,7 +79,7 @@ public class BazelPackageInfo {
/**
* Creates the root info object for a Bazel workspace. This is not normally associated with an actual Bazel package
* (hopefully not), so it is a special case node. All other info nodes descend from this node.
- *
+ *
* @param rootDirectory
* the file system location that holds the workspace. This directory must have a WORKSPACE file.
*/
@@ -97,13 +96,11 @@ public BazelPackageInfo(File rootDirectory) {
this.workspaceRoot = rootDirectory;
File workspaceFile = new File(this.workspaceRoot, WORKSPACE_FILENAME);
if (!workspaceFile.exists()) {
- throw new IllegalArgumentException("The path [" + rootDirectory.getAbsolutePath() + "] does not contain a "
- + WORKSPACE_FILENAME + " file.");
- }
-
- File buildFile = new File(this.workspaceRoot, BUILD_FILENAME);
- if (buildFile.exists()) {
- throw new IllegalStateException("Root package is not supported. BUILD files should be in subdirectories");
+ workspaceFile = new File(this.workspaceRoot, WORKSPACE_FILENAME_ALT);
+ if (!workspaceFile.exists()) {
+ throw new IllegalArgumentException("The path [" + rootDirectory.getAbsolutePath()
+ + "] does not contain a " + WORKSPACE_FILENAME + " file.");
+ }
}
this.parent = null;
@@ -118,7 +115,7 @@ public BazelPackageInfo(File rootDirectory) {
/**
* Creates a new info object for a Bazel package
- *
+ *
* @param anotherNode
* another node of the BazelPackageInfo tree, this cannot be null. The 'best' parent node for this new
* node will be found using the passed node's links to the other nodes in the tree. The parent package
@@ -161,6 +158,12 @@ public BazelPackageInfo(BazelPackageInfo anotherNode, String relativeWorkspacePa
"The path [" + this.directory.getAbsolutePath() + "] contains a " + WORKSPACE_FILENAME
+ " file. Nested workspaces are not supported by BazelPackageInfo at this time");
}
+ workspaceFile = new File(this.directory, WORKSPACE_FILENAME_ALT);
+ if (workspaceFile.exists()) {
+ throw new IllegalArgumentException(
+ "The path [" + this.directory.getAbsolutePath() + "] contains a " + WORKSPACE_FILENAME_ALT
+ + " file. Nested workspaces are not supported by BazelPackageInfo at this time");
+ }
// compute and cache the package name
String packageName = getBazelPackageName();
@@ -197,18 +200,20 @@ public Collection getChildPackageInfos() {
/**
* Is this node the workspace root?
- *
+ *
* @return true if the root, false otherwise
*/
+ @Override
public boolean isWorkspaceRoot() {
return this.isWorkspaceRoot;
}
/**
* Gets the workspace root filesystem directory.
- *
+ *
* @return the root directory
*/
+ @Override
public File getWorkspaceRootDirectory() {
// now is a good time to check that the root directory is still there
if (!this.workspaceRoot.exists()) {
@@ -221,7 +226,7 @@ public File getWorkspaceRootDirectory() {
/**
* Gets the WORKSPACE file
- *
+ *
* @return the file
*/
public File getWorkspaceFile() {
@@ -244,7 +249,7 @@ public File getWorkspaceFile() {
* Returns the absolute file system path of the package in the workspace. The separator char will be the OS file
* separator.
*
- *
+ *
* e.g. "/home/joe/dev/projects/libs/apple" or "C:\dev\projects\libs\apple"
*/
public String getBazelPackageFSAbsolutePath() {
@@ -256,9 +261,10 @@ public String getBazelPackageFSAbsolutePath() {
* Returns the relative file system path of the package in the workspace. The separator char will be the OS file
* separator.
*
- *
+ *
* e.g. "projects/libs/apple" or "projects\libs\apple"
*/
+ @Override
public String getBazelPackageFSRelativePath() {
return relativeWorkspacePath;
}
@@ -287,9 +293,10 @@ public String getBazelPackageFSRelativePathForUI() {
/**
* Provides the proper Bazel label for the Bazel package.
*
- *
+ *
* e.g. "//projects/libs/apple"
*/
+ @Override
public String getBazelPackageName() {
if (computedPackageName != null) {
return computedPackageName;
@@ -299,18 +306,28 @@ public String getBazelPackageName() {
// the caller is referring to the WORKSPACE root, which for build operations can
// (but not always) means that the user wants to build the entire workspace.
- // TODO refine this, so that if the root directory contains a BUILD file with a Java package to
- // somehow handle that workspace differently
+ // TODO refine this, so that if the root directory contains a BUILD file with a Java package to
+ // somehow handle that workspace differently
// Docs should indicate that a better practice is to keep the root dir free of an actual package
// For now, assume that anything referring to the root dir is a proxy for 'whole repo'
computedPackageName = "//...";
return computedPackageName;
}
- // set computedPackageName only when done computing it, to avoid threading issues
- computedPackageName = StreamSupport.stream(Paths.get(relativeWorkspacePath).spliterator(), false)
- .map(Object::toString).collect(Collectors.joining("/", "//", ""));
+ // split the file system path by OS path separator
+ String[] pathElements = relativeWorkspacePath.split(File.separator);
+
+ // assemble the path elements into a proper Bazel package name
+ String name = "/";
+ for (String e : pathElements) {
+ if (e.isEmpty()) {
+ continue;
+ }
+ name = name + "/" + e;
+ }
+ // set computedPackageName only when done computing it, to avoid threading issues
+ computedPackageName = name;
// and cache the last segment as well
getBazelPackageNameLastSegment();
@@ -322,6 +339,7 @@ public String getBazelPackageName() {
*
* e.g. if "//projects/libs/apple" is the package name, will return 'apple'
*/
+ @Override
public String getBazelPackageNameLastSegment() {
if (computedPackageNameLastSegment != null) {
return computedPackageNameLastSegment;
@@ -343,7 +361,7 @@ public String getBazelPackageNameLastSegment() {
/**
* Find a node in the tree that has the passed Bazel package path
- *
+ *
* @param bazelPackagePath
* path to find, such as //projects/libs/apple
* @return the node if found, or null
@@ -391,6 +409,23 @@ private BazelPackageInfo findBestParent(BazelPackageInfo candidate) {
return null;
}
+ @Override
+ public List gatherChildren() {
+ List gatherList = new ArrayList<>();
+ gatherChildrenRecur(gatherList);
+ return gatherList;
+ }
+
+ public void gatherChildrenRecur(List gatherList) {
+ if (!this.isWorkspaceRoot()) {
+ gatherList.add(this);
+ }
+ for (BazelPackageLocation child : this.childPackages.values()) {
+ BazelPackageInfo childInfo = (BazelPackageInfo) child;
+ childInfo.gatherChildrenRecur(gatherList);
+ }
+ }
+
@Override
public int hashCode() {
final int prime = 31;
@@ -401,23 +436,18 @@ public int hashCode() {
@Override
public boolean equals(Object obj) {
- if (this == obj) {
+ if (this == obj)
return true;
- }
- if (obj == null) {
+ if (obj == null)
return false;
- }
- if (getClass() != obj.getClass()) {
+ if (getClass() != obj.getClass())
return false;
- }
BazelPackageInfo other = (BazelPackageInfo) obj;
if (computedPackageName == null) {
- if (other.computedPackageName != null) {
+ if (other.computedPackageName != null)
return false;
- }
- } else if (!computedPackageName.equals(other.computedPackageName)) {
+ } else if (!computedPackageName.equals(other.computedPackageName))
return false;
- }
return true;
}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelPackageLocation.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelPackageLocation.java
new file mode 100644
index 0000000..9ea9eba
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/model/BazelPackageLocation.java
@@ -0,0 +1,65 @@
+package com.salesforce.bazel.sdk.model;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Minimal representation of a Bazel Package location on the file system.
+ *
+ * @see BazelLabel for a container that is also Bazel Target aware.
+ * @author stoens
+ * @since March 2020
+ */
+public interface BazelPackageLocation {
+
+ /**
+ * Returns the name of this Bazel Package - this is name of the final directory in the path.
+ *
+ * For example, if this Bazel Package is at the abs path ~/projects/bazel-workspace/a/b/c, this method returns "c".
+ */
+ String getBazelPackageNameLastSegment();
+
+ /**
+ * Returns the path of this Bazel Package, relative to the WORKSPACE root directory.
+ *
+ * For example, if this Bazel Package is at the abs path ~/projects/bazel-workspace/a/b/c, this method returns
+ * a/b/c.
+ */
+ String getBazelPackageFSRelativePath();
+
+ /**
+ * Returns the abs path of the directory containing the WORKSPACE file for this Bazel Package.
+ *
+ * For example, if this Bazel Package is at the abs path ~/projects/bazel-workspace/a/b/c, this method return
+ * ~/projects/bazel-workspace.
+ *
+ */
+ File getWorkspaceRootDirectory();
+
+ /**
+ * True if this is the root Bazel Package that contains the WORKSPACE file.
+ */
+ boolean isWorkspaceRoot();
+
+ /**
+ * Provides the proper Bazel label for the Bazel package.
+ *
+ *
+ * e.g. "//projects/libs/apple"
+ */
+ public String getBazelPackageName();
+
+ /**
+ * Builds a list containing this node, plus all children (recursively)
+ */
+ public List gatherChildren();
+
+ /**
+ * Returns the targets configured for this Bazel Package, at import time.
+ *
+ * A null return value indicates that the user did not specify any specific targets.
+ */
+ default public List getBazelTargets() {
+ return null;
+ }
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/project/ProjectView.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/project/ProjectView.java
new file mode 100644
index 0000000..1621649
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/project/ProjectView.java
@@ -0,0 +1,253 @@
+package com.salesforce.bazel.sdk.project;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.salesforce.bazel.sdk.model.BazelLabel;
+import com.salesforce.bazel.sdk.model.BazelPackageLocation;
+import com.salesforce.bazel.sdk.util.BazelConstants;
+
+/**
+ * The project view file.
+ *
+ * This implementations currently only supports a subset of the functionality provided by the IntelliJ Bazel Plugin,
+ * namely these sections:
+ *
+ *
+ *
+ * Example project view file:
+ *
+ *
+ * directories:
+ * path/to/bazel/package1
+ * path/to/bazel/package2
+ *
+ * targets:
+ * # the targets section is optional
+ * //path/to/bazel/package1:t1
+ *
+ *
+ * Exclusions are not supported yet.
+ *
+ * @author stoens
+ * @since March 2020
+ */
+public class ProjectView {
+
+ static String DIRECTORIES_SECTION = "directories:";
+ static String TARGETS_SECTION = "targets:";
+ static String DIRECTORIES_COMMENT = "# Add the directories you want added as source here";
+ static String INDENT = " ";
+
+ private final File rootWorkspaceDirectory;
+ private final Map packageToLineNumber;
+ private final Map targetToLineNumber;
+
+ /**
+ * Create a new ProjectView instance with the specified directories and targets.
+ */
+ public ProjectView(File rootWorkspaceDirectory, List directories, List targets) {
+ this.rootWorkspaceDirectory = rootWorkspaceDirectory;
+ Map pl = new LinkedHashMap<>();
+ Map tl = new LinkedHashMap<>();
+ initSections(directories, targets, pl, tl);
+ this.packageToLineNumber = Collections.unmodifiableMap(pl);
+ // this may get modified, so the map has to be mutable
+ this.targetToLineNumber = tl;
+ }
+
+ /**
+ * Creates a new ProjectView instance with the specified raw content.
+ */
+ public ProjectView(File rootWorkspaceDirectory, String content) {
+ this.rootWorkspaceDirectory = rootWorkspaceDirectory;
+ Map pl = new LinkedHashMap<>();
+ Map tl = new LinkedHashMap<>();
+ parseSections(content, rootWorkspaceDirectory, pl, tl);
+ this.packageToLineNumber = Collections.unmodifiableMap(pl);
+ // this may get modified, so the map has to be mutable
+ this.targetToLineNumber = tl;
+ }
+
+ /**
+ * Returns the raw project view file content.
+ */
+ public String getContent() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(DIRECTORIES_SECTION).append(System.lineSeparator());
+ sb.append(INDENT).append(DIRECTORIES_COMMENT).append(System.lineSeparator());
+ for (BazelPackageLocation pack : packageToLineNumber.keySet()) {
+ sb.append(INDENT).append(pack.getBazelPackageFSRelativePath()).append(System.lineSeparator());
+ }
+ if (!targetToLineNumber.isEmpty()) {
+ sb.append(System.lineSeparator());
+ sb.append(TARGETS_SECTION).append(System.lineSeparator());
+ for (BazelLabel target : targetToLineNumber.keySet()) {
+ sb.append(INDENT).append(target.getLabel()).append(System.lineSeparator());
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the line number, in the raw project view file content, of the specified bazel package, in the
+ * "directories" section.
+ */
+ public int getLineNumber(BazelPackageLocation pack) {
+ Integer lineNumber = packageToLineNumber.get(pack);
+ if (lineNumber == null) {
+ throw new IllegalArgumentException("Unknown " + pack);
+ }
+ return lineNumber;
+ }
+
+ /**
+ * Returns the directories with their targets.
+ */
+ public List getDirectories() {
+ List updatedPackageLocations = new ArrayList<>(packageToLineNumber.size());
+ for (BazelPackageLocation packageInfo : packageToLineNumber.keySet()) {
+ String directory = packageInfo.getBazelPackageFSRelativePath();
+ List targets = getTargetsForDirectory(directory);
+ updatedPackageLocations.add(new ProjectViewPackageLocation(rootWorkspaceDirectory, directory, targets));
+ }
+ return Collections.unmodifiableList(updatedPackageLocations);
+ }
+
+ /**
+ * Returns only the targets from the targets: section.
+ */
+ public List getTargets() {
+ return Collections.unmodifiableList(new ArrayList<>(targetToLineNumber.keySet()));
+ }
+
+ /**
+ * Adds the default targets for each directory that does not have one (or more) entries
+ * in the "targets:" section.
+ */
+ public void addDefaultTargets() {
+ List defaultLabels = new ArrayList<>();
+ for (BazelPackageLocation directory : packageToLineNumber.keySet()) {
+ boolean foundLabel = false;
+ BazelLabel bazelPackage = new BazelLabel(directory.getBazelPackageFSRelativePath());
+ for (BazelLabel label : targetToLineNumber.keySet()) {
+ if (label.getPackagePath().equals(bazelPackage.getPackagePath())) {
+ foundLabel = true;
+ break;
+ }
+ }
+ if (!foundLabel) {
+ for (String target : BazelConstants.DEFAULT_PACKAGE_TARGETS) {
+ defaultLabels.add(new BazelLabel(bazelPackage.getPackagePath(), target));
+ }
+ }
+ }
+ for (BazelLabel dflt : defaultLabels) {
+ // since this method is used to adjust internal state, it is ok for the
+ // line number to not be correct.
+ targetToLineNumber.put(dflt, 0);
+ }
+ }
+
+ public File getWorkspaceRootDirectory() {
+ return rootWorkspaceDirectory;
+ }
+
+ @Override
+ public int hashCode() {
+ return packageToLineNumber.keySet().hashCode() ^ targetToLineNumber.keySet().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof ProjectView)) {
+ return false;
+ }
+ ProjectView o = (ProjectView)other;
+ return packageToLineNumber.keySet().equals(o.packageToLineNumber.keySet()) &&
+ targetToLineNumber.keySet().equals(o.targetToLineNumber.keySet());
+ }
+
+ private List getTargetsForDirectory(String directory) {
+ List targets = null;
+ BazelLabel bazelPackage = new BazelLabel(directory);
+ for (BazelLabel target : targetToLineNumber.keySet()) {
+ if (target.getPackagePath().equals(bazelPackage.getPackagePath())) {
+ if (targets == null) {
+ targets = new ArrayList<>();
+ }
+ targets.add(target);
+ }
+ }
+ return targets;
+ }
+
+ private static void initSections(List packages, List targets,
+ Map packageToLineNumber,
+ Map targetToLineNumber)
+ {
+ // directories:
+ // # comment
+ // therefore:
+ int lineNumber = 3;
+ for (BazelPackageLocation pack : packages) {
+ packageToLineNumber.put(pack, lineNumber);
+ lineNumber += 1;
+ }
+ // newline
+ lineNumber += 1;
+ for (BazelLabel target : targets) {
+ targetToLineNumber.put(target, lineNumber);
+ lineNumber += 1;
+ }
+ }
+
+ private static void parseSections(String content, File rootWorkspaceDirectory,
+ Map packageToLineNumber,
+ Map targetToLineNumber)
+ {
+ boolean withinDirectoriesSection = false;
+ boolean withinTargetsSection = false;
+ int lineNumber = 0;
+ for (String line : content.split(System.lineSeparator())) {
+ lineNumber += 1;
+ line = line.trim();
+ if (line.isEmpty()) {
+ continue;
+ }
+ if (line.startsWith("#")) {
+ continue;
+ }
+ if (line.equals(DIRECTORIES_SECTION)) {
+ withinDirectoriesSection = true;
+ withinTargetsSection = false;
+ continue;
+ } else if (line.equals(TARGETS_SECTION)) {
+ withinDirectoriesSection = false;
+ withinTargetsSection = true;
+ continue;
+ } else if (line.endsWith(":")) {
+ // some other yet unknown section
+ withinDirectoriesSection = false;
+ withinTargetsSection = false;
+ continue;
+ }
+ if (withinDirectoriesSection) {
+ packageToLineNumber.put(new ProjectViewPackageLocation(rootWorkspaceDirectory, line), lineNumber);
+ } else if (withinTargetsSection) {
+ targetToLineNumber.put(new BazelLabel(line), lineNumber);
+ }
+ }
+ }
+
+
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/project/ProjectViewPackageLocation.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/project/ProjectViewPackageLocation.java
new file mode 100644
index 0000000..b269cb5
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/project/ProjectViewPackageLocation.java
@@ -0,0 +1,104 @@
+package com.salesforce.bazel.sdk.project;
+
+import java.io.File;
+import java.util.List;
+import java.util.Objects;
+
+import com.salesforce.bazel.sdk.model.BazelLabel;
+import com.salesforce.bazel.sdk.model.BazelPackageLocation;
+
+/**
+ * Represents a line in a project view file.
+ *
+ * TODO having this distinct from BazelPackageInfo adds complexity to the import logic. Revisit whether we can merge it.
+ *
+ */
+public class ProjectViewPackageLocation implements BazelPackageLocation {
+
+ private final File workspaceRootDirectory;
+ private final String packagePath;
+ private final List targets;
+
+ public ProjectViewPackageLocation(File workspaceRootDirectory, String packagePath) {
+ this(workspaceRootDirectory, packagePath, null);
+ }
+
+ ProjectViewPackageLocation(File workspaceRootDirectory, String packagePath, List targets) {
+ this.workspaceRootDirectory = Objects.requireNonNull(workspaceRootDirectory);
+ this.packagePath = Objects.requireNonNull(packagePath);
+ if (new File(this.packagePath).isAbsolute()) {
+ throw new IllegalArgumentException("[" + packagePath + "] must be relative");
+ }
+ this.targets = targets;
+ }
+
+ @Override
+ public String getBazelPackageNameLastSegment() {
+ return new File(this.packagePath).getName();
+ }
+
+ @Override
+ public String getBazelPackageFSRelativePath() {
+ return this.packagePath;
+ }
+
+ @Override
+ public File getWorkspaceRootDirectory() {
+ return this.workspaceRootDirectory;
+ }
+
+ @Override
+ public boolean isWorkspaceRoot() {
+ return this.packagePath.isEmpty();
+ }
+
+ @Override
+ public String getBazelPackageName() {
+ if ("".equals(packagePath)) {
+ // the caller is referring to the WORKSPACE root, which for build operations can
+ // (but not always) means that the user wants to build the entire workspace.
+
+ // TODO refine this, so that if the root directory contains a BUILD file with a Java package to
+ // somehow handle that workspace differently
+ // Docs should indicate that a better practice is to keep the root dir free of an actual package
+ // For now, assume that anything referring to the root dir is a proxy for 'whole repo'
+ return "//...";
+ }
+ return "//" + packagePath;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.workspaceRootDirectory.hashCode() ^ this.packagePath.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof ProjectViewPackageLocation) {
+ ProjectViewPackageLocation o = (ProjectViewPackageLocation) other;
+ return this.workspaceRootDirectory.equals(o.workspaceRootDirectory)
+ && this.packagePath.equals(o.packagePath);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "package path: " + this.packagePath;
+ }
+
+ @Override
+ public List gatherChildren() {
+ // TODO hard to implement, this class is planned for a rework
+ return null;
+ }
+
+ @Override
+ public List getBazelTargets() {
+ return targets;
+ }
+
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/util/BazelConstants.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/util/BazelConstants.java
new file mode 100644
index 0000000..20274b6
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/util/BazelConstants.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2020, Salesforce.com, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.salesforce.bazel.sdk.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+
+public interface BazelConstants {
+
+ /**
+ * The Bazel BUILD files BEF looks for.
+ */
+ Collection BUILD_FILE_NAMES =
+ Collections.unmodifiableSet(
+ new HashSet<>(
+ Arrays.asList(
+ new String[]{"BUILD", "BUILD.bazel"})));
+
+ /**
+ * The targets configured by default for each imported Bazel package.
+ */
+ Collection DEFAULT_PACKAGE_TARGETS =
+ Collections.unmodifiableSet(
+ new HashSet<>(
+ Arrays.asList(
+ // "*" includes test _deploy jars, which we currently need for our Eclipse JUnit
+ // integration to work - unfortunately building those jars can be slow if there
+ // are many test targets
+ new String[]{"*"})));
+
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/util/BazelPathHelper.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/util/BazelPathHelper.java
new file mode 100644
index 0000000..39d336b
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/util/BazelPathHelper.java
@@ -0,0 +1,60 @@
+package com.salesforce.bazel.sdk.util;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Static utilities.
+ */
+public class BazelPathHelper {
+
+ /**
+ * Resolve softlinks and other abstractions in the workspace paths.
+ */
+ public static File getCanonicalFileSafely(File directory) {
+ if (directory == null) {
+ return null;
+ }
+ try {
+ directory = directory.getCanonicalFile();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return directory;
+ }
+
+ /**
+ * Resolve softlinks and other abstractions in the workspace paths.
+ */
+ public static String getCanonicalPathStringSafely(File directory) {
+ String path = null;
+ if (directory == null) {
+ return null;
+ }
+ try {
+ path = directory.getCanonicalPath();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ if (path == null) {
+ // fallback to absolute path in case canonical path fails
+ path = directory.getAbsolutePath();
+ }
+ return path;
+ }
+
+ /**
+ * Resolve softlinks and other abstractions in the workspace paths.
+ */
+ public static String getCanonicalPathStringSafely(String path) {
+ if (path == null) {
+ return null;
+ }
+ try {
+ path = new File(path).getCanonicalPath();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return path;
+ }
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/util/WorkProgressMonitor.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/util/WorkProgressMonitor.java
new file mode 100644
index 0000000..b6961bd
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/util/WorkProgressMonitor.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2019, Salesforce.com, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.salesforce.bazel.sdk.util;
+
+/**
+ * Abstraction that allows an observer to monitor the progress of work performed during an operation.
+ */
+public interface WorkProgressMonitor {
+
+ /**
+ * Notifies that the main task is beginning. This must only be called once on a given progress monitor instance.
+ *
+ * @param name
+ * the name (or description) of the main task
+ * @param totalWork
+ * the total number of work units into which the main task is been subdivided. If the value is
+ * UNKNOWN
the implementation is free to indicate progress in a way which doesn't require
+ * the total number of work units in advance.
+ */
+ public void beginTask(String name, int totalWork);
+
+ /**
+ * Notifies that the work is done; that is, either the main task is completed or the user canceled it. This method
+ * may be called more than once (implementations should be prepared to handle this case).
+ */
+ public void done();
+
+ /**
+ * Returns whether cancelation of current operation has been requested. Long-running operations should poll to see
+ * if cancelation has been requested.
+ *
+ * @return true
if cancellation has been requested, and false
otherwise
+ * @see #setCanceled(boolean)
+ */
+ public boolean isCanceled();
+
+ /**
+ * Sets the cancel state to the given value.
+ *
+ * @param value
+ * true
indicates that cancelation has been requested (but not necessarily acknowledged);
+ * false
clears this flag
+ * @see #isCanceled()
+ */
+ public void setCanceled(boolean value);
+
+ /**
+ * Notifies that a subtask of the main task is beginning. Subtasks are optional; the main task might not have
+ * subtasks.
+ *
+ * @param name
+ * the name (or description) of the subtask
+ */
+ public void subTask(String name);
+
+ /**
+ * Notifies that a given number of work unit of the main task has been completed. Note that this amount represents
+ * an installment, as opposed to a cumulative amount of work done to date.
+ *
+ * @param work
+ * a non-negative number of work units just completed
+ */
+ public void worked(int work);
+
+ WorkProgressMonitor NOOP = new WorkProgressMonitor() {
+ @Override
+ public void worked(int work) {}
+
+ @Override
+ public void subTask(String name) {}
+
+ @Override
+ public void setCanceled(boolean value) {}
+
+ @Override
+ public boolean isCanceled() {
+ return false;
+ }
+
+ @Override
+ public void done() {}
+
+ @Override
+ public void beginTask(String name, int totalWork) {}
+ };
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/workspace/BazelPackageFinder.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/workspace/BazelPackageFinder.java
new file mode 100644
index 0000000..defa99b
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/workspace/BazelPackageFinder.java
@@ -0,0 +1,63 @@
+package com.salesforce.bazel.sdk.workspace;
+
+import java.io.File;
+import java.util.Set;
+
+import com.salesforce.bazel.sdk.logging.LogHelper;
+import com.salesforce.bazel.sdk.model.BazelBuildFileHelper;
+import com.salesforce.bazel.sdk.util.BazelConstants;
+import com.salesforce.bazel.sdk.util.BazelPathHelper;
+import com.salesforce.bazel.sdk.util.WorkProgressMonitor;
+
+public class BazelPackageFinder {
+ LogHelper logger;
+
+ public BazelPackageFinder() {
+ logger = LogHelper.log(this.getClass());
+ }
+
+ // TODO our workspace scanner is looking for Java packages, but uses primitive techniques. switch to use the aspect
+ // approach here, like we do with the classpath computation.
+
+ public void findBuildFileLocations(File dir, WorkProgressMonitor monitor, Set buildFileLocations, int depth) {
+ if (!dir.isDirectory()) {
+ return;
+ }
+
+ try {
+ File[] dirFiles = dir.listFiles();
+ for (File dirFile : dirFiles) {
+
+ if (shouldIgnore(dirFile, depth)) {
+ continue;
+ }
+
+ if (isBuildFile(dirFile)) {
+
+ // great, this dir is a Bazel package (but this may be a non-Java package)
+ // scan the BUILD file looking for java rules, only add if this is a java project
+ if (BazelBuildFileHelper.hasJavaRules(dirFile)) {
+ buildFileLocations.add(BazelPathHelper.getCanonicalFileSafely(dir));
+ }
+ } else if (dirFile.isDirectory()) {
+ findBuildFileLocations(dirFile, monitor, buildFileLocations, depth + 1);
+ }
+ }
+ } catch (Exception anyE) {
+ logger.error("ERROR scanning for Bazel packages: {}", anyE.getMessage());
+ }
+ }
+
+ private static boolean shouldIgnore(File f, int depth) {
+ if (depth == 0 && f.isDirectory() && f.getName().startsWith("bazel-")) {
+ // this is a Bazel internal directory at the root of the project dir, ignore
+ // TODO should this use one of the ignore directory facilities at the bottom of this class?
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean isBuildFile(File candidate) {
+ return BazelConstants.BUILD_FILE_NAMES.contains(candidate.getName());
+ }
+}
diff --git a/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/workspace/BazelWorkspaceScanner.java b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/workspace/BazelWorkspaceScanner.java
new file mode 100644
index 0000000..187037e
--- /dev/null
+++ b/com.salesforce.b2eclipse.jdt.ls/src/main/java/com/salesforce/bazel/sdk/workspace/BazelWorkspaceScanner.java
@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2019, Salesforce.com, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.salesforce.bazel.sdk.workspace;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+import java.util.TreeSet;
+
+import com.salesforce.bazel.sdk.model.BazelPackageInfo;
+
+/**
+ * Scans a Bazel workspace looking for Java packages (BUILD files that have java_binary or java_library targets). It is
+ * assumed that the user will provide the root workspace directory (where the WORKSPACE file is) and we will scan the
+ * subtree below that.
+ */
+public class BazelWorkspaceScanner {
+
+ public static String getBazelWorkspaceName(String bazelWorkspaceRootDirectory) {
+ // TODO pull the workspace name out of the WORKSPACE file, until then use the directory name (e.g. bazel-demo)
+ String bazelWorkspaceName = "workspace";
+ if (bazelWorkspaceRootDirectory != null) {
+ int lastSlash = bazelWorkspaceRootDirectory.lastIndexOf(File.separator);
+ if (lastSlash >= 0 && (bazelWorkspaceRootDirectory.length() - lastSlash) > 3) {
+ // add the directory name to the label, if it is meaningful (>3 chars)
+ bazelWorkspaceName = bazelWorkspaceRootDirectory.substring(lastSlash + 1);
+ } else {
+ bazelWorkspaceName = bazelWorkspaceRootDirectory;
+ }
+ }
+ return bazelWorkspaceName;
+ }
+
+ /**
+ * Get a list of candidate Bazel packages to import. This list is provided to the user in the form of a tree
+ * control.
+ *
+ * Currently, the list returned will always be of size 1. It represents the root node of the scanned Bazel
+ * workspace. The root node has child node references, and the tree expands from there.
+ *
+ * TODO support scanning at an arbitrary location inside of a Bazel workspace (e.g. //projects/libs) and have the
+ * scanner crawl up to the WORKSPACE root from there.
+ *
+ * @param rootDirectory
+ * the directory to scan, which must be the root node of a Bazel workspace
+ * @return the workspace root BazelPackageInfo
+ */
+ public BazelPackageInfo getPackages(String rootDirectory) throws IOException {
+ if (rootDirectory == null || rootDirectory.isEmpty()) {
+ // this is the initialization state of the wizard
+ return null;
+ }
+ File workspaceRootDir = new File(rootDirectory);
+ try {
+ workspaceRootDir = workspaceRootDir.getCanonicalFile();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ return null;
+ }
+ return getPackages(workspaceRootDir);
+ }
+
+ /**
+ * Get a list of candidate Bazel packages to import. This list is provided to the user in the form of a tree
+ * control.
+ *
+ * Currently, the list returned will always be of size 1. It represents the root node of the scanned Bazel
+ * workspace. The root node has child node references, and the tree expands from there.
+ *
+ * TODO support scanning at an arbitrary location inside of a Bazel workspace (e.g. //projects/libs) and have the
+ * scanner crawl up to the WORKSPACE root from there.
+ *
+ * @param rootDirectory
+ * the directory to scan, which must be the root node of a Bazel workspace
+ * @return the workspace root BazelPackageInfo
+ */
+ public BazelPackageInfo getPackages(File rootDirectoryFile) throws IOException {
+ if (rootDirectoryFile == null || !rootDirectoryFile.exists() || !rootDirectoryFile.isDirectory()) {
+ // this is the initialization state of the wizard
+ return null;
+ }
+ String rootDirectory = rootDirectoryFile.getCanonicalPath();
+ BazelPackageInfo workspace = new BazelPackageInfo(rootDirectoryFile);
+
+ // TODO the correct way to do this is put the scan on another thread, and allow it to update the progress monitor.
+ // Do it on-thread for now as it is easiest.
+
+ Set projects = new TreeSet<>();
+ BazelPackageFinder packageFinder = new BazelPackageFinder();
+ packageFinder.findBuildFileLocations(rootDirectoryFile, null, projects, 0);
+
+ int sizeOfWorkspacePath = rootDirectory.length();
+ for (File project : projects) {
+ String projectPath = project.getCanonicalPath();
+
+ if (projectPath.equals(rootDirectory)) {
+ // root path, already created the root node
+ continue;
+ }
+
+ // TODO ooh, this bazel package path manipulation seems error prone
+ String relativePath = projectPath.substring(sizeOfWorkspacePath + 1);
+
+ // instantiate the project info object, which will automatically hook itself to the appropriate parents
+ new BazelPackageInfo(workspace, relativePath);
+ }
+
+ return workspace;
+ }
+
+}