aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/cpp/WriteBuildInfoHeaderAction.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/cpp/WriteBuildInfoHeaderAction.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/WriteBuildInfoHeaderAction.java194
1 files changed, 194 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/WriteBuildInfoHeaderAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/WriteBuildInfoHeaderAction.java
new file mode 100644
index 0000000000..58b33309b6
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/WriteBuildInfoHeaderAction.java
@@ -0,0 +1,194 @@
+// Copyright 2014 Google Inc. 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.devtools.build.lib.rules.cpp;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Executor;
+import com.google.devtools.build.lib.analysis.BuildInfoHelper;
+import com.google.devtools.build.lib.analysis.WorkspaceStatusAction;
+import com.google.devtools.build.lib.analysis.actions.AbstractFileWriteAction;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.util.Fingerprint;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * An action that creates a C++ header containing the build information in the
+ * form of #define directives.
+ */
+public final class WriteBuildInfoHeaderAction extends AbstractFileWriteAction {
+ private static final String GUID = "b0798174-1352-4a54-854a-9785aaea491b";
+
+ private final ImmutableList<Artifact> valueArtifacts;
+
+ private final boolean writeVolatileInfo;
+ private final boolean writeStableInfo;
+
+ /**
+ * Creates an action that writes a C++ header with the build information.
+ *
+ * <p>It reads the set of build info keys from an action context that is usually contributed
+ * to Bazel by the workspace status module, and the value associated with said keys from the
+ * workspace status files (stable and volatile) written by the workspace status action.
+ *
+ * <p>Without input artifacts this action uses redacted build information.
+ * @param inputs Artifacts that contain build information, or an empty
+ * collection to use redacted build information
+ * @param output the C++ header Artifact created by this action
+ * @param writeVolatileInfo whether to write the volatile part of the build
+ * information to the generated header
+ * @param writeStableInfo whether to write the non-volatile part of the
+ * build information to the generated header
+ */
+ public WriteBuildInfoHeaderAction(Collection<Artifact> inputs,
+ Artifact output, boolean writeVolatileInfo, boolean writeStableInfo) {
+ super(BuildInfoHelper.BUILD_INFO_ACTION_OWNER,
+ inputs, output, /*makeExecutable=*/false);
+ valueArtifacts = ImmutableList.copyOf(inputs);
+ if (!inputs.isEmpty()) {
+ // With non-empty inputs we should not generate both volatile and non-volatile data
+ // in the same header file.
+ Preconditions.checkState(writeVolatileInfo ^ writeStableInfo);
+ }
+ Preconditions.checkState(
+ output.isConstantMetadata() == (writeVolatileInfo && !inputs.isEmpty()));
+
+ this.writeVolatileInfo = writeVolatileInfo;
+ this.writeStableInfo = writeStableInfo;
+ }
+
+ @Override
+ public DeterministicWriter newDeterministicWriter(EventHandler eventHandler, Executor executor)
+ throws IOException {
+ WorkspaceStatusAction.Context context =
+ executor.getContext(WorkspaceStatusAction.Context.class);
+
+ final Map<String, WorkspaceStatusAction.Key> keys = new LinkedHashMap<>();
+ if (writeVolatileInfo) {
+ keys.putAll(context.getVolatileKeys());
+ }
+
+ if (writeStableInfo) {
+ keys.putAll(context.getStableKeys());
+ }
+
+ final Map<String, String> values = new LinkedHashMap<>();
+ for (Artifact valueFile : valueArtifacts) {
+ values.putAll(WorkspaceStatusAction.parseValues(valueFile.getPath()));
+ }
+
+ final boolean redacted = valueArtifacts.isEmpty();
+
+ return new DeterministicWriter() {
+ @Override
+ public void writeOutputFile(OutputStream out) throws IOException {
+ Writer writer = new OutputStreamWriter(out, UTF_8);
+
+ for (Map.Entry<String, WorkspaceStatusAction.Key> key : keys.entrySet()) {
+ if (!key.getValue().isInLanguage("C++")) {
+ continue;
+ }
+
+ String value = redacted ? key.getValue().getRedactedValue()
+ : values.containsKey(key.getKey()) ? values.get(key.getKey())
+ : key.getValue().getDefaultValue();
+
+ switch (key.getValue().getType()) {
+ case VERBATIM:
+ case INTEGER:
+ break;
+
+ case STRING:
+ value = quote(value);
+ break;
+
+ default:
+ throw new IllegalStateException();
+ }
+ define(writer, key.getKey(), value);
+
+ }
+ writer.flush();
+ }
+ };
+ }
+
+ @Override
+ protected String computeKey() {
+ Fingerprint f = new Fingerprint();
+ f.addString(GUID);
+ f.addBoolean(writeStableInfo);
+ f.addBoolean(writeVolatileInfo);
+ return f.hexDigestAndReset();
+ }
+
+ @Override
+ public boolean executeUnconditionally() {
+ // Note: isVolatile must return true if executeUnconditionally can ever return true
+ // for this instance.
+ return isUnconditional();
+ }
+
+ @Override
+ public boolean isVolatile() {
+ return isUnconditional();
+ }
+
+ private boolean isUnconditional() {
+ // Because of special handling in the MetadataHandler, changed volatile build
+ // information does not trigger relinking of all libraries that have
+ // linkstamps. But we do want to regenerate the header in case libraries are
+ // relinked because of other reasons.
+ // Without inputs the contents of the header do not change, so there is no
+ // point in executing the action again in that case.
+ return writeVolatileInfo && !Iterables.isEmpty(getInputs());
+ }
+
+ /**
+ * Quote a string with double quotes.
+ */
+ private String quote(String string) {
+ // TODO(bazel-team): This is doesn't really work if the string contains quotes. Or a newline.
+ // Or a backslash. Or anything unusual, really.
+ return "\"" + string + "\"";
+ }
+
+ /**
+ * Write a preprocessor define directive to a Writer.
+ */
+ private void define(Writer writer, String name, String value) throws IOException {
+ writer.write("#define ");
+ writer.write(name);
+ writer.write(' ');
+ writer.write(value);
+ writer.write('\n');
+ }
+
+ @Override
+ protected String getRawProgressMessage() {
+ return null;
+ }
+}