diff --git a/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/BazelPackage.java b/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/BazelPackage.java index d7aaff39..31b0f198 100644 --- a/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/BazelPackage.java +++ b/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/BazelPackage.java @@ -9,12 +9,14 @@ import java.nio.file.Path; import java.util.List; +import java.util.Map; import java.util.Objects; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Status; +import com.salesforce.bazel.sdk.command.querylight.Target; import com.salesforce.bazel.sdk.model.BazelLabel; /** @@ -71,6 +73,7 @@ public static boolean isBuildFileName(String fileName) { private final BazelWorkspace parent; private final BazelLabel label; private final IPath packagePath; + private Map targets; BazelPackage(BazelWorkspace parent, IPath packagePath) throws NullPointerException, IllegalArgumentException { this.packagePath = @@ -87,7 +90,9 @@ protected BazelPackageInfo createInfo() throws CoreException { Status.error(format("Package '%s' does not exist in workspace '%s'!", label, parent.getName()))); } - var targets = BazelPackageInfo.queryForTargets(this, getCommandExecutor()); + if (targets == null) { + targets = BazelPackageInfo.queryForTargets(this, getCommandExecutor()); + } return new BazelPackageInfo(buildFile, this, targets); } @@ -344,4 +349,8 @@ public void rediscoverBazelProject() throws CoreException { getInfo(); } + public void setTargets(Map targets) { + this.targets = targets; + } + } diff --git a/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/BazelWorkspace.java b/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/BazelWorkspace.java index 0e414402..3fe6b53c 100644 --- a/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/BazelWorkspace.java +++ b/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/BazelWorkspace.java @@ -17,18 +17,20 @@ import static com.salesforce.bazel.eclipse.core.BazelCoreSharedContstants.FILE_NAME_REPO_BAZEL; import static com.salesforce.bazel.eclipse.core.BazelCoreSharedContstants.FILE_NAME_WORKSPACE; import static com.salesforce.bazel.eclipse.core.BazelCoreSharedContstants.FILE_NAME_WORKSPACE_BAZEL; -import static com.salesforce.bazel.eclipse.core.model.BazelPackageInfo.queryForTargets; import static java.lang.String.format; import static java.nio.file.Files.isDirectory; import static java.nio.file.Files.isRegularFile; import static java.util.Objects.requireNonNull; import static java.util.function.Predicate.not; +import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import java.nio.file.Path; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Stream; @@ -43,6 +45,8 @@ import com.salesforce.bazel.eclipse.core.projectview.BazelProjectView; import com.salesforce.bazel.sdk.BazelVersion; import com.salesforce.bazel.sdk.command.BazelBinary; +import com.salesforce.bazel.sdk.command.BazelQueryForTargetProtoCommand; +import com.salesforce.bazel.sdk.command.querylight.Target; import com.salesforce.bazel.sdk.model.BazelLabel; /** @@ -616,7 +620,7 @@ public void open(Collection bazelPackages) throws CoreException { } // open all closed projects - var targetsByPackage = queryForTargets(this, closedPackages, getCommandExecutor()); + var targetsByPackage = queryForTargetsWithDependencies(this, closedPackages, getCommandExecutor()); for (BazelPackage bazelPackage : closedPackages) { if (bazelPackage.hasInfo()) { continue; @@ -644,6 +648,75 @@ public void open(Collection bazelPackages) throws CoreException { } } + public Map> queryForTargetsWithDependencies(BazelWorkspace bazelWorkspace, + Collection bazelPackages, BazelElementCommandExecutor bazelElementCommandExecutor) + throws CoreException { + // bazel query 'kind(rule, deps(//foo:all + //bar:all))"' + + if (bazelPackages.isEmpty()) { + return Collections.emptyMap(); + } + var workspaceRoot = bazelWorkspace.getLocation().toPath(); + var query = bazelPackages.stream() + .map(bazelPackage -> format("//%s:all", bazelPackage.getWorkspaceRelativePath())) + .collect(joining(" + ")); + + Map bazelPackageByWorkspaceRelativePath = new HashMap<>(); + bazelPackages.stream() + .forEach(p -> bazelPackageByWorkspaceRelativePath.put(p.getWorkspaceRelativePath().toString(), p)); + + query = "kind(rule, deps(" + query + "))"; + Map> result = new HashMap<>(); + LOG.debug("{}: querying Bazel for list of targets from: {}", bazelWorkspace, query); + var queryResult = bazelElementCommandExecutor.runQueryWithoutLock( + new BazelQueryForTargetProtoCommand( + workspaceRoot, + query, + true /* keep going */, + List.of("--noproto:locations", "--noproto:default_values", "--noimplicit_deps", "--notool_deps"), + format( + "Loading targets for %d %s", + bazelPackages.size(), + bazelPackages.size() == 1 ? "package" : "packages"))); + for (Target target : queryResult) { + if (!target.hasRule()) { + LOG.trace("{}: ignoring target: {}", bazelWorkspace, target); + System.out.println(); + continue; + } + + try { + BazelLabel.validateLabelPath(target.rule().name(), true); + } catch (Exception e) { + LOG.trace("{}: ignoring target: {}", bazelWorkspace, target); + continue; + } + LOG.trace("{}: found target: {}", bazelWorkspace, target); + var targetLabel = new BazelLabel(target.rule().name()); + + var bazelPackage = bazelPackageByWorkspaceRelativePath.get(targetLabel.getPackagePath()); + if (bazelPackage == null) { + // LOG.debug("{}: ignoring target for unknown package: {}", bazelWorkspace, targetLabel); + // continue; + var packageLabel = targetLabel.getPackageLabel(); + if (bazelWorkspace.isRootedAtThisWorkspace(packageLabel)) { + bazelPackage = bazelWorkspace.getBazelPackage(packageLabel); + } + } + if (bazelPackage == null) { + LOG.debug("{}: ignoring target for unknown package: {}", bazelWorkspace, targetLabel); + continue; + } + if (!result.containsKey(bazelPackage)) { + result.put(bazelPackage, new HashMap<>()); + } + + var targetName = targetLabel.getTargetName(); + result.get(bazelPackage).put(targetName, target); + } + return result; + } + Path workspacePath() { return root.toPath(); } diff --git a/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/BuildfileDrivenProvisioningStrategy.java b/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/BuildfileDrivenProvisioningStrategy.java index e546c98c..a6f34829 100644 --- a/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/BuildfileDrivenProvisioningStrategy.java +++ b/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/BuildfileDrivenProvisioningStrategy.java @@ -28,6 +28,7 @@ import com.google.idea.blaze.base.model.primitives.TargetName; import com.salesforce.bazel.eclipse.core.classpath.BazelClasspathScope; import com.salesforce.bazel.eclipse.core.classpath.CompileAndRuntimeClasspath; +import com.salesforce.bazel.eclipse.core.model.BazelPackage; import com.salesforce.bazel.eclipse.core.model.BazelProject; import com.salesforce.bazel.eclipse.core.model.BazelTarget; import com.salesforce.bazel.eclipse.core.model.BazelWorkspace; @@ -158,6 +159,7 @@ protected List doProvisionProjects(Collection ta monitor.beginTask("Provisioning projects", packages.size() * 3); var result = new ArrayList(); + var bazelPackages = new ArrayList(); for (Path packagePath : packages) { var bazelPackage = workspace.getBazelPackage(IPath.fromPath(packagePath)); @@ -171,6 +173,13 @@ protected List doProvisionProjects(Collection ta STRATEGY_NAME))); continue; } + bazelPackages.add(bazelPackage); + } + var targetsByPackage = + workspace.queryForTargetsWithDependencies(workspace, bazelPackages, workspace.getCommandExecutor()); + + for (BazelPackage bazelPackage : bazelPackages) { + bazelPackage.setTargets(targetsByPackage.get(bazelPackage)); // get the top-level macro calls var topLevelMacroCalls = bazelPackage.getBazelBuildFile().getTopLevelCalls(); diff --git a/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/JavaAspectsClasspathInfo.java b/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/JavaAspectsClasspathInfo.java index 6c166b96..a4af98d6 100644 --- a/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/JavaAspectsClasspathInfo.java +++ b/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/JavaAspectsClasspathInfo.java @@ -30,6 +30,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IWorkspaceRoot; @@ -61,6 +62,7 @@ import com.google.idea.blaze.java.sync.model.BlazeJarLibrary; import com.salesforce.bazel.eclipse.core.classpath.CompileAndRuntimeClasspath; import com.salesforce.bazel.eclipse.core.classpath.CompileAndRuntimeClasspath.Builder; +import com.salesforce.bazel.eclipse.core.model.BazelPackage; import com.salesforce.bazel.eclipse.core.model.BazelProject; import com.salesforce.bazel.eclipse.core.model.BazelTarget; import com.salesforce.bazel.eclipse.core.model.BazelWorkspace; @@ -68,6 +70,7 @@ import com.salesforce.bazel.eclipse.core.model.discovery.classpath.ClasspathEntry; import com.salesforce.bazel.sdk.command.BazelBuildWithIntelliJAspectsCommand; import com.salesforce.bazel.sdk.command.querylight.BazelRuleAttribute; +import com.salesforce.bazel.sdk.command.querylight.Target; import com.salesforce.bazel.sdk.model.BazelLabel; /** @@ -331,6 +334,11 @@ public IStatus addTarget(BazelTarget bazelTarget) throws CoreException { * @throws CoreException */ public CompileAndRuntimeClasspath compute() throws CoreException { + return compute(null); + } + + public CompileAndRuntimeClasspath compute(Map> targetsByPackage) + throws CoreException { // the code below is copied and adapted from BlazeJavaWorkspaceImporter var classpathBuilder = new Builder(); @@ -359,7 +367,7 @@ public CompileAndRuntimeClasspath compute() throws CoreException { var targetKey = targetLabel != null ? TargetKey.forPlainTarget(targetLabel) : null; library = new BlazeJarLibrary(libraryArtifact, targetKey); } - var entry = resolveLibrary(library); + var entry = resolveLibrary(library, targetsByPackage); if (entry != null) { if (!validateEntry(entry)) { continue; @@ -400,7 +408,7 @@ public CompileAndRuntimeClasspath compute() throws CoreException { // Collect jars referenced by direct deps for (TargetKey targetKey : directDeps) { - var entries = resolveDependency(targetKey); + var entries = resolveDependency(targetKey, targetsByPackage); for (ClasspathEntry entry : entries) { if (validateEntry(entry)) { classpathBuilder.addCompileEntry(entry); @@ -410,7 +418,7 @@ public CompileAndRuntimeClasspath compute() throws CoreException { // Collect jars referenced by runtime deps for (TargetKey targetKey : runtimeDeps) { - var entries = resolveDependency(targetKey); + var entries = resolveDependency(targetKey, targetsByPackage); var addRuntimeDependencyAsCompileEntry = includeRuntimeDependencyAsProjectCompileDependency(targetKey); for (ClasspathEntry entry : entries) { @@ -585,8 +593,9 @@ protected boolean relevantDep(Deps.Dependency dep) { return (dep.getKind() == Deps.Dependency.Kind.EXPLICIT) || (dep.getKind() == Deps.Dependency.Kind.IMPLICIT); } - protected Collection resolveDependency(TargetKey targetKey) throws CoreException { - var projectEntry = resolveProject(targetKey); + protected Collection resolveDependency(TargetKey targetKey, + Map> targetsByPackage) throws CoreException { + var projectEntry = resolveProject(targetKey, targetsByPackage); if (projectEntry != null) { return Set.of(projectEntry); } @@ -623,10 +632,11 @@ protected BlazeArtifact resolveJdepsOutput(TargetIdeInfo target) { return locationDecoder.resolveOutput(javaIdeInfo.getJdepsFile()); } - private ClasspathEntry resolveLibrary(BlazeJarLibrary library) throws CoreException { + private ClasspathEntry resolveLibrary(BlazeJarLibrary library, + Map> targetsByPackage) throws CoreException { // find project in workspace if possible if (library.targetKey != null) { - var projectEntry = resolveProject(library.targetKey); + var projectEntry = resolveProject(library.targetKey, targetsByPackage); if (projectEntry != null) { return projectEntry; } @@ -636,7 +646,8 @@ private ClasspathEntry resolveLibrary(BlazeJarLibrary library) throws CoreExcept return resolveJar(library.libraryArtifact); } - private ClasspathEntry resolveProject(final Label targetLabel) throws CoreException { + private ClasspathEntry resolveProject(final Label targetLabel, + Map> targetsByPackage) throws CoreException { var workspace = bazelWorkspace; // check for project mapping (it trumps everything) @@ -686,10 +697,18 @@ private ClasspathEntry resolveProject(final Label targetLabel) throws CoreExcept } } var bazelPackage = workspace.getBazelPackage(forPosix(targetLabel.blazePackage().relativePath())); - var bazelTarget = bazelPackage.getBazelTarget(targetLabel.targetName().toString()); - if (bazelTarget.hasBazelProject() && bazelTarget.getBazelProject().getProject().isAccessible()) { - // a direct target match is preferred - return newProjectReference(targetLabel, bazelTarget.getBazelProject()); + if (targetsByPackage != null) { + var targets = targetsByPackage.get(bazelPackage); + bazelPackage.setTargets(targets); + } + var strategy = new TargetDiscoveryAndProvisioningExtensionLookup() + .createTargetProvisioningStrategy(bazelWorkspace.getBazelProjectView()); + if (strategy instanceof ProjectPerTargetProvisioningStrategy) { + var bazelTarget = bazelPackage.getBazelTarget(targetLabel.targetName().toString()); + if (bazelTarget.hasBazelProject() && bazelTarget.getBazelProject().getProject().isAccessible()) { + // a direct target match is preferred + return newProjectReference(targetLabel, bazelTarget.getBazelProject()); + } } if (bazelPackage.hasBazelProject() && bazelPackage.getBazelProject().getProject().isAccessible()) { // we have to check the target name is part of the enabled project list @@ -701,7 +720,7 @@ private ClasspathEntry resolveProject(final Label targetLabel) throws CoreExcept .anyMatch(t -> t.getTargetName().equals(targetName))) { return newProjectReference(targetLabel, bazelPackage.getBazelProject()); } - + var bazelTarget = bazelPackage.getBazelTarget(targetLabel.targetName().toString()); // it may be possible that the target is explicitly hidden from IDEs // in this case, it won't match in the above condition, however, we still want to make it a project references // the reason is that we do expect the project to represent the package adequately @@ -715,12 +734,13 @@ private ClasspathEntry resolveProject(final Label targetLabel) throws CoreExcept return null; } - protected ClasspathEntry resolveProject(TargetKey targetKey) throws CoreException { + protected ClasspathEntry resolveProject(TargetKey targetKey, + Map> targetsByPackage) throws CoreException { if (!targetKey.isPlainTarget()) { return null; } - return resolveProject(targetKey.getLabel()); + return resolveProject(targetKey.getLabel(), targetsByPackage); } /** diff --git a/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/ProjectPerPackageProvisioningStrategy.java b/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/ProjectPerPackageProvisioningStrategy.java index 070bfa24..f80d9ef0 100644 --- a/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/ProjectPerPackageProvisioningStrategy.java +++ b/bundles/com.salesforce.bazel.eclipse.core/src/com/salesforce/bazel/eclipse/core/model/discovery/ProjectPerPackageProvisioningStrategy.java @@ -139,6 +139,14 @@ public Map computeClasspaths(Collectio var shardsToBuild = createShards(activeTargetsPerProject, workspace); monitor.setWorkRemaining(5 * shardsToBuild.size()); + Set packages = activeTargetsPerProject.values() + .stream() + .flatMap(Collection::stream) + .map(BazelTarget::getBazelPackage) + .filter(bp -> bp != null) + .collect(Collectors.toSet()); + var targetsByPackage = + workspace.queryForTargetsWithDependencies(workspace, packages, workspace.getCommandExecutor()); // run the build per shard var currentShardCount = 0; for (Map> shard : shardsToBuild) { @@ -200,6 +208,10 @@ public Map computeClasspaths(Collectio activeTargetsPerProject.get(bazelProject), () -> format("programming error: not targets for project: %s", bazelProject)); for (BazelTarget target : projectTargets) { + var bazelPackage = target.getBazelPackage(); + if (bazelPackage != null) { + bazelPackage.setTargets(targetsByPackage.get(bazelPackage)); + } var status = classpathInfo.addTarget(target); if (!status.isOK()) { buildPathProblems.add(status); @@ -207,7 +219,7 @@ public Map computeClasspaths(Collectio } // compute the classpath - var classpath = classpathInfo.compute(); + var classpath = classpathInfo.compute(targetsByPackage); // remove old marker deleteClasspathContainerProblems(bazelProject); @@ -314,10 +326,8 @@ private void createWarningsForFilesWithoutCommonRoot(BazelProject project, JavaS protected List doProvisionProjects(Collection targets, TracingSubMonitor monitor) throws CoreException { // initialize the list of allowed java like rules - var javaLikeRulesValue = getFileSystemMapper().getBazelWorkspace() - .getBazelProjectView() - .targetProvisioningSettings() - .get(JAVA_LIKE_RULES); + var workspace = getFileSystemMapper().getBazelWorkspace(); + var javaLikeRulesValue = workspace.getBazelProjectView().targetProvisioningSettings().get(JAVA_LIKE_RULES); if (javaLikeRulesValue != null) { Stream.of(javaLikeRulesValue.split(",")) .map(String::trim) @@ -331,11 +341,13 @@ protected List doProvisionProjects(Collection targets monitor.setWorkRemaining(targetsByPackage.size() * 3); + var targetsByPackageWithDependencies = workspace + .queryForTargetsWithDependencies(workspace, targetsByPackage.keySet(), workspace.getCommandExecutor()); + var result = new ArrayList(); for (Entry> entry : targetsByPackage.entrySet()) { var bazelPackage = entry.getKey(); var packageTargets = entry.getValue(); - monitor.subTask(bazelPackage.getName()); // skip the root package (not supported) @@ -346,7 +358,7 @@ protected List doProvisionProjects(Collection targets "The root package was skipped during sync because it's not supported by the project-per-package strategy. Consider excluding it in the .bazelproject file.")); continue; } - + bazelPackage.setTargets(targetsByPackageWithDependencies.get(bazelPackage)); // create the project for the package var project = provisionPackageProject(bazelPackage, monitor.slice(1));