Skip to content

Commit

Permalink
[#6664] Add blaze mod ... runner implementation - 2/n (#6673)
Browse files Browse the repository at this point in the history
* [#6664] Add `blaze mod ...` runner implementation - 2/n

* Revert unnecessary change

* javadocs

* More comments

* Skip using asMap

* Update copyright and use project local output file

* better name + copyright years
  • Loading branch information
mtoader authored Aug 29, 2024
1 parent 9afe047 commit bc4f18b
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 21 deletions.
1 change: 1 addition & 0 deletions base/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ java_library(
"//shared:vcs",
"//third_party/auto_value",
"@error_prone_annotations//jar",
"@gson//jar"
],
)

Expand Down
2 changes: 2 additions & 0 deletions base/src/META-INF/blaze-base.xml
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@
serviceImplementation="com.google.idea.blaze.base.io.VirtualFileSystemProviderImpl"/>
<applicationService serviceInterface="com.google.idea.blaze.base.command.info.BlazeInfoRunner"
serviceImplementation="com.google.idea.blaze.base.command.info.BlazeInfoRunnerImpl"/>
<applicationService serviceInterface="com.google.idea.blaze.base.command.mod.BlazeModRunner"
serviceImplementation="com.google.idea.blaze.base.command.mod.BlazeModRunnerImpl"/>
<applicationService serviceImplementation="com.google.idea.blaze.base.model.primitives.Kind$ApplicationState"/>
<applicationService serviceInterface="com.google.idea.blaze.base.io.TempDirectoryProvider"
serviceImplementation="com.google.idea.blaze.base.io.TempDirectoryProviderImpl"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -64,11 +66,11 @@ public class CommandLineBlazeCommandRunner implements BlazeCommandRunner {

@Override
public BlazeBuildOutputs run(
Project project,
BlazeCommand.Builder blazeCommandBuilder,
BuildResultHelper buildResultHelper,
BlazeContext context,
Map<String, String> envVars) {
Project project,
BlazeCommand.Builder blazeCommandBuilder,
BuildResultHelper buildResultHelper,
BlazeContext context,
Map<String, String> envVars) {
try {
performGuardCheck(project, context);
} catch (ExecutionDeniedException e) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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<String, String> env: envVars.entrySet()) {
for (Map.Entry<String, String> env : envVars.entrySet()) {
blazeCommandBuilder.addBlazeFlags(BlazeFlags.TEST_ENV, String.format("%s=%s", env.getKey(), env.getValue()));
}

Expand Down Expand Up @@ -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<String, String> envVars, BlazeContext context) {
blazeCommandBuilder.addBlazeFlags(getExtraBuildFlags(blazeCommandBuilder));
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<ExternalWorkspaceData>
*/
@SuppressWarnings({"unused"}) // will be used shortly
public abstract ListenableFuture<ExternalWorkspaceData> dumpRepoMapping(
Project project,
BuildInvoker invoker,
BlazeContext context,
BuildSystemName buildSystemName,
List<String> 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<byte[]> runBlazeModGetBytes(
Project project,
BuildInvoker invoker,
BlazeContext context,
List<String> args,
List<String> flags);
}
Original file line number Diff line number Diff line change
@@ -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<ExternalWorkspaceData> dumpRepoMapping(
Project project,
BuildSystem.BuildInvoker invoker,
BlazeContext context,
BuildSystemName buildSystemName,
List<String> 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<ExternalWorkspace> 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<byte[]> runBlazeModGetBytes(
Project project,
BuildSystem.BuildInvoker invoker,
BlazeContext context,
List<String> args,
List<String> 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();
}
}
});
}
}
Loading

0 comments on commit bc4f18b

Please sign in to comment.