Parse /showIncludes output for MSVC compiler Instead of parsing .d file generated by wrapper script, we directly parse the output of /showIncludes option. Change-Id: Id94e20a5cb05a494a793fd6a43756d44d27cea8a PiperOrigin-RevId: 154161939
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java index 2593881..8a59e04 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
@@ -1139,6 +1139,13 @@ Executor executor = actionExecutionContext.getExecutor(); CppCompileActionContext.Reply reply; + ShowIncludesFilter showIncludesFilter = null; + // If parse_showincludes feature is enabled, instead of parsing dotD file we parse the output of + // cl.exe caused by /showIncludes option. + if (featureConfiguration.isEnabled(CppRuleClasses.PARSE_SHOWINCLUDES)) { + showIncludesFilter = new ShowIncludesFilter(getSourceFile().getFilename()); + actionExecutionContext.getFileOutErr().setOutputFilter(showIncludesFilter); + } try { reply = executor.getContext(actionContext).execWithReply(this, actionExecutionContext); } catch (ExecException e) { @@ -1151,8 +1158,15 @@ IncludeScanningContext scanningContext = executor.getContext(IncludeScanningContext.class); Path execRoot = executor.getExecRoot(); - NestedSet<Artifact> discoveredInputs = - discoverInputsFromDotdFiles(execRoot, scanningContext.getArtifactResolver(), reply); + NestedSet<Artifact> discoveredInputs; + if (showIncludesFilter != null) { + discoveredInputs = + discoverInputsFromShowIncludesFilter( + execRoot, scanningContext.getArtifactResolver(), showIncludesFilter); + } else { + discoveredInputs = + discoverInputsFromDotdFiles(execRoot, scanningContext.getArtifactResolver(), reply); + } reply = null; // Clear in-memory .d files early. // Post-execute "include scanning", which modifies the action inputs to match what the compile @@ -1171,6 +1185,29 @@ } @VisibleForTesting + public NestedSet<Artifact> discoverInputsFromShowIncludesFilter( + Path execRoot, ArtifactResolver artifactResolver, ShowIncludesFilter showIncludesFilter) + throws ActionExecutionException { + if (!cppSemantics.needsDotdInputPruning()) { + return NestedSetBuilder.emptySet(Order.STABLE_ORDER); + } + HeaderDiscovery.Builder discoveryBuilder = + new HeaderDiscovery.Builder() + .setAction(this) + .setSourceFile(getSourceFile()) + .setSpecialInputsHandler(specialInputsHandler) + .setDependencies(showIncludesFilter.getDependencies(execRoot)) + .setPermittedSystemIncludePrefixes(getPermittedSystemIncludePrefixes(execRoot)) + .setAllowedDerivedinputsMap(getAllowedDerivedInputsMap()); + + if (cppSemantics.needsIncludeValidation()) { + discoveryBuilder.shouldValidateInclusions(); + } + + return discoveryBuilder.build().discoverInputsFromDependencies(execRoot, artifactResolver); + } + + @VisibleForTesting public NestedSet<Artifact> discoverInputsFromDotdFiles( Path execRoot, ArtifactResolver artifactResolver, Reply reply) throws ActionExecutionException { @@ -1180,10 +1217,9 @@ HeaderDiscovery.Builder discoveryBuilder = new HeaderDiscovery.Builder() .setAction(this) - .setDotdFile(getDotdFile()) .setSourceFile(getSourceFile()) .setSpecialInputsHandler(specialInputsHandler) - .setDependencySet(processDepset(execRoot, reply)) + .setDependencies(processDepset(execRoot, reply).getDependencies()) .setPermittedSystemIncludePrefixes(getPermittedSystemIncludePrefixes(execRoot)) .setAllowedDerivedinputsMap(getAllowedDerivedInputsMap()); @@ -1191,7 +1227,7 @@ discoveryBuilder.shouldValidateInclusions(); } - return discoveryBuilder.build().discoverInputsFromDotdFiles(execRoot, artifactResolver); + return discoveryBuilder.build().discoverInputsFromDependencies(execRoot, artifactResolver); } public DependencySet processDepset(Path execRoot, Reply reply) throws ActionExecutionException { @@ -1331,8 +1367,7 @@ CcToolchainFeatures.Variables variables, String actionName) { this.sourceFile = Preconditions.checkNotNull(sourceFile); - this.dotdFile = CppFileTypes.mustProduceDotdFile(sourceFile) - ? Preconditions.checkNotNull(dotdFile) : null; + this.dotdFile = isGenerateDotdFile(sourceFile) ? Preconditions.checkNotNull(dotdFile) : null; this.copts = Preconditions.checkNotNull(copts); this.coptsFilter = coptsFilter; this.features = Preconditions.checkNotNull(features); @@ -1340,6 +1375,12 @@ this.actionName = actionName; } + /** Returns true if Dotd file should be generated. */ + private boolean isGenerateDotdFile(Artifact sourceArtifact) { + return CppFileTypes.headerDiscoveryRequired(sourceArtifact) + && !featureConfiguration.isEnabled(CppRuleClasses.PARSE_SHOWINCLUDES); + } + /** * Returns the environment variables that should be set for C++ compile actions. */
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java index eb0de2c..597ebed 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java
@@ -185,7 +185,7 @@ } }; - public static final boolean mustProduceDotdFile(Artifact source) { + public static final boolean headerDiscoveryRequired(Artifact source) { // Sources from TreeArtifacts and TreeFileArtifacts will not generate dotd file. if (source.isTreeArtifact() || source.hasParent()) { return false;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java index c3702db..1495309 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java
@@ -409,8 +409,8 @@ buildVariables.addStringVariable("output_object_file", realOutputFilePath); } - DotdFile dotdFile = CppFileTypes.mustProduceDotdFile(sourceFile) - ? Preconditions.checkNotNull(builder.getDotdFile()) : null; + DotdFile dotdFile = + isGenerateDotdFile(sourceFile) ? Preconditions.checkNotNull(builder.getDotdFile()) : null; // Set dependency_file to enable <object>.d file generation. if (dotdFile != null) { buildVariables.addStringVariable( @@ -496,6 +496,12 @@ CcToolchainFeatures.Variables variables = buildVariables.build(); builder.setVariables(variables); } + + /** Returns true if Dotd file should be generated. */ + private boolean isGenerateDotdFile(Artifact sourceArtifact) { + return CppFileTypes.headerDiscoveryRequired(sourceArtifact) + && !featureConfiguration.isEnabled(CppRuleClasses.PARSE_SHOWINCLUDES); + } /** * Constructs the C++ compiler actions. It generally creates one action for every specified source @@ -533,8 +539,8 @@ if (!sourceArtifact.isTreeArtifact()) { switch (source.getType()) { case HEADER: - createHeaderAction(outputName, result, env, builder, - CppFileTypes.mustProduceDotdFile(sourceArtifact)); + createHeaderAction( + outputName, result, env, builder, isGenerateDotdFile(sourceArtifact)); break; case CLIF_INPUT_PROTO: createClifMatchAction(outputName, result, env, builder); @@ -557,7 +563,7 @@ // output (since it isn't generating a native object with debug // info). In that case the LTOBackendAction will generate the dwo. /*generateDwo=*/ cppConfiguration.useFission() && !bitcodeOutput, - CppFileTypes.mustProduceDotdFile(sourceArtifact), + isGenerateDotdFile(sourceArtifact), source.getBuildVariables()); break; } @@ -635,10 +641,7 @@ builder.setSemantics(semantics); builder.setPicMode(pic); builder.setOutputs( - ruleContext, - ArtifactCategory.OBJECT_FILE, - outputName, - CppFileTypes.mustProduceDotdFile(module)); + ruleContext, ArtifactCategory.OBJECT_FILE, outputName, isGenerateDotdFile(module)); PathFragment ccRelativeName = semantics.getEffectiveSourcePath(module); String gcnoFileName = @@ -708,7 +711,7 @@ /*addObject=*/ false, /*enableCoverage=*/ false, /*generateDwo=*/ false, - CppFileTypes.mustProduceDotdFile(moduleMapArtifact), + isGenerateDotdFile(moduleMapArtifact), ImmutableMap.<String, String>of()); }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java index 502ed9f..04c3368 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java
@@ -285,6 +285,9 @@ */ public static final String GENERATE_PDB_FILE = "generate_pdb_file"; + /** A string constant for /showIncludes parsing feature, should only be used for MSVC toolchain */ + public static final String PARSE_SHOWINCLUDES = "parse_showincludes"; + /* * A string constant for the fdo_instrument feature. */
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 index 720c9d2..f359545 100644 --- 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
@@ -156,10 +156,9 @@ HeaderDiscovery.Builder discoveryBuilder = new HeaderDiscovery.Builder() .setAction(this) - .setDotdFile(getDotdFile()) .setSourceFile(getSourceFile()) .setSpecialInputsHandler(specialInputsHandler) - .setDependencySet(processDepset(execRoot, reply)) + .setDependencies(processDepset(execRoot, reply).getDependencies()) .setPermittedSystemIncludePrefixes(getPermittedSystemIncludePrefixes(execRoot)) .setAllowedDerivedinputsMap(getAllowedDerivedInputsMap()); @@ -170,7 +169,7 @@ discoveredInputs = discoveryBuilder .build() - .discoverInputsFromDotdFiles(execRoot, scanningContext.getArtifactResolver()); + .discoverInputsFromDependencies(execRoot, scanningContext.getArtifactResolver()); } reply = null; // Clear in-memory .d files early.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/HeaderDiscovery.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/HeaderDiscovery.java index 5ae21d1..8072e56 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/HeaderDiscovery.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/HeaderDiscovery.java
@@ -26,16 +26,18 @@ import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible; -import com.google.devtools.build.lib.rules.cpp.CppCompileAction.DotdFile; import com.google.devtools.build.lib.rules.cpp.CppCompileAction.SpecialInputsHandler; -import com.google.devtools.build.lib.util.DependencySet; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; +import java.util.Collection; import java.util.List; import java.util.Map; -/** Manages the process of obtaining inputs used in a compilation from .d files. */ +/** + * Manages the process of obtaining inputs used in a compilation from a dependency set parsed from + * either .d files or /showIncludes output. + */ public class HeaderDiscovery { /** Indicates if a compile should perform dotd pruning. */ @@ -46,12 +48,11 @@ private final Action action; private final Artifact sourceFile; - private final DotdFile dotdFile; private final SpecialInputsHandler specialInputsHandler; private final boolean shouldValidateInclusions; - private final DependencySet depSet; + private final Collection<Path> dependencies; private final List<Path> permittedSystemIncludePrefixes; private final Map<PathFragment, Artifact> allowedDerivedInputsMap; @@ -60,32 +61,30 @@ * * @param action the action instance requiring header discovery * @param sourceFile the source file for the compile - * @param dotdFile the .d file used for header discovery * @param specialInputsHandler the SpecialInputsHandler for the build * @param shouldValidateInclusions true if include validation should be performed */ public HeaderDiscovery( Action action, Artifact sourceFile, - DotdFile dotdFile, SpecialInputsHandler specialInputsHandler, boolean shouldValidateInclusions, - DependencySet depSet, + Collection<Path> dependencies, List<Path> permittedSystemIncludePrefixes, Map<PathFragment, Artifact> allowedDerivedInputsMap) { this.action = Preconditions.checkNotNull(action); this.sourceFile = Preconditions.checkNotNull(sourceFile); - this.dotdFile = Preconditions.checkNotNull(dotdFile); this.specialInputsHandler = specialInputsHandler; this.shouldValidateInclusions = shouldValidateInclusions; - this.depSet = depSet; + this.dependencies = dependencies; this.permittedSystemIncludePrefixes = permittedSystemIncludePrefixes; this.allowedDerivedInputsMap = allowedDerivedInputsMap; } /** * Returns a collection with additional input artifacts relevant to the action by reading the - * dynamically-discovered dependency information from the .d file after the action has run. + * dynamically-discovered dependency information from the parsed dependency set after the action + * has run. * * <p>Artifacts are considered inputs but not "mandatory" inputs. * @@ -94,17 +93,17 @@ */ @VisibleForTesting @ThreadCompatible - public NestedSet<Artifact> discoverInputsFromDotdFiles( + public NestedSet<Artifact> discoverInputsFromDependencies( Path execRoot, ArtifactResolver artifactResolver) throws ActionExecutionException { NestedSetBuilder<Artifact> inputs = NestedSetBuilder.stableOrder(); - if (dotdFile == null) { + if (dependencies == null) { return inputs.build(); } List<Path> systemIncludePrefixes = permittedSystemIncludePrefixes; // Check inclusions. IncludeProblems problems = new IncludeProblems(); - for (Path execPath : depSet.getDependencies()) { + for (Path execPath : dependencies) { PathFragment execPathFragment = execPath.asFragment(); if (execPathFragment.isAbsolute()) { // Absolute includes from system paths are ignored. @@ -157,11 +156,10 @@ public static class Builder { private Action action; private Artifact sourceFile; - private DotdFile dotdFile; private SpecialInputsHandler specialInputsHandler; private boolean shouldValidateInclusions = false; - private DependencySet depSet; + private Collection<Path> dependencies; private List<Path> permittedSystemIncludePrefixes; private Map<PathFragment, Artifact> allowedDerivedInputsMap; @@ -177,12 +175,6 @@ return this; } - /** Sets the dotd file to be used to discover inputs. */ - public Builder setDotdFile(DotdFile dotdFile) { - this.dotdFile = dotdFile; - return this; - } - /** Sets the SpecialInputsHandler for inputs to this build. */ public Builder setSpecialInputsHandler(SpecialInputsHandler specialInputsHandler) { this.specialInputsHandler = specialInputsHandler; @@ -195,9 +187,9 @@ return this; } - /** Sets the DependencySet capturing used headers by this compile. */ - public Builder setDependencySet(DependencySet depSet) { - this.depSet = depSet; + /** Sets the dependencies capturing used headers by this compile. */ + public Builder setDependencies(Collection<Path> dependencies) { + this.dependencies = dependencies; return this; } @@ -218,10 +210,9 @@ return new HeaderDiscovery( action, sourceFile, - dotdFile, specialInputsHandler, shouldValidateInclusions, - depSet, + dependencies, permittedSystemIncludePrefixes, allowedDerivedInputsMap); }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/ShowIncludesFilter.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/ShowIncludesFilter.java new file mode 100644 index 0000000..14b1417 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/ShowIncludesFilter.java
@@ -0,0 +1,107 @@ +// Copyright 2014 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.devtools.build.lib.util.io.FileOutErr; +import com.google.devtools.build.lib.vfs.Path; +import java.io.ByteArrayOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +/** + * A Class for filtering the output of /showIncludes from MSVC compiler. + * + * <p>A discovered header file will be printed with prefix "Note: including file:", the path is + * collected, and the line is suppressed from the actual output users can see. + * + * <p>Also suppress the basename of source file, which is printed unconditionally by MSVC compiler, + * there is no way to turn it off. + */ +public class ShowIncludesFilter implements FileOutErr.OutputFilter { + + private FilterShowIncludesOutputStream filterShowIncludesOutputStream; + private final String sourceFileName; + + public ShowIncludesFilter(String sourceFileName) { + this.sourceFileName = sourceFileName; + } + + /** + * Use this class to filter and collect the headers discovered by MSVC compiler, also filter out + * the source file name printed unconditionally by the compiler. + */ + public static class FilterShowIncludesOutputStream extends FilterOutputStream { + + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(4096); + private final Collection<String> dependencies = new ArrayList<>(); + private static final int NEWLINE = '\n'; + private static final String SHOW_INCLUDES_PREFIX = "Note: including file:"; + private final String sourceFileName; + + public FilterShowIncludesOutputStream(OutputStream out, String sourceFileName) { + super(out); + this.sourceFileName = sourceFileName; + } + + @Override + public void write(int b) throws IOException { + buffer.write(b); + if (b == NEWLINE) { + String line = buffer.toString(StandardCharsets.UTF_8.name()); + if (line.startsWith(SHOW_INCLUDES_PREFIX)) { + dependencies.add(line.substring(SHOW_INCLUDES_PREFIX.length()).trim()); + } else if (!line.trim().equals(sourceFileName)) { + buffer.writeTo(out); + } + buffer.reset(); + } + } + + @Override + public void flush() throws IOException { + String line = buffer.toString(StandardCharsets.UTF_8.name()); + if (!line.startsWith(SHOW_INCLUDES_PREFIX) && !line.startsWith(sourceFileName)) { + buffer.writeTo(out); + } + out.flush(); + } + + public Collection<String> getDependencies() { + return this.dependencies; + } + } + + @Override + public FilterOutputStream getFilteredOutputStream(OutputStream outputStream) { + filterShowIncludesOutputStream = + new FilterShowIncludesOutputStream(outputStream, sourceFileName); + return filterShowIncludesOutputStream; + } + + public Collection<Path> getDependencies(Path root) { + Collection<Path> dependenciesInPath = new ArrayList<>(); + if (filterShowIncludesOutputStream != null) { + for (String dep : filterShowIncludesOutputStream.getDependencies()) { + dependenciesInPath.add(root.getRelative(dep)); + } + } + return Collections.unmodifiableCollection(dependenciesInPath); + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCompileAction.java index e5be5fd..5b6280d 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCompileAction.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCompileAction.java
@@ -236,12 +236,11 @@ return new HeaderDiscovery.Builder() .setAction(this) .setSourceFile(sourceFile) - .setDotdFile(dotdFile) - .setDependencySet(processDepset(execRoot)) + .setDependencies(processDepset(execRoot).getDependencies()) .setPermittedSystemIncludePrefixes(ImmutableList.<Path>of()) .setAllowedDerivedinputsMap(getAllowedDerivedInputsMap(true)) .build() - .discoverInputsFromDotdFiles(execRoot, artifactResolver); + .discoverInputsFromDependencies(execRoot, artifactResolver); } private DependencySet processDepset(Path execRoot) throws ActionExecutionException {
diff --git a/src/main/java/com/google/devtools/build/lib/util/io/FileOutErr.java b/src/main/java/com/google/devtools/build/lib/util/io/FileOutErr.java index b1e0faa..07949e4 100644 --- a/src/main/java/com/google/devtools/build/lib/util/io/FileOutErr.java +++ b/src/main/java/com/google/devtools/build/lib/util/io/FileOutErr.java
@@ -18,7 +18,7 @@ import com.google.devtools.build.lib.concurrent.ThreadSafety; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; - +import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -83,6 +83,16 @@ super(stream, stream); } + // Set a filter for FileOutputStream + public void setOutputFilter(OutputFilter outputFilter) { + getFileOutputStream().setFilter(outputFilter); + } + + // Set a filter for FileErrorStream + public void setErrorFilter(OutputFilter outputFilter) { + getFileOutputStream().setFilter(outputFilter); + } + /** * Returns true if any output was recorded. */ @@ -210,10 +220,15 @@ */ abstract void dumpOut(OutputStream out); - /** - * Closes and delets the output. - */ + /** Closes and deletes the output. */ abstract void clear() throws IOException; + + /** + * Set a Filter for the output + * + * @param outputFilter + */ + abstract void setFilter(OutputFilter outputFilter); } /** @@ -254,6 +269,9 @@ public void clear() { } + @Override + void setFilter(OutputFilter outputFilter) {} + @Override public void write(byte[] b, int off, int len) { @@ -268,6 +286,10 @@ } } + /** An interface to get a filtered output stream from the original one. */ + public interface OutputFilter { + FilterOutputStream getFilteredOutputStream(OutputStream outputStream); + } /** * An output stream that captures all output into a file. @@ -290,6 +312,7 @@ private final Path outputFile; private OutputStream outputStream; private String error; + private OutputFilter outputFilter; protected FileRecordingOutputStream(Path outputFile) { this.outputFile = outputFile; @@ -309,6 +332,9 @@ // you should hold the lock before you invoke this method if (outputStream == null) { outputStream = outputFile.getOutputStream(); + if (outputFilter != null) { + outputStream = outputFilter.getFilteredOutputStream(outputStream); + } } return outputStream; } @@ -324,6 +350,11 @@ outputFile.delete(); } + @Override + void setFilter(OutputFilter outputFilter) { + this.outputFilter = outputFilter; + } + /** * Called whenever the FileRecordingOutputStream finds an error. */
diff --git a/tools/cpp/CROSSTOOL.tpl b/tools/cpp/CROSSTOOL.tpl index 2d394c62..353e86f 100644 --- a/tools/cpp/CROSSTOOL.tpl +++ b/tools/cpp/CROSSTOOL.tpl
@@ -300,8 +300,15 @@ } } + # Stop adding any flag for dotD file, Bazel knows how to parse the output of /showIncludes option + # TODO(bazel-team): Remove this empty feature. https://github.com/bazelbuild/bazel/issues/2868 feature { name: 'dependency_file' + } + + # Tell Bazel to parse the output of /showIncludes + feature { + name: 'parse_showincludes' flag_set { action: 'assemble' action: 'preprocess-assemble' @@ -310,10 +317,8 @@ action: 'c++-module-compile' action: 'c++-header-preprocessing' action: 'c++-header-parsing' - expand_if_all_available: 'dependency_file' flag_group { - flag: '/DEPENDENCY_FILE' - flag: '%{dependency_file}' + flag: "/showIncludes" } } } @@ -361,6 +366,7 @@ } implies: 'nologo' implies: 'msvc_env' + implies: 'parse_showincludes' } action_config { @@ -396,6 +402,7 @@ } implies: 'nologo' implies: 'msvc_env' + implies: 'parse_showincludes' } action_config {
diff --git a/tools/cpp/wrapper/bin/pydir/msvc_cl.py b/tools/cpp/wrapper/bin/pydir/msvc_cl.py index 89766a6..4f4b685 100644 --- a/tools/cpp/wrapper/bin/pydir/msvc_cl.py +++ b/tools/cpp/wrapper/bin/pydir/msvc_cl.py
@@ -43,7 +43,6 @@ # This is unneeded for Windows. (('-include', '(.+)'), ['/FI$PATH0']), - (('/DEPENDENCY_FILE', '(.+)'), ['$GENERATE_DEPS0']), ('-w', ['/w']), ('-Wall', ['/Wall']), ('-Wsign-compare', ['/we4018']), @@ -107,6 +106,27 @@ """ parser = msvc_tools.ArgParser(self, argv, GCCPATTERNS) + # Select runtime option + # Find the last runtime option passed + rt = None + rt_idx = -1 + for i, opt in enumerate(reversed(parser.options)): + if opt in ['/MT', '/MTd', '/MD', '/MDd']: + if opt[-1] == 'd': + parser.enforce_debug_rt = True + rt = opt[:3] + rt_idx = len(parser.options) - i - 1 + break + rt = rt or '/MT' # Default to static runtime + # Add debug if necessary + if parser.enforce_debug_rt: + rt += 'd' + # Include runtime option + if rt_idx >= 0: + parser.options[rt_idx] = rt + else: + parser.options.append(rt) + compiler = 'cl' if parser.is_cuda_compilation: compiler = 'nvcc'
diff --git a/tools/cpp/wrapper/bin/pydir/msvc_tools.py.tpl b/tools/cpp/wrapper/bin/pydir/msvc_tools.py.tpl index 76a5aa5..b8f86ab 100644 --- a/tools/cpp/wrapper/bin/pydir/msvc_tools.py.tpl +++ b/tools/cpp/wrapper/bin/pydir/msvc_tools.py.tpl
@@ -20,6 +20,7 @@ import os import re import subprocess +import sys MAX_PATH = 260 # The maximum number of characters in a Windows path. MAX_OPTION_LENGTH = 10 # The maximum length of a compiler/linker option. @@ -58,6 +59,7 @@ self.global_whole_archive = None self.is_cuda_compilation = None self.cuda_log = False + self.enforce_debug_rt = False self._ParseArgs(argv) def ReplaceLibrary(self, arg): @@ -256,7 +258,6 @@ matched = [] unmatched = [] files = [] - enforce_debug_rt = False while i < len(argv): num_matched, action, groups = self._MatchOneArg(argv[i:]) arg = argv[i] @@ -308,7 +309,7 @@ continue if entry == '$DEBUG_RT': - enforce_debug_rt = True + self.enforce_debug_rt = True continue if not groups: @@ -338,11 +339,6 @@ exit(-1) continue - if entry == ('$GENERATE_DEPS%d' % g): - self.options.append('/showIncludes') - self.deps_file = value - continue - # Regular substitution. patterns = { '$%d' % g: value, @@ -356,27 +352,6 @@ i += num_matched self.leftover = unmatched - # Select runtime option - # Find the last runtime option passed - rt = None - rt_idx = -1 - for i, opt in enumerate(reversed(self.options)): - if opt in ['/MT', '/MTd', '/MD', '/MDd']: - if opt[-1] == 'd': - enforce_debug_rt = True - rt = opt[:3] - rt_idx = len(self.options) - i - 1 - break - rt = rt or '/MT' # Default to static runtime - # Add debug if necessary - if enforce_debug_rt: - rt += 'd' - # Include runtime option - if rt_idx >= 0: - self.options[rt_idx] = rt - else: - self.options.append(rt) - # Add in any parsed files self.options += files @@ -452,28 +427,7 @@ Returns: The return code from executing binary. """ - # Filter out some not-so-useful cl windows messages. - filters = [ - '.*warning LNK4006: __NULL_IMPORT_DESCRIPTOR already defined.*\n', - '.*warning LNK4044: unrecognized option \'/MT\'; ignored.*\n', - '.*warning LNK4044: unrecognized option \'/link\'; ignored.*\n', - '.*warning LNK4221: This object file does not define any ' - 'previously.*\n', - '\r\n', - '\n\r', - ] - # Check again the arguments are within MAX_PATH. - for arg in args: - if os.path.splitext(arg)[1].lower() in ['.c', '.cc', '.cpp', '.s']: - # cl.exe prints out the file name it is compiling; add that to the - # filter. - name = arg.rpartition(ntpath.sep)[2] - filters.append(name) - - # Construct a large regular expression for all filters. - output_filter = re.compile('(' + ')|('.join(filters) + ')') - includes_filter = re.compile(r'Note: including file:\s+(.*)') # Run the command. if parser.params_file: try: @@ -493,32 +447,9 @@ # Unconmment the following line to see what exact command is executed. # print("Running: " + " ".join(cmd)) proc = subprocess.Popen(cmd, + stdout=sys.stdout, + stderr=sys.stderr, env=os.environ.copy(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, shell=True) - deps = [] - for line in proc.stdout: - line = line.decode('utf-8') - if not output_filter.match(line): - includes = includes_filter.match(line) - if includes: - filename = includes.group(1).rstrip() - deps += [filename] - else: - print(line.rstrip()) proc.wait() - - # Generate deps file if requested. - if parser.deps_file: - with open(parser.deps_file, 'w') as deps_file: - # Start with the name of the output file. - deps_file.write(parser.output_file + ': \\\n') - for i, dep in enumerate(deps): - dep = dep.replace('\\', '/').replace(' ', '\\ ') - deps_file.write(' ' + dep) - if i < len(deps) - 1: - deps_file.write(' \\') - deps_file.write('\n') - return proc.returncode