diff options
author | Han-Wen Nienhuys <hanwen@google.com> | 2015-02-25 16:45:20 +0100 |
---|---|---|
committer | Han-Wen Nienhuys <hanwen@google.com> | 2015-02-25 16:45:20 +0100 |
commit | d08b27fa9701fecfdb69e1b0d1ac2459efc2129b (patch) | |
tree | 5d50963026239ca5aebfb47ea5b8db7e814e57c8 /src/main/java/com/google/devtools/build/lib/rules/cpp/FakeCppCompileAction.java |
Update from Google.
--
MOE_MIGRATED_REVID=85702957
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/cpp/FakeCppCompileAction.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/cpp/FakeCppCompileAction.java | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FakeCppCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FakeCppCompileAction.java new file mode 100644 index 0000000000..bd15455491 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FakeCppCompileAction.java @@ -0,0 +1,212 @@ +// 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.ISO_8859_1; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +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.ActionExecutionException; +import com.google.devtools.build.lib.actions.ActionOwner; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.ExecException; +import com.google.devtools.build.lib.actions.Executor; +import com.google.devtools.build.lib.actions.ResourceSet; +import com.google.devtools.build.lib.analysis.config.BuildConfiguration; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible; +import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; +import com.google.devtools.build.lib.syntax.Label; +import com.google.devtools.build.lib.util.ShellEscaper; +import com.google.devtools.build.lib.util.io.FileOutErr; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.PathFragment; + +import java.io.IOException; +import java.util.UUID; +import java.util.logging.Logger; + +import javax.annotation.Nullable; + +/** + * Action that represents a fake C++ compilation step. + */ +@ThreadCompatible +public class FakeCppCompileAction extends CppCompileAction { + + private static final Logger LOG = Logger.getLogger(FakeCppCompileAction.class.getName()); + + public static final UUID GUID = UUID.fromString("b2d95c91-1434-47ae-a786-816017de8494"); + + private final PathFragment tempOutputFile; + + FakeCppCompileAction(ActionOwner owner, + ImmutableList<String> features, + FeatureConfiguration featureConfiguration, + Artifact sourceFile, + Label sourceLabel, + NestedSet<Artifact> mandatoryInputs, + Artifact outputFile, + PathFragment tempOutputFile, + DotdFile dotdFile, + BuildConfiguration configuration, + CppConfiguration cppConfiguration, + CppCompilationContext context, + ImmutableList<String> copts, + ImmutableList<String> pluginOpts, + Predicate<String> nocopts, + ImmutableList<PathFragment> extraSystemIncludePrefixes, + boolean enableLayeringCheck, + @Nullable String fdoBuildStamp) { + super(owner, features, featureConfiguration, sourceFile, sourceLabel, mandatoryInputs, + outputFile, dotdFile, null, null, null, + configuration, cppConfiguration, + // We only allow inclusion of header files explicitly declared in + // "srcs", so we only use declaredIncludeSrcs, not declaredIncludeDirs. + // (Disallowing use of undeclared headers for cc_fake_binary is needed + // because the header files get included in the runfiles for the + // cc_fake_binary and for the negative compilation tests that depend on + // the cc_fake_binary, and the runfiles must be determined at analysis + // time, so they can't depend on the contents of the ".d" file.) + CppCompilationContext.disallowUndeclaredHeaders(context), null, copts, pluginOpts, nocopts, + extraSystemIncludePrefixes, enableLayeringCheck, fdoBuildStamp, VOID_INCLUDE_RESOLVER, + ImmutableList.<IncludeScannable>of(), + GUID, /*compileHeaderModules=*/false); + this.tempOutputFile = Preconditions.checkNotNull(tempOutputFile); + } + + @Override + @ThreadCompatible + public void execute(ActionExecutionContext actionExecutionContext) + throws ActionExecutionException, InterruptedException { + Executor executor = actionExecutionContext.getExecutor(); + + // First, do an normal compilation, to generate the ".d" file. The generated + // object file is built to a temporary location (tempOutputFile) and ignored + // afterwards. + LOG.info("Generating " + getDotdFile()); + CppCompileActionContext context = executor.getContext(CppCompileActionContext.class); + CppCompileActionContext.Reply reply = null; + try { + // We delegate stdout/stderr to nowhere, i.e. same as redirecting to /dev/null. + reply = context.execWithReply( + this, actionExecutionContext.withFileOutErr(new FileOutErr())); + } catch (ExecException e) { + // We ignore failures here (other than capturing the Distributor reply). + // The compilation may well fail (that's the whole point of negative compilation tests). + // We execute it here just for the side effect of generating the ".d" file. + reply = context.getReplyFromException(e, this); + if (reply == null) { + // This can only happen if the ExecException does not come from remote execution. + throw e.toActionExecutionException("", executor.getVerboseFailures(), this); + } + } + IncludeScanningContext scanningContext = executor.getContext(IncludeScanningContext.class); + updateActionInputs(executor.getExecRoot(), scanningContext.getArtifactResolver(), reply); + + // Even cc_fake_binary rules need to properly declare their dependencies... + // In fact, they need to declare their dependencies even more than cc_binary rules do. + // CcCommonConfiguredTarget passes in an empty set of declaredIncludeDirs, + // so this check below will only allow inclusion of header files that are explicitly + // listed in the "srcs" of the cc_fake_binary or in the "srcs" of a cc_library that it + // depends on. + try { + validateInclusions(actionExecutionContext.getMiddlemanExpander(), executor.getEventHandler()); + } catch (ActionExecutionException e) { + // TODO(bazel-team): (2009) make this into an error, once most of the current warnings + // are fixed. + executor.getEventHandler().handle(Event.warn( + getOwner().getLocation(), + e.getMessage() + ";\n this warning may eventually become an error")); + } + + // Generate a fake ".o" file containing the command line needed to generate + // the real object file. + LOG.info("Generating " + outputFile); + + // A cc_fake_binary rule generates fake .o files and a fake target file, + // which merely contain instructions on building the real target. We need to + // be careful to use a new set of output file names in the instructions, as + // to not overwrite the fake output files when someone tries to follow the + // instructions. As the real compilation is executed by the test from its + // runfiles directory (where writing is forbidden), we patch the command + // line to write to $TEST_TMPDIR instead. + final String outputPrefix = "$TEST_TMPDIR/"; + String argv = Joiner.on(' ').join( + Iterables.transform(getArgv(outputFile.getExecPath()), new Function<String, String>() { + @Override + public String apply(String input) { + String result = ShellEscaper.escapeString(input); + if (input.equals(outputFile.getExecPathString()) + || input.equals(getDotdFile().getSafeExecPath().getPathString())) { + result = outputPrefix + result; + } + return result; + } + })); + + // Write the command needed to build the real .o file to the fake .o file. + // Generate a command to ensure that the output directory exists; otherwise + // the compilation would fail. + try { + // Ensure that the .d file and .o file are siblings, so that the "mkdir" below works for + // both. + Preconditions.checkState(outputFile.getExecPath().getParentDirectory().equals( + getDotdFile().getSafeExecPath().getParentDirectory())); + FileSystemUtils.writeContent(outputFile.getPath(), ISO_8859_1, + outputFile.getPath().getBaseName() + ": " + + "mkdir -p " + outputPrefix + "$(dirname " + outputFile.getExecPath() + ")" + + " && " + argv + "\n"); + } catch (IOException e) { + throw new ActionExecutionException("failed to create fake compile command for rule '" + + getOwner().getLabel() + ": " + e.getMessage(), + this, false); + } + } + + @Override + protected PathFragment getInternalOutputFile() { + return tempOutputFile; + } + + @Override + public String getMnemonic() { return "FakeCppCompile"; } + + @Override + public String describeStrategy(Executor executor) { + return "fake"; + } + + @Override + public ResourceSet estimateResourceConsumptionLocal() { + return new ResourceSet(/*memoryMb=*/1, /*cpuUsage=*/0.1, /*ioUsage=*/0.0); + } + + @Override + public ResourceSet estimateResourceConsumption(Executor executor) { + return executor.getContext(CppCompileActionContext.class).estimateResourceConsumption(this); + } + + @Override + protected boolean needsIncludeScanning(Executor executor) { + return false; + } +} |