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