Introduce hook to BazelWorkspaceStatusModule that calls external script(controled by workspace_status_command option, default to tools/buildstamp/get_workspace_status) to emit addtional workspace information to stable-status.txt. This should address #216. -- Change-Id: Iffb06482489f0d55393e27b0764e6e127fedbc20 Reviewed-on: https://bazel-review.git.corp.google.com/#/c/1550 MOS_MIGRATED_REVID=97678871
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BuildInfo.java b/src/main/java/com/google/devtools/build/lib/analysis/BuildInfo.java index 65cdbf6..2972e46 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/BuildInfo.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/BuildInfo.java
@@ -38,4 +38,14 @@ * Build time as milliseconds since epoch */ public static final String BUILD_TIMESTAMP = "BUILD_TIMESTAMP"; + + /** + * The revision of source tree reported by source control system + */ + public static final String BUILD_SCM_REVISION = "BUILD_SCM_REVISION"; + + /** + * The status of source tree reported by source control system + */ + public static final String BUILD_SCM_STATUS = "BUILD_SCM_STATUS"; }
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java index 23b6b1e..919a001 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java
@@ -34,10 +34,14 @@ import com.google.devtools.build.lib.analysis.WorkspaceStatusAction; import com.google.devtools.build.lib.analysis.WorkspaceStatusAction.Key; import com.google.devtools.build.lib.analysis.WorkspaceStatusAction.KeyType; +import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.runtime.BlazeModule; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.GotOptionsEvent; +import com.google.devtools.build.lib.shell.CommandException; +import com.google.devtools.build.lib.shell.CommandResult; +import com.google.devtools.build.lib.util.CommandBuilder; import com.google.devtools.build.lib.util.NetUtil; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; @@ -59,13 +63,16 @@ private final Artifact stableStatus; private final Artifact volatileStatus; private final AtomicReference<Options> options; - + private final String username; private final String hostname; private final long timestamp; + private final com.google.devtools.build.lib.shell.Command getWorkspaceStatusCommand; + private final PathFragment EMPTY_FRAGMENT = new PathFragment(""); private BazelWorkspaceStatusAction( AtomicReference<WorkspaceStatusAction.Options> options, + BlazeRuntime runtime, Artifact stableStatus, Artifact volatileStatus) { super(BuildInfoHelper.BUILD_INFO_ACTION_OWNER, Artifact.NO_ARTIFACTS, @@ -76,6 +83,18 @@ this.username = System.getProperty("user.name"); this.hostname = NetUtil.findShortHostName(); this.timestamp = System.currentTimeMillis(); + this.getWorkspaceStatusCommand = + options.get().workspaceStatusCommand.equals(EMPTY_FRAGMENT) + ? null + : new CommandBuilder() + .addArgs(options.get().workspaceStatusCommand.toString()) + // Pass client env, because certain SCM client(like + // perforce, git) relies on environment variables to work + // correctly. + .setEnv(runtime.getClientEnv()) + .setWorkingDir(runtime.getWorkspace()) + .useShell(true) + .build(); } @Override @@ -83,6 +102,30 @@ return ""; } + private String getAdditionalWorkspaceStatus(ActionExecutionContext actionExecutionContext) + throws ActionExecutionException { + try { + if (this.getWorkspaceStatusCommand != null) { + actionExecutionContext + .getExecutor() + .getEventHandler() + .handle( + Event.progress( + "Getting additional workspace status by running " + + options.get().workspaceStatusCommand)); + CommandResult result = this.getWorkspaceStatusCommand.execute(); + if (result.getTerminationStatus().success()) { + return new String(result.getStdout()); + } + throw new ActionExecutionException( + "workspace status command failed: " + result.getTerminationStatus(), this, true); + } + } catch (CommandException e) { + throw new ActionExecutionException(e, this, true); + } + return ""; + } + @Override public void execute(ActionExecutionContext actionExecutionContext) throws ActionExecutionException { @@ -94,12 +137,19 @@ BuildInfo.BUILD_HOST + " " + hostname, BuildInfo.BUILD_USER + " " + username); FileSystemUtils.writeContent(stableStatus.getPath(), info.getBytes(StandardCharsets.UTF_8)); - String volatileInfo = BuildInfo.BUILD_TIMESTAMP + " " + timestamp + "\n"; + String volatileInfo = + joiner.join( + BuildInfo.BUILD_TIMESTAMP + " " + timestamp, + getAdditionalWorkspaceStatus(actionExecutionContext)); FileSystemUtils.writeContent( volatileStatus.getPath(), volatileInfo.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { - throw new ActionExecutionException(e, this, true); + throw new ActionExecutionException( + "Failed to run workspace status command " + options.get().workspaceStatusCommand, + e, + this, + true); } } @@ -172,7 +222,7 @@ Artifact volatileArtifact = factory.getConstantMetadataArtifact( new PathFragment("volatile-status.txt"), root, artifactOwner); - return new BazelWorkspaceStatusAction(options, stableArtifact, volatileArtifact); + return new BazelWorkspaceStatusAction(options, runtime, stableArtifact, volatileArtifact); } } @@ -186,7 +236,11 @@ BuildInfo.BUILD_HOST, Key.of(KeyType.STRING, "hostname", "redacted"), BuildInfo.BUILD_USER, - Key.of(KeyType.STRING, "username", "redacted")); + Key.of(KeyType.STRING, "username", "redacted"), + BuildInfo.BUILD_SCM_REVISION, + Key.of(KeyType.STRING, "0", "0"), + BuildInfo.BUILD_SCM_STATUS, + Key.of(KeyType.STRING, "", "redacted")); } @Override
diff --git a/tools/buildstamp/BUILD b/tools/buildstamp/BUILD new file mode 100644 index 0000000..6ad7e59 --- /dev/null +++ b/tools/buildstamp/BUILD
@@ -0,0 +1,8 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "all", + srcs = [ + "get_workspace_status", + ], +)
diff --git a/tools/buildstamp/get_workspace_status b/tools/buildstamp/get_workspace_status new file mode 100644 index 0000000..1c358f7 --- /dev/null +++ b/tools/buildstamp/get_workspace_status
@@ -0,0 +1,29 @@ +#!/bin/bash + +# This script will be run bazel when building process starts to +# generate key-value information that represents the status of the +# workspace. The output should be like +# +# KEY1 VALUE1 +# KEY2 VALUE2 +# +# If the script exits with non-zero code, it's considered as a failure +# and the output will be discarded. + +# The code below presents an implementation that works for git repository +git_rev=$(git rev-parse HEAD) +if [[ $? != 0 ]]; +then + exit 1 +fi +echo "BUILD_SCM_REVISION ${git_rev}" + +# Check whether there are any uncommited changes +git diff-index --quiet HEAD -- +if [[ $? == 0 ]]; +then + tree_status="Clean" +else + tree_status="Modified" +fi +echo "BUILD_SCM_STATUS ${tree_status}"