diff --git a/base/BUILD b/base/BUILD
index c39b2dd87a7..4f27f7343e2 100644
--- a/base/BUILD
+++ b/base/BUILD
@@ -45,6 +45,7 @@ java_library(
"//shared:vcs",
"//third_party/auto_value",
"@error_prone_annotations//jar",
+ "@gson//jar"
],
)
diff --git a/base/src/META-INF/blaze-base.xml b/base/src/META-INF/blaze-base.xml
index ee02185227b..313f805dac1 100644
--- a/base/src/META-INF/blaze-base.xml
+++ b/base/src/META-INF/blaze-base.xml
@@ -281,6 +281,8 @@
serviceImplementation="com.google.idea.blaze.base.io.VirtualFileSystemProviderImpl"/>
+
diff --git a/base/src/com/google/idea/blaze/base/command/BlazeCommandName.java b/base/src/com/google/idea/blaze/base/command/BlazeCommandName.java
index 4e340993c56..2c8d3e84ccf 100644
--- a/base/src/com/google/idea/blaze/base/command/BlazeCommandName.java
+++ b/base/src/com/google/idea/blaze/base/command/BlazeCommandName.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 The Bazel Authors. All rights reserved.
+ * Copyright 2024 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.
@@ -41,6 +41,7 @@ public final class BlazeCommandName {
public static final BlazeCommandName INFO = fromString("info");
public static final BlazeCommandName MOBILE_INSTALL = fromString("mobile-install");
public static final BlazeCommandName COVERAGE = fromString("coverage");
+ public static final BlazeCommandName MOD = fromString("mod");
public static BlazeCommandName fromString(String name) {
knownCommands.putIfAbsent(name, new BlazeCommandName(name));
diff --git a/base/src/com/google/idea/blaze/base/command/BlazeCommandRunner.java b/base/src/com/google/idea/blaze/base/command/BlazeCommandRunner.java
index 3122e52bc3c..3ccd419208e 100644
--- a/base/src/com/google/idea/blaze/base/command/BlazeCommandRunner.java
+++ b/base/src/com/google/idea/blaze/base/command/BlazeCommandRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Bazel Authors. All rights reserved.
+ * Copyright 2024 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.
@@ -77,6 +77,14 @@ InputStream runBlazeInfo(
BlazeContext context)
throws BuildException;
+ @MustBeClosed
+ InputStream runBlazeMod(
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context)
+ throws BuildException;
+
/** Allows enabling the use of command runner for restricted set of users. */
default boolean canUseCli() {
return true;
diff --git a/base/src/com/google/idea/blaze/base/command/CommandLineBlazeCommandRunner.java b/base/src/com/google/idea/blaze/base/command/CommandLineBlazeCommandRunner.java
index 84d54e599e0..d689fafd8cd 100644
--- a/base/src/com/google/idea/blaze/base/command/CommandLineBlazeCommandRunner.java
+++ b/base/src/com/google/idea/blaze/base/command/CommandLineBlazeCommandRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Bazel Authors. All rights reserved.
+ * Copyright 2024 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.
@@ -27,6 +27,7 @@
import com.google.idea.blaze.base.command.buildresult.BuildResultHelper.GetArtifactsException;
import com.google.idea.blaze.base.command.buildresult.BuildResultHelperBep;
import com.google.idea.blaze.base.command.buildresult.ParsedBepOutput;
+import com.google.idea.blaze.base.command.mod.BlazeModException;
import com.google.idea.blaze.base.console.BlazeConsoleLineProcessorProvider;
import com.google.idea.blaze.base.execution.BazelGuard;
import com.google.idea.blaze.base.execution.ExecutionDeniedException;
@@ -54,6 +55,7 @@
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Optional;
@@ -64,11 +66,11 @@ public class CommandLineBlazeCommandRunner implements BlazeCommandRunner {
@Override
public BlazeBuildOutputs run(
- Project project,
- BlazeCommand.Builder blazeCommandBuilder,
- BuildResultHelper buildResultHelper,
- BlazeContext context,
- Map envVars) {
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context,
+ Map envVars) {
try {
performGuardCheck(project, context);
} catch (ExecutionDeniedException e) {
@@ -99,7 +101,7 @@ public BlazeBuildOutputs run(
ParsedBepOutput buildOutput = buildResultHelper.getBuildOutput(stringInterner);
context.output(SummaryOutput.output(SummaryOutput.Prefix.TIMESTAMP, "Handling parsed BEP outputs..."));
BlazeBuildOutputs blazeBuildOutputs = BlazeBuildOutputs.fromParsedBepOutput(
- buildResult, buildOutput);
+ buildResult, buildOutput);
context.output(SummaryOutput.output(SummaryOutput.Prefix.TIMESTAMP, "BEP outputs have been processed."));
return blazeBuildOutputs;
} catch (GetArtifactsException e) {
@@ -123,7 +125,7 @@ public BlazeTestResults runTest(
}
// For tests, we have to pass the environment variables as `--test_env`, otherwise they don't get forwarded
- for (Map.Entry env: envVars.entrySet()) {
+ for (Map.Entry env : envVars.entrySet()) {
blazeCommandBuilder.addBlazeFlags(BlazeFlags.TEST_ENV, String.format("%s=%s", env.getKey(), env.getValue()));
}
@@ -224,6 +226,45 @@ public InputStream runBlazeInfo(
}
}
+ @Override
+ @MustBeClosed
+ public InputStream runBlazeMod(
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context)
+ throws BuildException {
+ performGuardCheckAsBuildException(project, context);
+
+ if (project.getBasePath() == null) {
+ throw new BlazeModException("Project base path is null");
+ }
+
+ try (Closer closer = Closer.create()) {
+ Path queriesDir = Files.createDirectories(Paths.get(project.getBasePath()).resolve("queries"));
+ Path tmpFile = Files.createTempFile(queriesDir, "blaze-mod-", ".stdout");
+
+ OutputStream stdout = closer.register(Files.newOutputStream(tmpFile));
+ OutputStream stderr = closer.register(
+ LineProcessingOutputStream.of(
+ new PrintOutputLineProcessor(context)));
+ int exitCode =
+ ExternalTask.builder(WorkspaceRoot.fromProject(project))
+ .addBlazeCommand(blazeCommandBuilder.build())
+ .context(context)
+ .stdout(stdout)
+ .stderr(stderr)
+ .ignoreExitCode(true)
+ .build()
+ .run();
+ BazelExitCodeException.throwIfFailed(blazeCommandBuilder, exitCode);
+ return new BufferedInputStream(
+ Files.newInputStream(tmpFile, StandardOpenOption.DELETE_ON_CLOSE));
+ } catch (IOException e) {
+ throw new BlazeModException("io error while running blaze mod", e);
+ }
+ }
+
private BuildResult issueBuild(
BlazeCommand.Builder blazeCommandBuilder, WorkspaceRoot workspaceRoot, Map envVars, BlazeContext context) {
blazeCommandBuilder.addBlazeFlags(getExtraBuildFlags(blazeCommandBuilder));
diff --git a/base/src/com/google/idea/blaze/base/command/mod/BlazeModException.java b/base/src/com/google/idea/blaze/base/command/mod/BlazeModException.java
new file mode 100644
index 00000000000..8c1f565fd33
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/command/mod/BlazeModException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.command.mod;
+
+import com.google.idea.blaze.exception.BuildException;
+
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+public final class BlazeModException extends BuildException {
+ public BlazeModException(String message) {
+ super(message);
+ }
+
+ public BlazeModException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/command/mod/BlazeModRunner.java b/base/src/com/google/idea/blaze/base/command/mod/BlazeModRunner.java
new file mode 100644
index 00000000000..cf3a27f8cb5
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/command/mod/BlazeModRunner.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.command.mod;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.idea.blaze.base.bazel.BuildSystem.BuildInvoker;
+import com.google.idea.blaze.base.model.ExternalWorkspaceData;
+import com.google.idea.blaze.base.scope.BlazeContext;
+import com.google.idea.blaze.base.settings.BuildSystemName;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+
+import java.util.List;
+
+/** Runs the {@code blaze mod ...} command. The results may be cached in the workspace. */
+public abstract class BlazeModRunner {
+
+ public static BlazeModRunner getInstance() {
+ return ApplicationManager.getApplication().getService(BlazeModRunner.class);
+ }
+
+ /**
+ * This calls {@code blaze mod dump_repo_mapping workspace} so blaze mod will return all mapped
+ * repos visible to the current workspace.
+ *
+ * @param flags The blaze flags that will be passed to Blaze.
+ * @return a ListenableFuture
+ */
+ @SuppressWarnings({"unused"}) // will be used shortly
+ public abstract ListenableFuture dumpRepoMapping(
+ Project project,
+ BuildInvoker invoker,
+ BlazeContext context,
+ BuildSystemName buildSystemName,
+ List flags);
+
+ /**
+ * @param args The arguments passed into `blaze mod ...`
+ * @param flags The blaze flags that will be passed to {@code blaze ...}
+ * @return the stdout bytes of the command
+ */
+ protected abstract ListenableFuture runBlazeModGetBytes(
+ Project project,
+ BuildInvoker invoker,
+ BlazeContext context,
+ List args,
+ List flags);
+}
diff --git a/base/src/com/google/idea/blaze/base/command/mod/BlazeModRunnerImpl.java b/base/src/com/google/idea/blaze/base/command/mod/BlazeModRunnerImpl.java
new file mode 100644
index 00000000000..12622f271ef
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/command/mod/BlazeModRunnerImpl.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2024 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.command.mod;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.idea.blaze.base.async.executor.BlazeExecutor;
+import com.google.idea.blaze.base.bazel.BuildSystem;
+import com.google.idea.blaze.base.command.BlazeCommand;
+import com.google.idea.blaze.base.command.BlazeCommandName;
+import com.google.idea.blaze.base.command.BlazeCommandRunner;
+import com.google.idea.blaze.base.command.buildresult.BuildResultHelper;
+import com.google.idea.blaze.base.model.ExternalWorkspaceData;
+import com.google.idea.blaze.base.model.primitives.ExternalWorkspace;
+import com.google.idea.blaze.base.scope.BlazeContext;
+import com.google.idea.blaze.base.settings.BuildSystemName;
+import com.intellij.openapi.project.Project;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+public class BlazeModRunnerImpl extends BlazeModRunner {
+
+ @Override
+ public ListenableFuture dumpRepoMapping(
+ Project project,
+ BuildSystem.BuildInvoker invoker,
+ BlazeContext context,
+ BuildSystemName buildSystemName,
+ List flags) {
+ return Futures.transform(
+ runBlazeModGetBytes(project, invoker, context, ImmutableList.of( "dump_repo_mapping", "workspace"), flags),
+ bytes -> {
+ JsonObject json = JsonParser.parseString(new String(bytes, StandardCharsets.UTF_8).trim()).getAsJsonObject();
+
+ ImmutableList externalWorkspaces =
+ json.entrySet().stream()
+ .filter(e -> e.getValue().isJsonPrimitive())
+ .filter(e -> !e.getValue().getAsString().trim().isEmpty())
+ .map(e -> ExternalWorkspace.create(e.getValue().getAsString(), e.getKey()))
+ .collect(ImmutableList.toImmutableList());
+
+ return ExternalWorkspaceData.create(externalWorkspaces);
+ },
+ BlazeExecutor.getInstance().getExecutor());
+ }
+
+ @Override
+ public ListenableFuture runBlazeModGetBytes(
+ Project project,
+ BuildSystem.BuildInvoker invoker,
+ BlazeContext context,
+ List args,
+ List flags) {
+ return BlazeExecutor.getInstance()
+ .submit(() -> {
+ BlazeCommand.Builder builder =
+ BlazeCommand.builder(invoker, BlazeCommandName.MOD)
+ .addBlazeFlags(flags);
+
+ if (args != null) {
+ builder.addBlazeFlags(args);
+ }
+
+ try (BuildResultHelper buildResultHelper = invoker.createBuildResultHelper()) {
+ BlazeCommandRunner runner = invoker.getCommandRunner();
+ try (InputStream stream = runner.runBlazeMod(project, builder, buildResultHelper, context)) {
+ return stream.readAllBytes();
+ }
+ }
+ });
+ }
+}
diff --git a/base/tests/utils/unit/com/google/idea/blaze/base/bazel/FakeBlazeCommandRunner.java b/base/tests/utils/unit/com/google/idea/blaze/base/bazel/FakeBlazeCommandRunner.java
index 30ba51cc6ca..41157d0fb2f 100644
--- a/base/tests/utils/unit/com/google/idea/blaze/base/bazel/FakeBlazeCommandRunner.java
+++ b/base/tests/utils/unit/com/google/idea/blaze/base/bazel/FakeBlazeCommandRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Bazel Authors. All rights reserved.
+ * Copyright 2024 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.
@@ -21,6 +21,7 @@
import com.google.idea.blaze.base.command.buildresult.BuildResultHelper;
import com.google.idea.blaze.base.command.buildresult.BuildResultHelper.GetArtifactsException;
import com.google.idea.blaze.base.command.info.BlazeInfoException;
+import com.google.idea.blaze.base.command.mod.BlazeModException;
import com.google.idea.blaze.base.logging.utils.querysync.BuildDepsStatsScope;
import com.google.idea.blaze.base.logging.utils.querysync.SyncQueryStatsScope;
import com.google.idea.blaze.base.run.testlogs.BlazeTestResults;
@@ -29,6 +30,7 @@
import com.google.idea.blaze.base.sync.aspects.BuildResult;
import com.google.idea.blaze.exception.BuildException;
import com.intellij.openapi.project.Project;
+
import java.io.InputStream;
import java.util.Map;
@@ -59,11 +61,11 @@ public FakeBlazeCommandRunner(BuildFunction buildFunction) {
@Override
public BlazeBuildOutputs run(
- Project project,
- BlazeCommand.Builder blazeCommandBuilder,
- BuildResultHelper buildResultHelper,
- BlazeContext context,
- Map envVars)
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context,
+ Map envVars)
throws BuildException {
command = blazeCommandBuilder.build();
try {
@@ -78,11 +80,11 @@ public BlazeBuildOutputs run(
@Override
public BlazeTestResults runTest(
- Project project,
- BlazeCommand.Builder blazeCommandBuilder,
- BuildResultHelper buildResultHelper,
- BlazeContext context,
- Map envVars) {
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context,
+ Map envVars) {
return BlazeTestResults.NO_RESULTS;
}
@@ -108,6 +110,17 @@ public InputStream runBlazeInfo(
return InputStream.nullInputStream();
}
+ @Override
+ @MustBeClosed
+ public InputStream runBlazeMod(
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context)
+ throws BlazeModException {
+ return InputStream.nullInputStream();
+ }
+
public BlazeCommand getIssuedCommand() {
return command;
}