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..bdc18dce2ab 100644
--- a/base/src/com/google/idea/blaze/base/command/BlazeCommandName.java
+++ b/base/src/com/google/idea/blaze/base/command/BlazeCommandName.java
@@ -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..ebe3ffb2c69 100644
--- a/base/src/com/google/idea/blaze/base/command/BlazeCommandRunner.java
+++ b/base/src/com/google/idea/blaze/base/command/BlazeCommandRunner.java
@@ -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..11a4c7043ea 100644
--- a/base/src/com/google/idea/blaze/base/command/CommandLineBlazeCommandRunner.java
+++ b/base/src/com/google/idea/blaze/base/command/CommandLineBlazeCommandRunner.java
@@ -224,6 +224,41 @@ public InputStream runBlazeInfo(
}
}
+ @Override
+ @MustBeClosed
+ public InputStream runBlazeMod(
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context)
+ throws BuildException {
+ performGuardCheckAsBuildException(project, context);
+
+ try (Closer closer = Closer.create()) {
+ Path tmpFile =
+ Files.createTempFile(
+ String.format("intellij-bazel-%s-", blazeCommandBuilder.build().getName()),
+ ".stdout");
+ OutputStream out = 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(out)
+ .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 BuildException(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..9d097381af5
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/command/mod/BlazeModException.java
@@ -0,0 +1,16 @@
+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..0f2ceccc595
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/command/mod/BlazeModRunner.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.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 blaze info 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 blaze info without any specific key so blaze info will return all keys and values
+ * that it has.
+ *
+ * @param blazeFlags The blaze flags that will be passed to Blaze.
+ * @return The blaze info data fields.
+ */
+ public abstract ListenableFuture dumpRepoMapping(
+ Project project,
+ BuildInvoker invoker,
+ BlazeContext context,
+ BuildSystemName buildSystemName,
+ List blazeFlags);
+
+ /**
+ * @param modArgs The arguments passed into `blaze mod ...`
+ * @param blazeFlags The blaze flags that will be passed to Blaze.
+ * @return The blaze info value associated with the specified key
+ */
+ protected abstract ListenableFuture runBlazeModGetBytes(
+ Project project,
+ BuildInvoker invoker,
+ BlazeContext context,
+ List modArgs,
+ List blazeFlags);
+}
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..c21cbaf873e
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/command/mod/BlazeModRunnerImpl.java
@@ -0,0 +1,76 @@
+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 blazeFlags) {
+ return Futures.transform(
+ runBlazeModGetBytes(project, invoker, context, ImmutableList.of( "dump_repo_mapping", "workspace"), blazeFlags),
+ bytes -> {
+ JsonObject json = JsonParser.parseString(new String(bytes, StandardCharsets.UTF_8).trim()).getAsJsonObject();
+
+ ImmutableList externalWorkspaces =
+ json
+ .asMap().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 modArgs,
+ List blazeFlags) {
+ return BlazeExecutor.getInstance()
+ .submit(() -> {
+ BlazeCommand.Builder builder =
+ BlazeCommand.builder(invoker, BlazeCommandName.MOD)
+ .addBlazeFlags(blazeFlags);
+
+ if (modArgs != null) {
+ builder.addBlazeFlags(modArgs);
+ }
+
+ 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..e97cb817c96 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
@@ -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;
@@ -38,77 +40,88 @@
*/
public class FakeBlazeCommandRunner implements BlazeCommandRunner {
- @FunctionalInterface
- public interface BuildFunction {
- BlazeBuildOutputs runBuild(BuildResultHelper buildResultHelper) throws BuildException;
- }
+ @FunctionalInterface
+ public interface BuildFunction {
+ BlazeBuildOutputs runBuild(BuildResultHelper buildResultHelper) throws BuildException;
+ }
- private final BuildFunction resultsFunction;
- private BlazeCommand command;
+ private final BuildFunction resultsFunction;
+ private BlazeCommand command;
- public FakeBlazeCommandRunner() {
- this(
- buildResultHelper ->
- BlazeBuildOutputs.fromParsedBepOutput(
- BuildResult.SUCCESS, buildResultHelper.getBuildOutput()));
- }
+ public FakeBlazeCommandRunner() {
+ this(
+ buildResultHelper ->
+ BlazeBuildOutputs.fromParsedBepOutput(
+ BuildResult.SUCCESS, buildResultHelper.getBuildOutput()));
+ }
+
+ public FakeBlazeCommandRunner(BuildFunction buildFunction) {
+ this.resultsFunction = buildFunction;
+ }
- public FakeBlazeCommandRunner(BuildFunction buildFunction) {
- this.resultsFunction = buildFunction;
- }
+ @Override
+ public BlazeBuildOutputs run(
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context,
+ Map envVars)
+ throws BuildException {
+ command = blazeCommandBuilder.build();
+ try {
+ BlazeBuildOutputs blazeBuildOutputs = resultsFunction.runBuild(buildResultHelper);
+ int exitCode = blazeBuildOutputs.buildResult.exitCode;
+ BuildDepsStatsScope.fromContext(context).ifPresent(stats -> stats.setBazelExitCode(exitCode));
+ return blazeBuildOutputs;
+ } catch (GetArtifactsException e) {
+ return BlazeBuildOutputs.noOutputs(BuildResult.FATAL_ERROR);
+ }
+ }
- @Override
- public BlazeBuildOutputs run(
- Project project,
- BlazeCommand.Builder blazeCommandBuilder,
- BuildResultHelper buildResultHelper,
- BlazeContext context,
- Map envVars)
- throws BuildException {
- command = blazeCommandBuilder.build();
- try {
- BlazeBuildOutputs blazeBuildOutputs = resultsFunction.runBuild(buildResultHelper);
- int exitCode = blazeBuildOutputs.buildResult.exitCode;
- BuildDepsStatsScope.fromContext(context).ifPresent(stats -> stats.setBazelExitCode(exitCode));
- return blazeBuildOutputs;
- } catch (GetArtifactsException e) {
- return BlazeBuildOutputs.noOutputs(BuildResult.FATAL_ERROR);
+ @Override
+ public BlazeTestResults runTest(
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context,
+ Map envVars) {
+ return BlazeTestResults.NO_RESULTS;
}
- }
- @Override
- public BlazeTestResults runTest(
- Project project,
- BlazeCommand.Builder blazeCommandBuilder,
- BuildResultHelper buildResultHelper,
- BlazeContext context,
- Map envVars) {
- return BlazeTestResults.NO_RESULTS;
- }
+ @Override
+ public InputStream runQuery(
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context)
+ throws BuildException {
+ SyncQueryStatsScope.fromContext(context).ifPresent(stats -> stats.setBazelExitCode(0));
+ return InputStream.nullInputStream();
+ }
- @Override
- public InputStream runQuery(
- Project project,
- BlazeCommand.Builder blazeCommandBuilder,
- BuildResultHelper buildResultHelper,
- BlazeContext context)
- throws BuildException {
- SyncQueryStatsScope.fromContext(context).ifPresent(stats -> stats.setBazelExitCode(0));
- return InputStream.nullInputStream();
- }
+ @Override
+ @MustBeClosed
+ public InputStream runBlazeInfo(
+ Project project,
+ BlazeCommand.Builder blazeCommandBuilder,
+ BuildResultHelper buildResultHelper,
+ BlazeContext context)
+ throws BlazeInfoException {
+ return InputStream.nullInputStream();
+ }
- @Override
- @MustBeClosed
- public InputStream runBlazeInfo(
- Project project,
- BlazeCommand.Builder blazeCommandBuilder,
- BuildResultHelper buildResultHelper,
- BlazeContext context)
- throws BlazeInfoException {
- 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;
- }
+ public BlazeCommand getIssuedCommand() {
+ return command;
+ }
}