Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#6664] Add blaze mod ... runner implementation - 2/n #6673

Merged
merged 8 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading