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}"