// 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 static com.google.common.collect.ImmutableList.toImmutableList; import com.google.common.collect.ImmutableList; import com.google.common.collect.Streams; import com.google.devtools.build.lib.actions.ActionExecutionContext; import com.google.devtools.build.lib.actions.ActionKeyContext; 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 publicHeaders; private final ImmutableList additionalExportedHeaders; public UmbrellaHeaderAction( ActionOwner owner, Artifact umbrellaHeader, Iterable publicHeaders, Iterable additionalExportedHeaders) { super( owner, Streams.stream(publicHeaders).filter(Artifact::isTreeArtifact).collect(toImmutableList()), 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 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 expandedHeaders(ArtifactExpander artifactExpander, Iterable unexpandedHeaders) { List 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 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 void computeKey(ActionKeyContext actionKeyContext, Fingerprint fp) { fp.addString(GUID); fp.addPath(umbrellaHeader.getExecPath()); fp.addInt(publicHeaders.size()); for (Artifact artifact : publicHeaders) { fp.addPath(artifact.getExecPath()); } fp.addInt(additionalExportedHeaders.size()); for (PathFragment path : additionalExportedHeaders) { fp.addPath(path); } } }