Generating PDB files on Windows

1. Add /Z7 as compiler flag in CROSSTOOL, this causes full debugging
   infomation built into object files, no PDB file is generated.

2. Add /DEBUG as linker flag so that a PDB file will be generated for
   executable or dll.
     * /DEBUG:FULL for dbg mode. the full PDB can be used to debug the
       executable when no other build products are available, such as
       when the executable is deployed.
     * /DEBUG:FASTLINK for fastbuild mode. object files are still needed
       when debugging the executable, but linking speed can be two to four
       times faster that full PDB generation.
     * No option is added for opt mode.
     More detailed info: https://msdn.microsoft.com/en-us/library/xe4t6fc1.aspx

3. Add an empty feature in MSVC CROSSTOOL to tell Bazel we need PDB file

4. Add PDB file artifact in an output named pdb_file of cc_binary,
   then you can build the pdb file by bazel build //foo/bar:bin --output_groups=pdb_file

5. Add test for PDB file generation

Change-Id: Ia5096470187ebca72f2c804f32d5b723f40c0b85
PiperOrigin-RevId: 153449059
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
index 9b9ae31..ca98d7b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
@@ -297,6 +297,14 @@
       linkActionBuilder.setLTOIndexing(false);
     }
 
+    // On Windows, if GENERATE_PDB_FILE feature is enabled
+    // then a pdb file will be built along with the executable.
+    Artifact pdbFile = null;
+    if (featureConfiguration.isEnabled(CppRuleClasses.GENERATE_PDB_FILE)) {
+      pdbFile = ruleContext.getRelatedArtifact(binary.getRootRelativePath(), ".pdb");
+      linkActionBuilder.addActionOutput(pdbFile);
+    }
+
     CppLinkAction linkAction = linkActionBuilder.build();
     ruleContext.registerAction(linkAction);
     LibraryToLink outputLibrary = linkAction.getOutputLibrary();
@@ -412,6 +420,11 @@
           new ExecutionInfoProvider(ImmutableMap.of(ExecutionRequirements.REQUIRES_DARWIN, "")));
     }
 
+    // If PDB file is generated by the link action, we add it to pdb_file output group
+    if (pdbFile != null) {
+      ruleBuilder.addOutputGroup("pdb_file", pdbFile);
+    }
+
     return ruleBuilder
         .addProvider(RunfilesProvider.class, RunfilesProvider.simple(runfiles))
         .addProvider(
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 698353a..6cad3de 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
@@ -283,6 +283,12 @@
    */
   public static final String THIN_LTO = "thin_lto";
 
+  /**
+   * A string constant for the PDB file generation feature, should only be used for toolchains
+   * targeting Windows that include a linker producing PDB files
+   */
+  public static final String GENERATE_PDB_FILE = "generate_pdb_file";
+
   /*
    * A string constant for the fdo_instrument feature.
    */
diff --git a/src/test/shell/bazel/bazel_windows_example_test.sh b/src/test/shell/bazel/bazel_windows_example_test.sh
index 5f16499..e62dc95 100755
--- a/src/test/shell/bazel/bazel_windows_example_test.sh
+++ b/src/test/shell/bazel/bazel_windows_example_test.sh
@@ -57,6 +57,10 @@
 function test_cpp() {
   local cpp_pkg=examples/cpp
   assert_build_output ./bazel-bin/${cpp_pkg}/libhello-lib.a ${cpp_pkg}:hello-world
+  assert_build_output ./bazel-bin/${cpp_pkg}/hello-world.pdb ${cpp_pkg}:hello-world --output_groups=pdb_file
+  assert_build_output ./bazel-bin/${cpp_pkg}/hello-world.pdb -c dbg ${cpp_pkg}:hello-world --output_groups=pdb_file
+  assert_build -c opt ${cpp_pkg}:hello-world --output_groups=pdb_file
+  test -f ./bazel-bin/${cpp_pkg}/hello-world.pdb && fail "PDB file should not be generated in OPT mode"
   assert_bazel_run "//examples/cpp:hello-world foo" "Hello foo"
   assert_test_ok "//examples/cpp:hello-success_test"
   assert_test_fails "//examples/cpp:hello-fail_test"
diff --git a/tools/cpp/CROSSTOOL.tpl b/tools/cpp/CROSSTOOL.tpl
index 60fb955..8bf57e5 100644
--- a/tools/cpp/CROSSTOOL.tpl
+++ b/tools/cpp/CROSSTOOL.tpl
@@ -463,6 +463,16 @@
   }
 
   feature {
+    name: 'generate_pdb_file'
+    requires: {
+      feature: 'dbg'
+    }
+    requires: {
+      feature: 'fastbuild'
+    }
+  }
+
+  feature {
     name: 'has_configured_linker_path'
   }
 
@@ -641,7 +651,6 @@
     }
   }
 
