Skip to content

Commit

Permalink
Support bazel_binary in project view
Browse files Browse the repository at this point in the history
  • Loading branch information
guw committed Aug 13, 2023
1 parent 07f80ab commit 1fed8ec
Show file tree
Hide file tree
Showing 13 changed files with 320 additions and 143 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.salesforce.bazel.eclipse.core.extensions;

import static java.lang.String.format;

import java.io.IOException;
import java.nio.file.Path;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;

import com.salesforce.bazel.sdk.command.BazelBinary;
import com.salesforce.bazel.sdk.command.BazelBinaryVersionDetector;

/**
* Calls <code>bazel --version</code> on a provided binary to identify the version use.
* <p>
* Calls {@link EclipseHeadlessBazelCommandExecutor#setBazelBinary(BazelBinary)} when done.
* </p>
*/
public final class DetectBazelVersionAndSetBinaryJob extends Job {
private final Path binary;
private final boolean wrapExecutionIntoShell;
private final Consumer<BazelBinary> binaryConsumer;
private final Supplier<BazelBinary> fallbackSupplier;

/**
* @param binary
* the binary to test
* @param wrapExecutionIntoShell
* <code>true</code> if shell wrapping is desired, <code>false</code> otherwise
* @param binaryConsumer
* receiver of the binary including the detected version (will be called with a fallback value in
* case of errors)
* @param fallbackSupplier
* supplier for fallback value in case of errors
*/
public DetectBazelVersionAndSetBinaryJob(Path binary, boolean wrapExecutionIntoShell,
Consumer<BazelBinary> binaryConsumer, Supplier<BazelBinary> fallbackSupplier) {
super(format("Detecting Bazel version...", binary));
this.binary = binary;
this.wrapExecutionIntoShell = wrapExecutionIntoShell;
this.binaryConsumer = binaryConsumer;
this.fallbackSupplier = fallbackSupplier;
setSystem(true);
setPriority(SHORT);
}

@Override
protected IStatus run(IProgressMonitor monitor) {
try {
var bazelVersion = new BazelBinaryVersionDetector(binary, wrapExecutionIntoShell).detectVersion();
binaryConsumer.accept(new BazelBinary(binary, bazelVersion));
return Status.OK_STATUS;
} catch (IOException e) {
binaryConsumer.accept(fallbackSupplier.get());
return Status.error(format("Unable to detect Bazel version of binary '%s'!", binary), e);
} catch (InterruptedException e) {
EclipseHeadlessBazelCommandExecutor.LOG.warn("Interrupted waiting for bazel --version to respond for binary '{}'", binary, e);
binaryConsumer.accept(fallbackSupplier.get());
return Status.CANCEL_STATUS;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,11 @@
import static com.salesforce.bazel.eclipse.preferences.BazelCorePreferenceKeys.PREF_KEY_USE_SHELL_ENVIRONMENT;
import static java.lang.String.format;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
Expand All @@ -26,47 +22,13 @@
import com.salesforce.bazel.eclipse.preferences.BazelCorePreferenceKeys;
import com.salesforce.bazel.sdk.BazelVersion;
import com.salesforce.bazel.sdk.command.BazelBinary;
import com.salesforce.bazel.sdk.command.BazelBinaryVersionDetector;
import com.salesforce.bazel.sdk.command.DefaultBazelCommandExecutor;

/**
* Headless version initializing the Bazel binary from Eclipse preferences.
*/
public class EclipseHeadlessBazelCommandExecutor extends DefaultBazelCommandExecutor {

/**
* Calls <code>bazel --version</code> on a provided binary to identify the version use.
* <p>
* Calls {@link EclipseHeadlessBazelCommandExecutor#setBazelBinary(BazelBinary)} when done.
* </p>
*/
protected final class DetectBazelVersionAndSetBinaryJob extends Job {
private final Path binary;

private DetectBazelVersionAndSetBinaryJob(Path binary) {
super(format("Detecting Bazel version...", binary));
this.binary = binary;
setSystem(true);
setPriority(SHORT);
}

@Override
protected IStatus run(IProgressMonitor monitor) {
try {
var bazelVersion = new BazelBinaryVersionDetector(binary, isWrapExecutionIntoShell()).detectVersion();
setBazelBinary(new BazelBinary(binary, bazelVersion));
return Status.OK_STATUS;
} catch (IOException e) {
setBazelBinary(UNKNOWN_BAZEL_BINARY);
return Status.error(format("Unable to detect Bazel version of binary '%s'!", binary), e);
} catch (InterruptedException e) {
LOG.warn("Interrupted waiting for bazel --version to respond for binary '{}'", binary, e);
setBazelBinary(UNKNOWN_BAZEL_BINARY);
return Status.CANCEL_STATUS;
}
}
}

static BazelBinary UNKNOWN_BAZEL_BINARY = new BazelBinary(Path.of("bazel"), new BazelVersion(999, 999, 999));
static Logger LOG = LoggerFactory.getLogger(EclipseHeadlessBazelCommandExecutor.class);

Expand Down Expand Up @@ -168,14 +130,20 @@ protected void initializeWrapExecutionIntoShellSettingFromPreferences() {
*/
protected Job scheduleInitBazelBinaryFromPreferencesJob() {
var binary = Path.of(Platform.getPreferencesService().get(PREF_KEY_BAZEL_BINARY, "bazel", preferencesLookup));
var job = new DetectBazelVersionAndSetBinaryJob(binary);
var job = new DetectBazelVersionAndSetBinaryJob(
binary,
isWrapExecutionIntoShell(),
this::setBazelBinary,
() -> UNKNOWN_BAZEL_BINARY);
job.schedule();
return job;
}

@Override
public void setBazelBinary(BazelBinary bazelBinary) {
LOG.info("Using default Bazel binary '{}' (version '{}')", bazelBinary.executable(),
LOG.info(
"Using default Bazel binary '{}' (version '{}')",
bazelBinary.executable(),
bazelBinary.bazelVersion());
super.setBazelBinary(bazelBinary);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@

import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -47,7 +49,8 @@ public BazelElementCommandExecutor(BazelElement<?, ?> executionContext) {
* Applies any Bazel workspace specific configuration to the command binary.
* <p>
* By default we have an Eclipse wide preference setting providing the Eclipse wide Bazel binary to use. However, a
* Bazel workspace may use a different Bazel version (eg., via <code>.bazelversion</code> file).
* Bazel workspace may use a different Bazel version (eg., via <code>.bazelversion</code> file or
* <code>.bazelproject</code> project view).
* </p>
* <p>
* We rely on a single, system wide Bazel binary (eg., Bazelisk or Bazel shell wrapper script) to resolve the
Expand All @@ -60,14 +63,22 @@ public BazelElementCommandExecutor(BazelElement<?, ?> executionContext) {
* the workspace to use
* @throws CoreException
*/
private void configureWithWorkspaceBazelVersion(BazelCommand<?> command, BazelWorkspace bazelWorkspace)
throws CoreException {
var bazelBinary = getExecutionService().getBazelBinary();
private void configureCommand(BazelCommand<?> command, BazelWorkspace bazelWorkspace) throws CoreException {
var bazelBinary = bazelWorkspace.getBazelBinary();
if (bazelBinary == null) {
bazelBinary = getExecutionService().getBazelBinary();
} else {
LOG.trace("Using binary from workspace: {}", bazelBinary);
}
var workspaceBazelVersion = bazelWorkspace.getBazelVersion();
if (!bazelBinary.bazelVersion().equals(workspaceBazelVersion)) {
LOG.trace("Using workspace Bazel version '{}' for command: {}", workspaceBazelVersion, command);
command.setBazelBinary(
new BazelBinary(getExecutionService().getBazelBinary().executable(), workspaceBazelVersion));
LOG.trace(
"Forcing (overriding) workspace Bazel version '{}' for command: {}",
workspaceBazelVersion,
command);
command.setBazelBinary(new BazelBinary(bazelBinary.executable(), workspaceBazelVersion));
} else {
command.setBazelBinary(bazelBinary);
}
}

Expand Down Expand Up @@ -107,28 +118,32 @@ final BazelModelCommandExecutionService getExecutionService() {
*/
public <R> R runDirectlyWithWorkspaceLock(BazelCommand<R> command, List<IResource> resourcesToRefresh,
IProgressMonitor monitor) throws CoreException {
configureWithWorkspaceBazelVersion(command, executionContext.getBazelWorkspace());
configureCommand(command, executionContext.getBazelWorkspace());
return getExecutionService().executeWithWorkspaceLock(command, executionContext, resourcesToRefresh, monitor);
}

/**
* Execute a Bazel query using
* Execute a Bazel query command using
* {@link BazelModelCommandExecutionService#executeOutsideWorkspaceLockAsync(BazelCommand, BazelElement)}.
* <p>
* The method will block and wait for the result.
* The method will block the current thread and wait for the result. However, the command execution will happen in a
* different {@link Job thread} in the background for proper progress handling/reporting.
* </p>
* <p>
* Note, the command must not modify any resources in the workspace (eg., performing a build or something).
* </p>
*
* @param <R>
* the command result type
* @param command
* the query command to execute
* the command to execute
* @return the command result (never <code>null</code>)
* @see BazelModelCommandExecutionService#executeOutsideWorkspaceLockAsync(BazelCommand, BazelElement)
* <code>executeOutsideWorkspaceLockAsync</code> for execution and locking semantics
* @throws CoreException
*/
public <R> R runQueryWithoutLock(BazelQueryCommand<R> command) throws CoreException {
configureWithWorkspaceBazelVersion(command, executionContext.getBazelWorkspace());
configureCommand(command, executionContext.getBazelWorkspace());
Future<R> future = getExecutionService().executeOutsideWorkspaceLockAsync(command, executionContext);
try {
return future.get();
Expand All @@ -144,9 +159,30 @@ public <R> R runQueryWithoutLock(BazelQueryCommand<R> command) throws CoreExcept
}
}

/**
* Execute a Bazel command using
* {@link BazelModelCommandExecutionService#executeOutsideWorkspaceLockAsync(BazelCommand, BazelElement)}.
* <p>
* The method will block the current thread and wait for the result. However, the command execution will happen in a
* different {@link Job thread} in the background for proper progress handling/reporting.
* </p>
*
* @param <R>
* the command result type
* @param command
* the command to execute
* @param rule
* the scheduling rule to apply to {@link WorkspaceJob#setRule(ISchedulingRule)}
* @param resourcesToRefresh
* list of resources to refresh recursively when the command execution is complete
* @return the command result (never <code>null</code>)
* @see BazelModelCommandExecutionService#executeWithWorkspaceLockAsync(BazelCommand, BazelElement, ISchedulingRule,
* List) <code>executeWithWorkspaceLockAsync</code> for execution and locking semantics
* @throws CoreException
*/
public <R> R runWithWorkspaceLock(BazelCommand<R> command, ISchedulingRule rule, List<IResource> resourcesToRefresh)
throws CoreException {
configureWithWorkspaceBazelVersion(command, executionContext.getBazelWorkspace());
configureCommand(command, executionContext.getBazelWorkspace());
Future<R> future = getExecutionService()
.executeWithWorkspaceLockAsync(command, executionContext, rule, resourcesToRefresh);
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public BazelClasspathManager getClasspathManager() {
/**
* @return the execution service used by the model
*/
public BazelModelCommandExecutionService getExecutionService() {
BazelModelCommandExecutionService getExecutionService() {
return executionService;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ protected BazelPackageInfo createInfo() throws CoreException {
Status.error(format("Package '%s' does not exist in workspace '%s'!", label, parent.getName())));
}

var targets = BazelPackageInfo.queryForTargets(this, getModelManager().getExecutionService());
var targets = BazelPackageInfo.queryForTargets(this, getCommandExecutor());
return new BazelPackageInfo(buildFile, this, targets);
}

Expand Down
Loading

0 comments on commit 1fed8ec

Please sign in to comment.