// Copyright 2017 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.devtools.build.lib.rules.cpp; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.ActionExecutionContext; import com.google.devtools.build.lib.actions.ActionOwner; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander; import com.google.devtools.build.lib.analysis.actions.AbstractFileWriteAction; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.util.Fingerprint; import com.google.devtools.build.lib.vfs.PathFragment; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashSet; import java.util.List; /** * Action for generating an umbrella header. All the headers are #included in the umbrella header. */ @Immutable public final class UmbrellaHeaderAction extends AbstractFileWriteAction { private static final String GUID = "62ea2952-bf28-92c3-efb1-e34621646910"; // NOTE: If you add a field here, you'll likely need to add it to the cache key in computeKey(). private final Artifact umbrellaHeader; private final ImmutableList<Artifact> publicHeaders; private final ImmutableList<PathFragment> additionalExportedHeaders; public UmbrellaHeaderAction( ActionOwner owner, Artifact umbrellaHeader, Iterable<Artifact> publicHeaders, Iterable<PathFragment> additionalExportedHeaders) { super( owner, ImmutableList.copyOf(Iterables.filter(publicHeaders, Artifact.IS_TREE_ARTIFACT)), umbrellaHeader, /*makeExecutable=*/false); this.umbrellaHeader = umbrellaHeader; this.publicHeaders = ImmutableList.copyOf(publicHeaders); this.additionalExportedHeaders = ImmutableList.copyOf(additionalExportedHeaders); } @Override public DeterministicWriter newDeterministicWriter(ActionExecutionContext ctx) { final ArtifactExpander artifactExpander = ctx.getArtifactExpander(); return new DeterministicWriter() { @Override public void writeOutputFile(OutputStream out) throws IOException { StringBuilder content = new StringBuilder(); HashSet<PathFragment> deduper = new HashSet<>(); for (Artifact artifact : expandedHeaders(artifactExpander, publicHeaders)) { appendHeader(content, artifact.getExecPath(), deduper); } for (PathFragment additionalExportedHeader : additionalExportedHeaders) { appendHeader(content, additionalExportedHeader, deduper); } out.write(content.toString().getBytes(StandardCharsets.ISO_8859_1)); } }; } private static Iterable<Artifact> expandedHeaders(ArtifactExpander artifactExpander, Iterable<Artifact> unexpandedHeaders) { List<Artifact> expandedHeaders = new ArrayList<>(); for (Artifact unexpandedHeader : unexpandedHeaders) { if (unexpandedHeader.isTreeArtifact()) { artifactExpander.expand(unexpandedHeader, expandedHeaders); } else { expandedHeaders.add(unexpandedHeader); } } return ImmutableList.copyOf(expandedHeaders); } private void appendHeader(StringBuilder content, PathFragment path, HashSet<PathFragment> deduper) { if (deduper.contains(path)) { return; } deduper.add(path); // #include headers. The #import directive is incompatible with J2ObjC segmented headers. content.append("#include \"").append(path).append("\""); content.append("\n"); } @Override public String getMnemonic() { return "UmbrellaHeader"; } @Override protected String computeKey() { Fingerprint f = new Fingerprint(); f.addString(GUID); f.addPath(umbrellaHeader.getExecPath()); f.addInt(publicHeaders.size()); for (Artifact artifact : publicHeaders) { f.addPath(artifact.getExecPath()); } f.addInt(additionalExportedHeaders.size()); for (PathFragment path : additionalExportedHeaders) { f.addPath(path); } return f.hexDigestAndReset(); } }