-
   feature {
     name: 'linker_param_file'
     flag_set {
@@ -664,31 +673,78 @@
     }
   }
 
+  feature {
+    name: 'dbg'
+    flag_set {
+      action: 'c-compile'
+      action: 'c++-compile'
+      flag_group {
+        flag: "/Od"
+        flag: '/MTd'
+        flag: "/Z7"
+      }
+    }
+    flag_set {
+      action: 'c++-link-executable'
+      action: 'c++-link-dynamic-library'
+      flag_group {
+        flag: "/DEBUG:FULL"
+        flag: "/INCREMENTAL:NO"
+      }
+    }
+    implies: 'generate_pdb_file'
+  }
+
+  feature {
+    name: 'fastbuild'
+    flag_set {
+      action: 'c-compile'
+      action: 'c++-compile'
+      flag_group {
+        flag: "/Od"
+        flag: '/MT'
+        flag: "/Z7"
+      }
+    }
+    flag_set {
+      action: 'c++-link-executable'
+      action: 'c++-link-dynamic-library'
+      flag_group {
+        flag: "/DEBUG:FASTLINK"
+        flag: "/INCREMENTAL:NO"
+      }
+    }
+    implies: 'generate_pdb_file'
+  }
+
+  feature {
+    name: 'opt'
+    flag_set {
+      action: 'c-compile'
+      action: 'c++-compile'
+      flag_group {
+        flag: "/O2"
+        flag: '/MT'
+      }
+    }
+  }
+
   compilation_mode_flags {
     mode: DBG
-    compiler_flag: "/DDEBUG=1"
-    # This will signal the wrapper that we are doing a debug build, which sets
-    # some internal state of the toolchain wrapper. It is intentionally a "-"
-    # flag to make this very obvious.
-    compiler_flag: "-g"
-    compiler_flag: "/Od"
     compiler_flag: "-Xcompilation-mode=dbg"
     linker_flag: "-Xcompilation-mode=dbg"
   }
 
   compilation_mode_flags {
     mode: FASTBUILD
-    compiler_flag: "/DNDEBUG"
-    compiler_flag: "/Od"
     compiler_flag: "-Xcompilation-mode=fastbuild"
     linker_flag: "-Xcompilation-mode=fastbuild"
   }
 
   compilation_mode_flags {
     mode: OPT
-    compiler_flag: "/DNDEBUG"
-    compiler_flag: "/O2"
     compiler_flag: "-Xcompilation-mode=opt"
     linker_flag: "-Xcompilation-mode=opt"
   }
+
 }
diff --git a/tools/cpp/wrapper/bin/pydir/msvc_tools.py.tpl b/tools/cpp/wrapper/bin/pydir/msvc_tools.py.tpl
index 0695593..127aa87 100644
--- a/tools/cpp/wrapper/bin/pydir/msvc_tools.py.tpl
+++ b/tools/cpp/wrapper/bin/pydir/msvc_tools.py.tpl
@@ -263,8 +263,6 @@
       if arg.startswith('/Fo') or arg.startswith('/Fa') or arg.startswith(
           '/Fi'):
         self.output_file = arg[3:]
-        self.options.append(
-            '/Fd%s.pdb' % self.NormPath(os.path.splitext(self.output_file)[0]))
       if num_matched == 0:
         # Strip out any .a's that have 0 size, they are header or intermediate
         # dependency libraries and don't contain any code. 0-length files are