From 668655d4a265436945448f36d27f156278aab811 Mon Sep 17 00:00:00 2001 From: Peter Laird Date: Wed, 30 Sep 2020 08:34:06 -0600 Subject: [PATCH] global search, and prefs --- bzl-java-sdk/META-INF/MANIFEST.MF | 1 + .../sdk/index/BazelJvmIndexClasspath.java | 118 +++++++-------- .../bazel/sdk/index/JvmCodeIndexer.java | 26 ++-- .../sdk/index/jar/JarIdentiferResolver.java | 51 +++---- .../bazel/sdk/index/jar/JavaJarCrawler.java | 31 +++- .../sdk/lang/jvm/BazelJvmExternalUtil.java | 54 ------- .../external/BazelExternalJarRuleManager.java | 80 ++++++++++ .../external/BazelExternalJarRuleType.java | 64 ++++++++ .../sdk/lang/jvm/external/CoursierUtil.java | 61 ++++++++ .../MavenInstallExternalJarRuleType.java | 141 ++++++++++++++++++ docs/using_the_feature_settings.md | 3 +- .../bazel/eclipse/BazelPluginActivator.java | 17 ++- .../bazel/eclipse/builder/BazelBuilder.java | 2 + .../BazelGlobalSearchClasspathContainer.java | 45 +++--- .../EclipseBazelConfigurationManager.java | 8 +- .../BazelPreferenceInitializer.java | 23 ++- .../preferences/BazelPreferenceKeys.java | 25 ++++ .../preferences/BazelPreferencePage.java | 14 +- .../bazel/eclipse/mock/MockEclipse.java | 13 +- 19 files changed, 569 insertions(+), 208 deletions(-) delete mode 100644 bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/BazelJvmExternalUtil.java create mode 100644 bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/BazelExternalJarRuleManager.java create mode 100644 bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/BazelExternalJarRuleType.java create mode 100644 bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/CoursierUtil.java create mode 100644 bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/MavenInstallExternalJarRuleType.java create mode 100644 plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferenceKeys.java diff --git a/bzl-java-sdk/META-INF/MANIFEST.MF b/bzl-java-sdk/META-INF/MANIFEST.MF index e8a3aa39b..f249bed9a 100644 --- a/bzl-java-sdk/META-INF/MANIFEST.MF +++ b/bzl-java-sdk/META-INF/MANIFEST.MF @@ -11,6 +11,7 @@ Export-Package: com.salesforce.bazel.sdk.aspect, com.salesforce.bazel.sdk.console, com.salesforce.bazel.sdk.index;version="1.2.1.qualifier", com.salesforce.bazel.sdk.lang.jvm;version="1.2.1.qualifier", + com.salesforce.bazel.sdk.lang.jvm.external;version="1.2.1.qualifier", com.salesforce.bazel.sdk.logging;version="1.2.1.qualifier", com.salesforce.bazel.sdk.model;version="1.2.1.qualifier", com.salesforce.bazel.sdk.project;version="1.2.1.qualifier", diff --git a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/BazelJvmIndexClasspath.java b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/BazelJvmIndexClasspath.java index 12dd72317..644a278db 100644 --- a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/BazelJvmIndexClasspath.java +++ b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/BazelJvmIndexClasspath.java @@ -27,13 +27,16 @@ import java.util.ArrayList; import java.util.List; -import com.salesforce.bazel.sdk.index.jar.JavaJarCrawler; import com.salesforce.bazel.sdk.index.jar.JarIdentiferResolver; +import com.salesforce.bazel.sdk.index.jar.JavaJarCrawler; import com.salesforce.bazel.sdk.index.model.CodeLocationDescriptor; import com.salesforce.bazel.sdk.lang.jvm.BazelJvmClasspathResponse; import com.salesforce.bazel.sdk.lang.jvm.JvmClasspathEntry; +import com.salesforce.bazel.sdk.lang.jvm.external.BazelExternalJarRuleManager; +import com.salesforce.bazel.sdk.lang.jvm.external.BazelExternalJarRuleType; import com.salesforce.bazel.sdk.model.BazelWorkspace; import com.salesforce.bazel.sdk.util.WorkProgressMonitor; +import com.salesforce.bazel.sdk.workspace.OperatingEnvironmentDetectionStrategy; /** * Creates a classpath containing all downloaded jars (binary and source) and all workspace built jars @@ -42,89 +45,82 @@ * that needs to enumerate all available types in a workspace. */ public class BazelJvmIndexClasspath { + /** + * Associated workspace. + */ protected BazelWorkspace bazelWorkspace; - protected File bazelBin; - protected File jarCacheDir; - private List bazelBinInternals = new ArrayList<>(); + + // collaborators + protected OperatingEnvironmentDetectionStrategy os; + protected BazelExternalJarRuleManager externalJarRuleManager; + + /** + * User provided locations to search. + */ + protected List additionalJarLocations; - private BazelJvmClasspathResponse cacheResponse; + protected BazelJvmClasspathResponse cacheResponse; /** * Ctor with workspace. */ - public BazelJvmIndexClasspath(BazelWorkspace bazelWorkspace, File jarCacheDir) { + public BazelJvmIndexClasspath(BazelWorkspace bazelWorkspace, OperatingEnvironmentDetectionStrategy os, + BazelExternalJarRuleManager externalJarRuleManager, List additionalJarLocations) { this.bazelWorkspace = bazelWorkspace; - this.bazelBin = bazelWorkspace.getBazelBinDirectory(); - - if (jarCacheDir != null) { - // maven_install for example stores all downloaded jars outside of the bazel-out directory structure - this.jarCacheDir = jarCacheDir; - } else { - // default is to hunt around in the bazel-bin/external directory - this.jarCacheDir = new File(bazelBin, "external"); - } - - File bazelBinInternal = new File(bazelBin, "projects"); // TODO needs to be configurable/computable, this is just our convention - if (bazelBinInternal.exists()) { - bazelBinInternals.add(bazelBinInternal); - } + this.os = os; + this.externalJarRuleManager = externalJarRuleManager; + this.additionalJarLocations = additionalJarLocations; } - /** - * Ctor with workspace, and the names of the root directories in the Bazel workspace that are interesting to - * index. This will typically exclude //tools and other non-production code. For example, if your production code - * base is rooted in //projects and //libs you would pass ['projects', 'libs'] for the second param. - */ - public BazelJvmIndexClasspath(BazelWorkspace bazelWorkspace, File jarCacheDir, List internalRootDirectoryNames) { - this(bazelWorkspace, jarCacheDir); - - for (String name : internalRootDirectoryNames) { - File internalRoot = new File(bazelBin, name); - if (internalRoot.exists()) { - bazelBinInternals.add(internalRoot); - } - } - } /** - * Computes the JVM classpath for the associated BazelProject + * Computes the JVM classpath for the associated Bazel workspace */ public BazelJvmClasspathResponse getClasspathEntries(WorkProgressMonitor progressMonitor) { if (cacheResponse != null) { - return cacheResponse; // TODO expire the cache + return cacheResponse; } - - return getClasspathEntriesInternal(this.bazelBinInternals, this.jarCacheDir, progressMonitor); - } - - /* visible for testing */ - BazelJvmClasspathResponse getClasspathEntriesInternal(List bazelBinInternals, File jarCacheDir, - WorkProgressMonitor progressMonitor) { JvmCodeIndex index = new JvmCodeIndex(); + List locations = new ArrayList<>(); - // EXTERNAL (aka maven_install downloaded) - if (jarCacheDir != null) { - JarIdentiferResolver jarResolver = new JarIdentiferResolver("/public/"); // TODO this is only maven_install convention - JavaJarCrawler jarCrawler = new JavaJarCrawler(index, jarResolver); - jarCrawler.index(jarCacheDir, false); + // for each jar downloading rule type in the workspace, add the appropriate local directories of the downloaded jars + List ruleTypes = externalJarRuleManager.findInUseExternalJarRuleTypes(this.bazelWorkspace); + for (BazelExternalJarRuleType ruleType : ruleTypes) { + System.out.println(""); + List ruleSpecificLocations = ruleType.getDownloadedJarLocations(bazelWorkspace); + locations.addAll(ruleSpecificLocations); } - // INTERNAL (jars produced by Bazel) - if (bazelBinInternals != null && bazelBinInternals.size() > 0) { - - // TODO we should actually add the source files directly instead of indexing the built jars - - JarIdentiferResolver jarResolver = new JarIdentiferResolver("/bin/"); - JavaJarCrawler jarCrawler = new JavaJarCrawler(index, jarResolver); - for (File bazelBinInternal : bazelBinInternals) { - jarCrawler.index(bazelBinInternal, false); - } + // add internal location (jars built by the bazel workspace + addInternalLocations(locations); + + // add the additional directories the user wants to search + if (additionalJarLocations != null) { + locations.addAll(additionalJarLocations); + } + + // now do the searching + for (File location : locations) { + getClasspathEntriesInternal(location, index, progressMonitor); } cacheResponse = convertIndexIntoResponse(index); return cacheResponse; } + public void clearCache() { + cacheResponse = null; + } + + /* visible for testing */ + void getClasspathEntriesInternal(File location, JvmCodeIndex index, WorkProgressMonitor progressMonitor) { + if (location != null && location.exists()) { + JarIdentiferResolver jarResolver = new JarIdentiferResolver(); + JavaJarCrawler jarCrawler = new JavaJarCrawler(index, jarResolver); + jarCrawler.index(location, false); + } + } + protected BazelJvmClasspathResponse convertIndexIntoResponse(JvmCodeIndex index) { BazelJvmClasspathResponse response = new BazelJvmClasspathResponse(); List entries = new ArrayList<>(); @@ -167,4 +163,8 @@ protected void addJarEntry(List entries, CodeLocationDescript } entries.add(cpEntry); } + + protected void addInternalLocations(List locations) { + // TODO INTERNAL (jars produced by Bazel) + } } \ No newline at end of file diff --git a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/JvmCodeIndexer.java b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/JvmCodeIndexer.java index d06b665b4..e94cb2832 100644 --- a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/JvmCodeIndexer.java +++ b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/JvmCodeIndexer.java @@ -104,30 +104,28 @@ public JvmCodeIndex buildIndex() { if (jarResolver != null) { JavaJarCrawler jarCrawler = new JavaJarCrawler(index, jarResolver); jarCrawler.index(externalJarRootFile, true); + } else { + logInfo("Could not determine the build system (maven/bazel) from the jar root. Skipping jar scanning..."); } - if (sourceRoot == null) { - logInfo("The provided source code root directory does not exist. This is ok."); - return index; + if (sourceRoot != null) { + File sourceRootFile = new File(sourceRoot); + if (!sourceRootFile.exists()) { + logInfo("The provided source code root directory does not exist. This is ok."); + return index; + } + JavaSourceCrawler sourceCrawler = new JavaSourceCrawler(index, pickJavaSourceArtifactMarker(externalJarRoot)); + sourceCrawler.index(sourceRootFile); } - File sourceRootFile = new File(sourceRoot); - if (!sourceRootFile.exists()) { + else { logInfo("The provided source code root directory does not exist. This is ok."); - return index; } - JavaSourceCrawler sourceCrawler = new JavaSourceCrawler(index, pickJavaSourceArtifactMarker(externalJarRoot)); - sourceCrawler.index(sourceRootFile); return index; } private static JarIdentiferResolver pickJavaJarResolver(String jarRepoPath) { - if (jarRepoPath.contains(".m2/repository")) { - return new JarIdentiferResolver("repository"); - } else if (jarRepoPath.contains("bazel-out")) { - return new JarIdentiferResolver("public"); - } - return null; + return new JarIdentiferResolver(); } private static String pickJavaSourceArtifactMarker(String jarRepoPath) { diff --git a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/jar/JarIdentiferResolver.java b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/jar/JarIdentiferResolver.java index 946f85440..a6f1453a9 100644 --- a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/jar/JarIdentiferResolver.java +++ b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/jar/JarIdentiferResolver.java @@ -26,27 +26,18 @@ import java.io.File; import java.util.zip.ZipFile; +/** + * Resolver that decides if the passed File is an interesting jar file, and if so it will deduce + * GAV information from the path. + */ public class JarIdentiferResolver { - private String startWord; - - /** - * Creates a resolver, with the important startWord. The startWord delimits the path part that - * divides the location on disk for the directory structure from the important structure information - * that identifies the artifact. For example, in a Maven repository, the path information below the - * .m2/repository directory contains the groupId and versionId. - * - * @param startWord for Maven repo, "repository"; for a bazel workspace using maven_install, "public" - */ - public JarIdentiferResolver(String startWord) { - this.startWord = startWord; + + public JarIdentiferResolver() { } - public JarIdentifier resolveJarIdentifier(File pathFile, ZipFile zipFile) { + public JarIdentifier resolveJarIdentifier(File gavRoot, File pathFile, ZipFile zipFile) { String path = pathFile.getAbsolutePath(); - int startIndex = path.lastIndexOf(startWord); - if (startIndex == -1) { - return null; - } + // do some quick Bazel exclusions (TODO how best to identify the interesting jars in the Bazel output dirs?) if (path.contains("-hjar.jar")) { return null; // perf optimization jar, not intended for non-Bazel consumption @@ -64,7 +55,10 @@ public JarIdentifier resolveJarIdentifier(File pathFile, ZipFile zipFile) { return null; // source jar, this will be pulled in as an attribute of the main jar } if (path.contains("-gensrc.jar")) { - return null; // source jar, this will be pulled in as an attribute of the main jar + return null; // generated source jar, this will be pulled in as an attribute of the main jar + } + if (path.contains("_deploy.jar")) { + return null; // uber jar, which contains exploded classes from other jars } if (path.contains("Test.jar")) { return null; // by convention, a jar that contains a test to run in bazel @@ -73,10 +67,10 @@ public JarIdentifier resolveJarIdentifier(File pathFile, ZipFile zipFile) { return null; // by convention, a jar that contains a test to run in bazel } - // Maven: com/acme/libs/my-blue-impl/0.1.8/my-blue-impl-0.1.8.jar - // Bazel: com/acme/libs/my-blue-impl/0.1.8/my-blue-impl-0.1.8-ijar.jar + // Maven compatible: com/acme/libs/my-blue-impl/0.1.8/my-blue-impl-0.1.8.jar + // Bazel internal: com/acme/libs/my-blue-impl/0.1.8/my-blue-impl-0.1.8-ijar.jar - String gavPart = path.substring(startIndex+startWord.length()); + String gavPart = path.substring(gavRoot.getAbsolutePath().length()); String[] gavParts = gavPart.split("/"); // open-context-impl-0.1.8.jar => open-context-impl @@ -115,15 +109,22 @@ public JarIdentifier resolveJarIdentifier(File pathFile, ZipFile zipFile) { public static void main(String[] args) { // Maven build system - JarIdentiferResolver resolver = new JarIdentiferResolver("/repository/"); + JarIdentiferResolver resolver = new JarIdentiferResolver(); + File gavRoot = new File("/Users/mbenioff/.m2/repository"); File pathFile = new File("/Users/mbenioff/.m2/repository/com/acme/libs/my-blue-impl/0.1.8/my-blue-impl-0.1.8.jar"); - JarIdentifier id = resolver.resolveJarIdentifier(pathFile, null); + JarIdentifier id = resolver.resolveJarIdentifier(gavRoot, pathFile, null); System.out.println(id.locationIdentifier); + if (!id.locationIdentifier.equals("com.acme.libs:my-blue-impl:0.1.8")) { + System.err.println("FAIL!"); + } // Bazel build system - resolver = new JarIdentiferResolver("/public/"); + gavRoot = new File("/tmp/_bazel_benioff/dsf87dsfsl/execroot/__main__/bazel-out/darwin-fastbuild/bin/external/maven/v1/https/benioff%40nexus.acme.com/nexus/content/groups/public"); pathFile = new File("/tmp/_bazel_benioff/dsf87dsfsl/execroot/__main__/bazel-out/darwin-fastbuild/bin/external/maven/v1/https/benioff%40nexus.acme.com/nexus/content/groups/public/com/acme/libs/my-blue-impl/0.1.8/my-blue-impl-0.1.8-ijar.jar"); - id = resolver.resolveJarIdentifier(pathFile, null); + id = resolver.resolveJarIdentifier(gavRoot, pathFile, null); System.out.println(id.locationIdentifier); + if (!id.locationIdentifier.equals("com.acme.libs:my-blue-impl:0.1.8")) { + System.err.println("FAIL!"); + } } } diff --git a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/jar/JavaJarCrawler.java b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/jar/JavaJarCrawler.java index 276cf4c68..3bb7b752a 100644 --- a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/jar/JavaJarCrawler.java +++ b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/index/jar/JavaJarCrawler.java @@ -24,6 +24,7 @@ package com.salesforce.bazel.sdk.index.jar; import java.io.File; +import java.io.FilenameFilter; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -47,14 +48,31 @@ public JavaJarCrawler(JvmCodeIndex index, JarIdentiferResolver resolver) { } public void index(File basePath, boolean doIndexClasses) { - indexRecur(basePath, doIndexClasses); + indexRecur(null, basePath, doIndexClasses); } - protected void indexRecur(File path, boolean doIndexClasses) { + protected void indexRecur(File gavRoot, File path, boolean doIndexClasses) { File[] children = path.listFiles(); if (children == null) { return; } + + // some file system layouts put gav information in the path, e.g. + // ~/.m2/repository/com/acme/blue/1.0.0/blue.jar + // we want to track the start of the gav info in the path if possible + if (gavRoot == null) { + File[] gavRootIndicators = path.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + // TODO sketchy logic here, we assume at least one downloaded jar comes from a common domain + return name.equals("com") || name.equals("org") || name.equals("net"); + } + }); + if (gavRootIndicators.length > 0) { + gavRoot = path; + } + } + for (File child : children) { ZipFile zipFile = null; try { @@ -63,11 +81,11 @@ protected void indexRecur(File path, boolean doIndexClasses) { // bazel test sandbox, stay out of here as the jars in here are for running tests return; } - indexRecur(child, doIndexClasses); + indexRecur(gavRoot, child, doIndexClasses); } else if (child.canRead()) { if (child.getName().endsWith(".jar")) { zipFile = new ZipFile(child); - foundJar(child, zipFile, doIndexClasses); + foundJar(gavRoot, child, zipFile, doIndexClasses); } } } @@ -84,9 +102,10 @@ protected void indexRecur(File path, boolean doIndexClasses) { } } - protected void foundJar(File jarFile, ZipFile zipFile, boolean doIndexClasses) { + protected void foundJar(File gavRoot, File jarFile, ZipFile zipFile, boolean doIndexClasses) { // precisely identify the jar file - JarIdentifier jarId = resolver.resolveJarIdentifier(jarFile, zipFile); + log("jar:" , jarFile.getName(), null); + JarIdentifier jarId = resolver.resolveJarIdentifier(gavRoot, jarFile, zipFile); if (jarId == null) { // this jar is not part of the typical dependencies (e.g. it is a jar used in the build toolchain); ignore return; diff --git a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/BazelJvmExternalUtil.java b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/BazelJvmExternalUtil.java deleted file mode 100644 index e32ab6ba9..000000000 --- a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/BazelJvmExternalUtil.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.salesforce.bazel.sdk.lang.jvm; - -import java.io.File; - -import com.salesforce.bazel.sdk.workspace.OperatingEnvironmentDetectionStrategy; - -/** - * Helper routines for downloading jars from an external repo. - * See also rules_jvm_external (maven_install) and bazel_tools (jvm_maven_import_external) - */ -public class BazelJvmExternalUtil { - private static final String COURSIER_CACHE_LOCATION_LINUX = "/.cache/coursier/v1"; - private static final String COURSIER_CACHE_LOCATION_MACOS = "/Library/Caches/Coursier/v1"; - private static final String COURSIER_CACHE_LOCATION_WINDOWS = "/Coursier/cache/v1"; - private static File coursierCacheLocation = null; - - - public static File getDownloadedJarCacheLocation(OperatingEnvironmentDetectionStrategy os) { - // TODO this assumes maven_install, but need to support jvm_maven_import_external too - // but the problem is with IDEs this pref will be set before the Bazel workspace is identified - // so we can't default this using knowledge of the download tech - // we should create a second edition of this method that takes a BazelWorkspace as a param in addition - // to os, and then that one should be called after the workspace is loaded - - if (coursierCacheLocation == null) { - String defaultLocation = null; - String homedir = System.getProperty("user.home"); - if ("linux".equals(os.getOperatingSystemName())) { - defaultLocation = homedir+COURSIER_CACHE_LOCATION_LINUX; - } else if ("darwin".equals(os.getOperatingSystemName())) { - defaultLocation = homedir+COURSIER_CACHE_LOCATION_MACOS; - } else if ("windows".equals(os.getOperatingSystemName())) { - defaultLocation = homedir+COURSIER_CACHE_LOCATION_WINDOWS; - } else { - // or is there a better default? - defaultLocation = homedir+COURSIER_CACHE_LOCATION_LINUX; - } - - // TODO we are assuming the default cache location, but there are ways to force coursier to use alternate locations - // we should also factor that in. - // - // The problem with the default location is it might be mixing in jars from multiple Bazel workspaces. - // - // Another thought is to use a search strategy and accumulate a list of found - // caches on disk, and then ask the user to choose which one(s) to use. - // https://github.com/bazelbuild/rules_jvm_external#using-a-persistent-artifact-cache - - coursierCacheLocation = new File(defaultLocation); - } - - return coursierCacheLocation; - } - -} diff --git a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/BazelExternalJarRuleManager.java b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/BazelExternalJarRuleManager.java new file mode 100644 index 000000000..8a85e723d --- /dev/null +++ b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/BazelExternalJarRuleManager.java @@ -0,0 +1,80 @@ +package com.salesforce.bazel.sdk.lang.jvm.external; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.salesforce.bazel.sdk.model.BazelWorkspace; +import com.salesforce.bazel.sdk.workspace.OperatingEnvironmentDetectionStrategy; + +/** + * Bazel has a collection of tech stacks for downloading jars. For use cases where we need to know + * what the workspace is using, this manager is used to indicate what tech stack(s) are in operation. + */ +public class BazelExternalJarRuleManager { + + /** + * These started as an Enum, but new ways of downloading jars may become available, so String is a + * more extensible option. + */ + public static final String MAVEN_INSTALL = "maven_install"; // rules_jvm_external + public static final String JVM_MAVEN_IMPORT = "jvm_maven_import_external"; // bazeltools + public static final String MAVEN_JAR = "maven_jar"; // obsolete as of Bazel 2 + + private Map availableTypes; + + public BazelExternalJarRuleManager(OperatingEnvironmentDetectionStrategy os) { + availableTypes = new HashMap<>(); + availableTypes.put(MAVEN_INSTALL, new MavenInstallExternalJarRuleType(os)); + availableTypes.put(JVM_MAVEN_IMPORT, new BazelExternalJarRuleType(JVM_MAVEN_IMPORT, os)); // TODO create specialized subclass for jvm_import + availableTypes.put(MAVEN_JAR, new BazelExternalJarRuleType(MAVEN_JAR, os)); // TODO create specialized subclass for maven_jar + } + + /** + * Enumerates the known jar downloading technologies. + * @return + */ + public Map getAvailableTypes() { + return availableTypes; + } + + /** + * This is rare to use. If your tooling environment knows of an external jar downloading technology + * that is not known to the SDK, you can use this method as an extension point. Or you could use it + * to remove an existing type that you don't want to support. + */ + public void setAvailableTypes(Map availableTypes) { + this.availableTypes = availableTypes; + } + + /** + * Is the passed rule name a known jar downloading tech? + */ + public boolean isKnownType(String ruleName) { + return getType(ruleName) != null; + } + + /** + * Gets the jar downloading type by rule name. + */ + public BazelExternalJarRuleType getType(String ruleName) { + return availableTypes.get(ruleName); + } + + /** + * Determine what jar downloader rule types are in use for a given workspace. + */ + public List findInUseExternalJarRuleTypes(BazelWorkspace bazelWorkspace) { + List inUseRuleTypes = new ArrayList<>(); + + for (BazelExternalJarRuleType type : availableTypes.values()) { + if (type.isUsedInWorkspace(bazelWorkspace)) { + inUseRuleTypes.add(type); + } + } + + return inUseRuleTypes; + } + +} diff --git a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/BazelExternalJarRuleType.java b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/BazelExternalJarRuleType.java new file mode 100644 index 000000000..08ba57db1 --- /dev/null +++ b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/BazelExternalJarRuleType.java @@ -0,0 +1,64 @@ +package com.salesforce.bazel.sdk.lang.jvm.external; + +import java.io.File; +import java.util.List; + +import com.salesforce.bazel.sdk.model.BazelWorkspace; +import com.salesforce.bazel.sdk.workspace.OperatingEnvironmentDetectionStrategy; + +/** + * An instance of one of the supported types of Maven jar integration (maven_install, etc) + */ +public class BazelExternalJarRuleType { + public final String ruleName; + protected final OperatingEnvironmentDetectionStrategy os; + + protected List downloadedJarLocations; + protected boolean isUsedInWorkspace = false; + + /** + * This ctor should only be used by subclasses. + */ + public BazelExternalJarRuleType(String ruleName, OperatingEnvironmentDetectionStrategy os) { + this.ruleName = ruleName; + this.os = os; + this.downloadedJarLocations = null; + } + + /** + * Use this method only if there is a new rule type, and you don't want to implement a specialized subclass. + * Normally this is only used for tests, but if someone implemented a rule that used ~/.m2/repository this + * would be the way to implement it. + */ + public BazelExternalJarRuleType(String ruleName, OperatingEnvironmentDetectionStrategy os, List downloadedJarLocations, + boolean isUsedInWorkspace) { + this.ruleName = ruleName; + this.os = os; + this.downloadedJarLocations = downloadedJarLocations; + this.isUsedInWorkspace = isUsedInWorkspace; + } + + /** + * This rule type is known to the Bazel SDK, but is it being used by the workspace? Specialized implementations of this + * method will likely look into the WORKSPACE file to determine this. + */ + public boolean isUsedInWorkspace(BazelWorkspace bazelWorkspace) { + return isUsedInWorkspace; + } + + /** + * Get the locations of the local jars downloaded from the remote repo. These are the root directories, + * and the jars can be nested at arbitrary depths below each of these locations. + */ + public List getDownloadedJarLocations(BazelWorkspace bazelWorkspace) { + return downloadedJarLocations; + } + + /** + * Something about the workspace changed. Discard computed work for the passed workspace. + * If the parameter is null, discard the work for all workspaces. + */ + public void discardComputedWork(BazelWorkspace bazelWorkspace) { + + } +} diff --git a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/CoursierUtil.java b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/CoursierUtil.java new file mode 100644 index 000000000..ffdda9964 --- /dev/null +++ b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/CoursierUtil.java @@ -0,0 +1,61 @@ +package com.salesforce.bazel.sdk.lang.jvm.external; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import com.salesforce.bazel.sdk.model.BazelWorkspace; +import com.salesforce.bazel.sdk.workspace.OperatingEnvironmentDetectionStrategy; + +/** + * coursier is a Maven repo integration tool that is used in some cases by maven_install. + * This class encapsulates the behaviors of coursier. + */ +public class CoursierUtil { + private static final String COURSIER_CACHE_LOCATION_LINUX = "/.cache/coursier/v1"; + private static final String COURSIER_CACHE_LOCATION_MACOS = "/Library/Caches/Coursier/v1"; + private static final String COURSIER_CACHE_LOCATION_WINDOWS = "/Coursier/cache/v1"; + private Map coursierCacheLocations = new HashMap<>(); + + + /** + * If the user ran this: bazel run @unpinned_maven//:pin + * This invoked Coursier (a jar downloader) which populated the Coursier cache on the machine. + * The cache location is platform dependent, and global per user. So if you have multiple Bazel workspaces + * this cache location will have the union of all jars used by the workspaces. + */ + public File addCoursierCacheLocation(BazelWorkspace bazelWorkspace, OperatingEnvironmentDetectionStrategy os) { + + // workspace name is the key to our cached data + String workspaceName = bazelWorkspace.getName(); + File coursierCacheLocation = coursierCacheLocations.get(workspaceName); + if (coursierCacheLocation == null) { + // TODO we are just computing the default cache location, but there are ways to force coursier to use alternate locations. + // we should also factor that in, but that will be HARD. Details: + // https://get-coursier.io/docs/2.0.0-RC5-3/cache.html#default-location + + String defaultLocation = null; + String homedir = System.getProperty("user.home"); + String osName = os.getOperatingSystemName(); + if ("linux".equals(osName)) { + defaultLocation = homedir + COURSIER_CACHE_LOCATION_LINUX; + } else if ("darwin".equals(osName)) { + defaultLocation = homedir + COURSIER_CACHE_LOCATION_MACOS; + } else if ("windows".equals(osName)) { + defaultLocation = homedir + COURSIER_CACHE_LOCATION_WINDOWS; + } else { + // or is there a better default? + defaultLocation = homedir + COURSIER_CACHE_LOCATION_LINUX; + } + + coursierCacheLocation = new File(defaultLocation); + coursierCacheLocations.put(workspaceName, coursierCacheLocation); + } + return coursierCacheLocation; + } + + public void discardComputedWork(BazelWorkspace bazelWorkspace) { + String workspaceName = bazelWorkspace.getName(); + coursierCacheLocations.remove(workspaceName); + } +} diff --git a/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/MavenInstallExternalJarRuleType.java b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/MavenInstallExternalJarRuleType.java new file mode 100644 index 000000000..b9159d1d5 --- /dev/null +++ b/bzl-java-sdk/src/main/java/com/salesforce/bazel/sdk/lang/jvm/external/MavenInstallExternalJarRuleType.java @@ -0,0 +1,141 @@ +package com.salesforce.bazel.sdk.lang.jvm.external; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.salesforce.bazel.sdk.model.BazelWorkspace; +import com.salesforce.bazel.sdk.workspace.OperatingEnvironmentDetectionStrategy; + +/** + * Specialization of BazelExternalJarRuleType for the maven_install rule. + *

+ * The maven_install rule is a bit tricky, in that it is not reliable in where to find the + * downloaded jars. Also, the source jars may not be there. This will be an evolving solution + * as we better understand how to make this more reliable. + */ +public class MavenInstallExternalJarRuleType extends BazelExternalJarRuleType { + // these options are expected to be driven by tool preferences + public static boolean cachedJars_supplyCoursierCacheLocation = false; + public static boolean cachedJars_supplyWorkspaceBazelBinLocations = true; + + // derived from the WORKSPACE file (or .bzl files included from the WORKSPACE) + // each maven_install rule invocation must have a unique namespace, the default value is "maven" + private static Map> mavenInstallNamespaces; + + // maven_install can sometimes use coursier to download jars + // delegate to a dedicated util to worry about that + private CoursierUtil coursierUtil = new CoursierUtil(); + + public MavenInstallExternalJarRuleType(OperatingEnvironmentDetectionStrategy os) { + super(BazelExternalJarRuleManager.MAVEN_INSTALL, os); + init(); + } + + private void init() { + mavenInstallNamespaces = new HashMap<>(); + } + + /** + * This rule type is known to the Bazel SDK, but is it being used by the workspace? Specialized implementations of this + * method will likely look into the WORKSPACE file to determine this. + */ + public boolean isUsedInWorkspace(BazelWorkspace bazelWorkspace) { + // TODO BazelWorkspace should have some parsing functions for retrieving rule data. + // until we have that, we are going to do a cheat and just look for the default maven namespace directory in bazel-bin/external + File externalDir = new File(bazelWorkspace.getBazelBinDirectory(), "external"); + if (externalDir.exists()) { + File[] markerFiles = externalDir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return "maven".equals(name); + } + }); + isUsedInWorkspace = markerFiles.length > 0; + } + + return isUsedInWorkspace; + } + + /** + * Get the locations of the local jars downloaded from the remote repo. These are the root directories, + * and the jars can be nested at arbitrary depths below each of these locations. + */ + @Override + public List getDownloadedJarLocations(BazelWorkspace bazelWorkspace) { + // workspace name is the key to our cached data + String workspaceName = bazelWorkspace.getName(); + List namespaces = mavenInstallNamespaces.get(workspaceName); + + // namespaces are cached, as they will change almost never + if (namespaces == null) { + namespaces = loadNamespaces(bazelWorkspace); + mavenInstallNamespaces.put(workspaceName, namespaces); + } + + // locations are computed each time, as they can change based bazel clean activities + List localJarLocationsNew = new ArrayList<>(); + if (cachedJars_supplyCoursierCacheLocation) { + File coursierCacheLocation = coursierUtil.addCoursierCacheLocation(bazelWorkspace, os); + if (coursierCacheLocation != null) { + localJarLocationsNew.add(coursierCacheLocation); + } + } + if (cachedJars_supplyWorkspaceBazelBinLocations) { + addBazelBinLocations(bazelWorkspace, namespaces, localJarLocationsNew); + } + + // for thread safety, we build the list in a local var, and then switch at the end here + downloadedJarLocations = localJarLocationsNew; + return downloadedJarLocations; + } + + /** + * Something about the workspace changed. Discard computed work for the passed workspace. + * If the parameter is null, discard the work for all workspaces. + */ + public void discardComputedWork(BazelWorkspace bazelWorkspace) { + if (bazelWorkspace == null) { + init(); + return; + } + String workspaceName = bazelWorkspace.getName(); + mavenInstallNamespaces.remove(workspaceName); + + coursierUtil.discardComputedWork(bazelWorkspace); + } + + protected List loadNamespaces(BazelWorkspace bazelWorkspace) { + List namespaces = new ArrayList<>(); + + // TODO 'maven' is the default namespace, but there may be others. + // for each invocation of maven_install rule, there is a distinct namespace identified by the name attribute: + // maven_install(name = "deprecated", ... + // TODO BazelWorkspace should have some parsing functions for retrieving rule data. + // in this case, we need to find maven_install rule invocations, and pluck the list of name attributes. + // for our primary repo, this is complicated by the fact that the maven_install rules are actually in + // .bzl files brought in by load() statements in the WORKSPACE + namespaces.add("maven"); + + return namespaces; + } + + /** + * maven_install will download jars (and sometimes source jars) into directories such as: + * ROOT/bazel-bin/external/maven + * ROOT/bazel-bin/external/webtest + * if you have two maven_install rules with names 'maven' and 'webtest' + */ + protected void addBazelBinLocations(BazelWorkspace bazelWorkspace, List namespaces, List localJarLocationsNew) { + File bazelbinDir = bazelWorkspace.getBazelBinDirectory(); + File externalDir = new File(bazelbinDir, "external"); + + for (String namespace : namespaces) { + File rootMavenInstallDir = new File(externalDir, namespace); + localJarLocationsNew.add(rootMavenInstallDir); + } + } +} diff --git a/docs/using_the_feature_settings.md b/docs/using_the_feature_settings.md index 8d107d385..959412c17 100644 --- a/docs/using_the_feature_settings.md +++ b/docs/using_the_feature_settings.md @@ -18,7 +18,8 @@ Navigate to your Eclipse Preferences (menu option varies by platform) and click Topics: - The output location for each source directory - needed to launch Java apps - +- where to find the prefs file for the workspace +- global prefs: ~/.bazel/eclipse.properties ### Next Topic: Builds and the Bazel Eclipse Feature diff --git a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/BazelPluginActivator.java b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/BazelPluginActivator.java index 3bd253c09..b87a7cd8a 100644 --- a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/BazelPluginActivator.java +++ b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/BazelPluginActivator.java @@ -54,6 +54,7 @@ import com.salesforce.bazel.sdk.command.CommandBuilder; import com.salesforce.bazel.sdk.command.shell.ShellCommandBuilder; import com.salesforce.bazel.sdk.console.CommandConsoleFactory; +import com.salesforce.bazel.sdk.lang.jvm.external.BazelExternalJarRuleManager; import com.salesforce.bazel.sdk.logging.LogHelper; import com.salesforce.bazel.sdk.model.BazelConfigurationManager; import com.salesforce.bazel.sdk.model.BazelWorkspace; @@ -119,6 +120,11 @@ public class BazelPluginActivator extends AbstractUIPlugin { * Iteracts with preferences */ private static BazelConfigurationManager configurationManager; + + /** + * Manager for working with external jars + */ + private static BazelExternalJarRuleManager externalJarRuleManager; // LIFECYCLE @@ -142,9 +148,10 @@ public void start(BundleContext context) throws Exception { BazelProjectManager projectMgr = new EclipseBazelProjectManager(eclipseResourceHelper, eclipseJavaCoreHelper); OperatingEnvironmentDetectionStrategy osEnvStrategy = new RealOperatingEnvironmentDetectionStrategy(); BazelConfigurationManager configManager = new EclipseBazelConfigurationManager(eclipseResourceHelper); + BazelExternalJarRuleManager externalJarRuleManager = new BazelExternalJarRuleManager(osEnvStrategy); startInternal(aspectLocation, commandBuilder, consoleFactory, projectMgr, eclipseResourceHelper, - eclipseJavaCoreHelper, osEnvStrategy, configManager); + eclipseJavaCoreHelper, osEnvStrategy, configManager, externalJarRuleManager); } /** @@ -154,7 +161,8 @@ public void start(BundleContext context) throws Exception { */ public void startInternal(BazelAspectLocation aspectLocation, CommandBuilder commandBuilder, CommandConsoleFactory consoleFactory, BazelProjectManager projectMgr, ResourceHelper rh, - JavaCoreHelper javac, OperatingEnvironmentDetectionStrategy osEnv, BazelConfigurationManager configMgr) + JavaCoreHelper javac, OperatingEnvironmentDetectionStrategy osEnv, BazelConfigurationManager configMgr, + BazelExternalJarRuleManager externalJarRuleMgr) throws Exception { // reset internal state (this is so tests run in a clean env) bazelWorkspace = null; @@ -167,6 +175,7 @@ public void startInternal(BazelAspectLocation aspectLocation, CommandBuilder com osEnvStrategy = osEnv; bazelProjectManager = projectMgr; configurationManager = configMgr; + externalJarRuleManager = externalJarRuleMgr; // Get the bazel executable path from the settings, defaults to /usr/local/bin/bazel String bazelPath = configurationManager.getBazelExecutablePath(); @@ -315,6 +324,10 @@ public BazelConfigurationManager getConfigurationManager() { return configurationManager; } + public BazelExternalJarRuleManager getBazelExternalJarRuleManager() { + return externalJarRuleManager; + } + // LOGGING /** diff --git a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/builder/BazelBuilder.java b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/builder/BazelBuilder.java index 1a9c80cfc..cd7d19f7f 100644 --- a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/builder/BazelBuilder.java +++ b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/builder/BazelBuilder.java @@ -63,6 +63,7 @@ import com.google.common.collect.Lists; import com.salesforce.bazel.eclipse.BazelPluginActivator; import com.salesforce.bazel.eclipse.classpath.BazelClasspathContainer; +import com.salesforce.bazel.eclipse.classpath.BazelGlobalSearchClasspathContainer; import com.salesforce.bazel.eclipse.config.BazelEclipseProjectFactory; import com.salesforce.bazel.eclipse.runtime.api.JavaCoreHelper; import com.salesforce.bazel.eclipse.runtime.api.ResourceHelper; @@ -181,6 +182,7 @@ protected void clean(IProgressMonitor monitor) throws CoreException { } BazelClasspathContainer.clean(); + BazelGlobalSearchClasspathContainer.clean(); } private boolean buildProjects(BazelWorkspaceCommandRunner cmdRunner, Collection projects, diff --git a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/classpath/BazelGlobalSearchClasspathContainer.java b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/classpath/BazelGlobalSearchClasspathContainer.java index edd824c9b..6a22954cf 100644 --- a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/classpath/BazelGlobalSearchClasspathContainer.java +++ b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/classpath/BazelGlobalSearchClasspathContainer.java @@ -47,14 +47,15 @@ import org.osgi.service.prefs.BackingStoreException; import com.salesforce.bazel.eclipse.BazelPluginActivator; -import com.salesforce.bazel.eclipse.preferences.BazelPreferencePage; +import com.salesforce.bazel.eclipse.preferences.BazelPreferenceKeys; import com.salesforce.bazel.eclipse.runtime.api.ResourceHelper; import com.salesforce.bazel.eclipse.runtime.impl.EclipseWorkProgressMonitor; import com.salesforce.bazel.sdk.command.BazelCommandLineToolConfigurationException; import com.salesforce.bazel.sdk.index.BazelJvmIndexClasspath; -import com.salesforce.bazel.sdk.lang.jvm.BazelJvmClasspath; import com.salesforce.bazel.sdk.lang.jvm.BazelJvmClasspathResponse; +import com.salesforce.bazel.sdk.lang.jvm.external.BazelExternalJarRuleManager; import com.salesforce.bazel.sdk.model.BazelConfigurationManager; +import com.salesforce.bazel.sdk.workspace.OperatingEnvironmentDetectionStrategy; /** * The global search feature enables the user to search for types (e.g. Java classes) using the Open Type dialog, @@ -71,9 +72,9 @@ public class BazelGlobalSearchClasspathContainer extends BaseBazelClasspathConta public static final String CONTAINER_NAME = "com.salesforce.bazel.eclipse.BAZEL_GLOBAL_SEARCH_CONTAINER"; protected final BazelConfigurationManager config; - protected final BazelJvmIndexClasspath bazelClasspath; + protected final BazelJvmIndexClasspath bazelJvmIndexClasspath; - private static List instances = new ArrayList<>(); + private static List instances = new ArrayList<>(); public BazelGlobalSearchClasspathContainer(IProject eclipseProject) throws IOException, InterruptedException, BackingStoreException, JavaModelException, BazelCommandLineToolConfigurationException { @@ -84,22 +85,28 @@ public BazelGlobalSearchClasspathContainer(IProject eclipseProject, ResourceHelp throws IOException, InterruptedException, BackingStoreException, JavaModelException, BazelCommandLineToolConfigurationException { super(eclipseProject, resourceHelper); - config = BazelPluginActivator.getInstance().getConfigurationManager(); - - // lookup the directory that contains the cache of all the downloaded jars - // this depends on your jar tech (maven_install, jvm_import) and if you have - // overridden the chosen tech's default location - IPreferenceStore prefs = BazelPluginActivator.getInstance().getPreferenceStore(); - String jarCacheDir = prefs.getString(BazelPreferencePage.EXTERNAL_JAR_CACHE_PATH_PREF_NAME); - File jarCacheDirFile = null; + + BazelPluginActivator activator = BazelPluginActivator.getInstance(); + config = activator.getConfigurationManager(); + + OperatingEnvironmentDetectionStrategy os = activator.getOperatingEnvironmentDetectionStrategy(); + BazelExternalJarRuleManager externalJarManager = activator.getBazelExternalJarRuleManager(); + + // check if the user has provided an additional location to look for jars + List additionalJarLocations = null; + IPreferenceStore prefs = activator.getPreferenceStore(); + String jarCacheDir = prefs.getString(BazelPreferenceKeys.EXTERNAL_JAR_CACHE_PATH_PREF_NAME); if (jarCacheDir != null) { - jarCacheDirFile = new File(jarCacheDir); - if (!jarCacheDirFile.exists()) { - jarCacheDirFile = null; + // user has specified a location, make sure it exists + File jarCacheDirFile = new File(jarCacheDir); + if (jarCacheDirFile.exists()) { + additionalJarLocations = new ArrayList<>(); + additionalJarLocations.add(jarCacheDirFile); } } - bazelClasspath = new BazelJvmIndexClasspath(this.bazelWorkspace, jarCacheDirFile); + bazelJvmIndexClasspath = new BazelJvmIndexClasspath(this.bazelWorkspace, os, externalJarManager, additionalJarLocations); + instances.add(bazelJvmIndexClasspath); } @Override @@ -122,7 +129,7 @@ protected BazelJvmClasspathResponse computeClasspath() { // the Java SDK will produce a list of logical classpath entries long startTime = System.currentTimeMillis(); - BazelJvmClasspathResponse computedClasspath = bazelClasspath.getClasspathEntries(new EclipseWorkProgressMonitor(null)); + BazelJvmClasspathResponse computedClasspath = bazelJvmIndexClasspath.getClasspathEntries(new EclipseWorkProgressMonitor(null)); long endTime = System.currentTimeMillis(); BazelPluginActivator.info("BazelSearchContainerClasspath completed indexing in "+(endTime-startTime)+" milliseconds."); @@ -132,8 +139,8 @@ protected BazelJvmClasspathResponse computeClasspath() { // TODO this clean() method should not be static public static void clean() { - for (BazelJvmClasspath instance : instances) { - instance.clean(); + for (BazelJvmIndexClasspath instance : instances) { + instance.clearCache(); } } } \ No newline at end of file diff --git a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/config/EclipseBazelConfigurationManager.java b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/config/EclipseBazelConfigurationManager.java index 1de9a53fa..a4be363f4 100644 --- a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/config/EclipseBazelConfigurationManager.java +++ b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/config/EclipseBazelConfigurationManager.java @@ -40,7 +40,7 @@ import org.eclipse.jface.util.PropertyChangeEvent; import com.salesforce.bazel.eclipse.BazelPluginActivator; -import com.salesforce.bazel.eclipse.preferences.BazelPreferencePage; +import com.salesforce.bazel.eclipse.preferences.BazelPreferenceKeys; import com.salesforce.bazel.eclipse.runtime.api.ResourceHelper; import com.salesforce.bazel.sdk.command.BazelCommandManager; import com.salesforce.bazel.sdk.model.BazelConfigurationManager; @@ -60,7 +60,7 @@ public EclipseBazelConfigurationManager(ResourceHelper resourceHelper) { @Override public String getBazelExecutablePath() { IPreferenceStore prefsStore = this.resourceHelper.getPreferenceStore(BazelPluginActivator.getInstance()); - return prefsStore.getString(BazelPreferencePage.BAZEL_PATH_PREF_NAME); + return prefsStore.getString(BazelPreferenceKeys.BAZEL_PATH_PREF_NAME); } @Override @@ -69,7 +69,7 @@ public void setBazelExecutablePathListener(BazelCommandManager bazelCommandManag prefsStore.addPropertyChangeListener(new IPropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent event) { - if (event.getProperty().equals(BazelPreferencePage.BAZEL_PATH_PREF_NAME)) { + if (event.getProperty().equals(BazelPreferenceKeys.BAZEL_PATH_PREF_NAME)) { bazelCommandManager.setBazelExecutablePath(event.getNewValue().toString()); } } @@ -95,7 +95,7 @@ public void setBazelWorkspacePath(String bazelWorkspacePath) { @Override public boolean isGlobalClasspathSearchEnabled() { IPreferenceStore prefsStore = this.resourceHelper.getPreferenceStore(BazelPluginActivator.getInstance()); - return prefsStore.getBoolean(BazelPreferencePage.GLOBALCLASSPATH_SEARCH_PREF_NAME); + return prefsStore.getBoolean(BazelPreferenceKeys.GLOBALCLASSPATH_SEARCH_PREF_NAME); } } \ No newline at end of file diff --git a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferenceInitializer.java b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferenceInitializer.java index 36996bfbc..e366e924c 100644 --- a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferenceInitializer.java +++ b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferenceInitializer.java @@ -43,9 +43,7 @@ import org.eclipse.jface.preference.IPreferenceStore; import com.salesforce.bazel.eclipse.BazelPluginActivator; -import com.salesforce.bazel.sdk.lang.jvm.BazelJvmExternalUtil; import com.salesforce.bazel.sdk.util.BazelExecutableUtil; -import com.salesforce.bazel.sdk.workspace.OperatingEnvironmentDetectionStrategy; /** * Initialize the preferences of Bazel. @@ -57,20 +55,21 @@ public void initializeDefaultPreferences() { IPreferenceStore store = BazelPluginActivator.getInstance().getPreferenceStore(); Properties defaultPrefs = loadMasterPreferences(); + // USER FACING PREFS (visible on Prefs page) + String bazelExecLocationFromEnv = BazelExecutableUtil.which("bazel", "/usr/local/bin/bazel"); - String value = defaultPrefs.getProperty(BazelPreferencePage.BAZEL_PATH_PREF_NAME, bazelExecLocationFromEnv); - store.setDefault(BazelPreferencePage.BAZEL_PATH_PREF_NAME, value); + String value = defaultPrefs.getProperty(BazelPreferenceKeys.BAZEL_PATH_PREF_NAME, bazelExecLocationFromEnv); + store.setDefault(BazelPreferenceKeys.BAZEL_PATH_PREF_NAME, value); // enable global classpath search by default - value = defaultPrefs.getProperty(BazelPreferencePage.GLOBALCLASSPATH_SEARCH_PREF_NAME, "true"); - store.setDefault(BazelPreferencePage.GLOBALCLASSPATH_SEARCH_PREF_NAME, "true".equals(value)); + value = defaultPrefs.getProperty(BazelPreferenceKeys.GLOBALCLASSPATH_SEARCH_PREF_NAME, "false"); + store.setDefault(BazelPreferenceKeys.GLOBALCLASSPATH_SEARCH_PREF_NAME, "true".equals(value)); - // determine the location on local disk for the downloaded jar cache - OperatingEnvironmentDetectionStrategy os = BazelPluginActivator.getInstance().getOperatingEnvironmentDetectionStrategy(); - File externalJarCacheDefaultLocation = BazelJvmExternalUtil.getDownloadedJarCacheLocation(os); - String externalJarCacheDefaultPath = externalJarCacheDefaultLocation.getAbsolutePath(); - value = defaultPrefs.getProperty(BazelPreferencePage.EXTERNAL_JAR_CACHE_PATH_PREF_NAME, externalJarCacheDefaultPath); - store.setDefault(BazelPreferencePage.EXTERNAL_JAR_CACHE_PATH_PREF_NAME, value); + // BEF DEVELOPER PREFS (for efficient repetitive testing of BEF) + value = defaultPrefs.getProperty(BazelPreferenceKeys.BAZEL_DEFAULT_WORKSPACE_PATH_PREF_NAME); + if (value != null) { + store.setDefault(BazelPreferenceKeys.BAZEL_DEFAULT_WORKSPACE_PATH_PREF_NAME, value); + } } /** diff --git a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferenceKeys.java b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferenceKeys.java new file mode 100644 index 000000000..350bd52ab --- /dev/null +++ b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferenceKeys.java @@ -0,0 +1,25 @@ +package com.salesforce.bazel.eclipse.preferences; + +/** + * Names of the preference keys. Look for usages of these variables to see how BEF uses prefs. + */ +public class BazelPreferenceKeys { + + // USER FACING PREFS (visible on Prefs page) + + // path the the bazel executable + public static final String BAZEL_PATH_PREF_NAME = "BAZEL_PATH"; + + // Global classpath search allows BEF to index all jars associated with a Bazel Workspace which makes them + // available for Open Type searches. These prefs enabled it, and tell BEF where to look for the local cache + // of downloaded jars. + public static final String GLOBALCLASSPATH_SEARCH_PREF_NAME = "GLOBALCLASSPATH_SEARCH_ENABLED"; + public static final String EXTERNAL_JAR_CACHE_PATH_PREF_NAME = "EXTERNAL_JAR_CACHE_PATH"; + + + // BEF DEVELOPER PREFS (for efficient repetitive testing of BEF) + + // The import wizard will be populated by this path if set, which saves time during repetitive testing of imports + public static final String BAZEL_DEFAULT_WORKSPACE_PATH_PREF_NAME = "BAZEL_DEFAULT_WORKSPACE_PATH"; + +} diff --git a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferencePage.java b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferencePage.java index ae8043d38..848c55fa9 100644 --- a/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferencePage.java +++ b/plugin-core/src/main/java/com/salesforce/bazel/eclipse/preferences/BazelPreferencePage.java @@ -53,13 +53,9 @@ */ public class BazelPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { - public static final String BAZEL_PATH_PREF_NAME = "BAZEL_PATH"; - public static final String GLOBALCLASSPATH_SEARCH_PREF_NAME = "GLOBALCLASSPATH_SEARCH_ENABLED"; - public static final String EXTERNAL_JAR_CACHE_PATH_PREF_NAME = "EXTERNAL_JAR_CACHE_PATH"; - private static class BazelBinaryFieldEditor extends FileFieldEditor { BazelBinaryFieldEditor(Composite parent) { - super(BAZEL_PATH_PREF_NAME, "Path to the &Bazel binary:", true, VALIDATE_ON_KEY_STROKE, parent); + super(BazelPreferenceKeys.BAZEL_PATH_PREF_NAME, "Path to the &Bazel binary:", true, VALIDATE_ON_KEY_STROKE, parent); } @Override @@ -91,13 +87,13 @@ public boolean isValid() { private static class BazelGlobalClasspathSearchEnabledFieldEditor extends BooleanFieldEditor { public BazelGlobalClasspathSearchEnabledFieldEditor(Composite parent) { - super(GLOBALCLASSPATH_SEARCH_PREF_NAME, "Enable &global classpath search?", SEPARATE_LABEL, parent); + super(BazelPreferenceKeys.GLOBALCLASSPATH_SEARCH_PREF_NAME, "Enable &global classpath search? (experimental)", SEPARATE_LABEL, parent); } } private static class BazelExternalDownloadCachePathEditor extends DirectoryFieldEditor { BazelExternalDownloadCachePathEditor(Composite parent) { - super(EXTERNAL_JAR_CACHE_PATH_PREF_NAME, "Path to the local &cache of downloaded jar files:", parent); + super(BazelPreferenceKeys.EXTERNAL_JAR_CACHE_PATH_PREF_NAME, "Optional: path to the local &cache of downloaded jar files:", parent); } @Override @@ -109,6 +105,10 @@ protected boolean doCheckState() { public boolean isValid() { try { String cachePath = getStringValue(); + if (cachePath.isEmpty()) { + // a cache path is optional + return true; + } File cachePathFile = new File(cachePath); if (!cachePathFile.exists()) { setErrorMessage(cachePath + " does not exist"); diff --git a/plugin-core/src/test/java/com/salesforce/bazel/eclipse/mock/MockEclipse.java b/plugin-core/src/test/java/com/salesforce/bazel/eclipse/mock/MockEclipse.java index 236605ea2..115329137 100644 --- a/plugin-core/src/test/java/com/salesforce/bazel/eclipse/mock/MockEclipse.java +++ b/plugin-core/src/test/java/com/salesforce/bazel/eclipse/mock/MockEclipse.java @@ -30,16 +30,17 @@ import org.eclipse.core.resources.IProject; import com.salesforce.bazel.eclipse.BazelPluginActivator; -import com.salesforce.bazel.eclipse.config.EclipseBazelProjectManager; import com.salesforce.bazel.eclipse.config.EclipseBazelConfigurationManager; +import com.salesforce.bazel.eclipse.config.EclipseBazelProjectManager; import com.salesforce.bazel.eclipse.launch.BazelLaunchConfigurationDelegate; -import com.salesforce.bazel.eclipse.preferences.BazelPreferencePage; +import com.salesforce.bazel.eclipse.preferences.BazelPreferenceKeys; import com.salesforce.bazel.sdk.command.test.MockBazelAspectLocation; import com.salesforce.bazel.sdk.command.test.MockCommandBuilder; import com.salesforce.bazel.sdk.command.test.MockCommandConsole; import com.salesforce.bazel.sdk.command.test.TestBazelCommandEnvironmentFactory; -import com.salesforce.bazel.sdk.project.BazelProjectManager; +import com.salesforce.bazel.sdk.lang.jvm.external.BazelExternalJarRuleManager; import com.salesforce.bazel.sdk.model.BazelConfigurationManager; +import com.salesforce.bazel.sdk.project.BazelProjectManager; import com.salesforce.bazel.sdk.workspace.OperatingEnvironmentDetectionStrategy; import com.salesforce.bazel.sdk.workspace.test.TestBazelWorkspaceFactory; @@ -73,6 +74,7 @@ public class MockEclipse { private BazelLaunchConfigurationDelegate launchDelegate; private BazelProjectManager projectManager; private BazelConfigurationManager configManager; + private BazelExternalJarRuleManager externalJarRuleManager; // Bazel/filesystem layer (some mocks, some real filesystem artifacts) private TestBazelWorkspaceFactory bazelWorkspaceFactory; @@ -114,7 +116,7 @@ private void setup(TestBazelWorkspaceFactory bazelWorkspaceFactory, File testTem this.mockPrefsStore = new MockIPreferenceStore(); this.mockIProjectFactory = new MockIProjectFactory(); this.mockJavaCoreHelper = new MockJavaCoreHelper(); - this.mockPrefsStore.strings.put(BazelPreferencePage.BAZEL_PATH_PREF_NAME, + this.mockPrefsStore.strings.put(BazelPreferenceKeys.BAZEL_PATH_PREF_NAME, this.bazelCommandEnvironment.bazelExecutable.getAbsolutePath()); // feature collaborators @@ -122,13 +124,14 @@ private void setup(TestBazelWorkspaceFactory bazelWorkspaceFactory, File testTem this.launchDelegate = new BazelLaunchConfigurationDelegate(); this.configManager = new EclipseBazelConfigurationManager(mockResourceHelper); this.projectManager = new EclipseBazelProjectManager(this.mockResourceHelper, this.mockJavaCoreHelper); + this.externalJarRuleManager = new BazelExternalJarRuleManager(this.mockOsEnvStrategy); // initialize our plugins/feature with all the mock infrastructure // this simulates how our feature starts up when run inside of Eclipse this.pluginActivator.startInternal(this.bazelCommandEnvironment.bazelAspectLocation, this.bazelCommandEnvironment.commandBuilder, this.bazelCommandEnvironment.commandConsole, this.projectManager, this.mockResourceHelper, this.mockJavaCoreHelper, this.mockOsEnvStrategy, - this.configManager); + this.configManager, this.externalJarRuleManager); // At this point our plugins are wired up, the Bazel workspace is created, but the user // has not run a Bazel Import... wizard yet. See EclipseFunctionalTestEnvironmentFactory