thakis updated this revision to Diff 49325.
thakis added a comment.

clean up accidentally duplicate if block


http://reviews.llvm.org/D17695

Files:
  include/clang/Basic/DiagnosticDriverKinds.td
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Driver/Action.h
  include/clang/Driver/CLCompatOptions.td
  include/clang/Driver/Driver.h
  lib/Driver/Action.cpp
  lib/Driver/Compilation.cpp
  lib/Driver/Driver.cpp
  lib/Driver/Tools.cpp
  test/Driver/Inputs/pchfile.h
  test/Driver/cl-pch.c
  test/Driver/cl-pch.cpp

Index: test/Driver/cl-pch.cpp
===================================================================
--- test/Driver/cl-pch.cpp
+++ test/Driver/cl-pch.cpp
@@ -0,0 +1,309 @@
+// Note: %s and %S must be preceded by --, otherwise it may be interpreted as a
+// command-line option, e.g. on Mac where %s is commonly under /Users.
+
+// /Yc
+// RUN: %clang_cl -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC %s
+// 1. Build .pch file.
+// CHECK-YC: cc1
+// CHECK-YC: -emit-pch
+// CHECK-YC: -o
+// CHECK-YC: pchfile.pch
+// CHECK-YC: -x
+// CHECK-YC: "c++"
+// 2. Use .pch file.
+// CHECK-YC: cc1
+// CHECK-YC: -emit-obj
+// CHECK-YC: -include-pch
+// CHECK-YC: pchfile.pch
+
+// /Yc /Fo
+// /Fo overrides the .obj output filename, but not the .pch filename
+// RUN: %clang_cl -Werror /Fomyobj.obj /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCO %s
+// 1. Build .pch file.
+// CHECK-YCO: cc1
+// CHECK-YCO: -emit-pch
+// CHECK-YCO: -o
+// CHECK-YCO: pchfile.pch
+// 2. Use .pch file.
+// CHECK-YCO: cc1
+// CHECK-YCO: -emit-obj
+// CHECK-YCO: -include-pch
+// CHECK-YCO: pchfile.pch
+// CHECK-YCO: -o
+// CHECK-YCO: myobj.obj
+
+// /Yc /Y-
+// /Y- disables pch generation
+// RUN: %clang_cl -Werror /Y- /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-Y_ %s
+// CHECK-YC-Y_-NOT: -emit-pch
+// CHECK-YC-Y_-NOT: -include-pch
+
+// /Yu
+// RUN: %clang_cl -Werror /Yupchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU %s
+// Use .pch file, but don't build it.
+// CHECK-YU-NOT: -emit-pch
+// CHECK-YU: cc1
+// CHECK-YU: -emit-obj
+// CHECK-YU: -include-pch
+// CHECK-YU: pchfile.pch
+
+// /Yu /Y-
+// RUN: %clang_cl -Werror /Y- /Yupchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-Y_ %s
+// CHECK-YU-Y_-NOT: -emit-pch
+// CHECK-YU-Y_-NOT: -include-pch
+
+// /Yc /Yu -- /Yc overrides /Yc if they both refer to the same file
+// RUN: %clang_cl -Werror /Ycpchfile.h /Yupchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-YU %s
+// 1. Build .pch file.
+// CHECK-YC-YU: cc1
+// CHECK-YC-YU: -emit-pch
+// CHECK-YC-YU: -o
+// CHECK-YC-YU: pchfile.pch
+// 2. Use .pch file.
+// CHECK-YC-YU: cc1
+// CHECK-YC-YU: -emit-obj
+// CHECK-YC-YU: -include-pch
+// CHECK-YC-YU: pchfile.pch
+
+// If /Yc /Yu refer to different files, semantics are pretty wonky.  Since this
+// doesn't seem like something that's important in practice, just punt for now.
+// RUN: %clang_cl -Werror /Ycfoo1.h /Yufoo2.h /FIfoo1.h /FIfoo2.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-YU-MISMATCH %s
+// CHECK-YC-YU-MISMATCH: error: support for '/Yc' and '/Yu' with different filenames not implemented yet, ignoring flags
+
+// Similarly, punt on /Yc with more than one input file.
+// RUN: %clang_cl -Werror /Ycfoo1.h /FIfoo1.h /c -### -- %s %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-MULTIINPUT %s
+// CHECK-YC-MULTIINPUT: error: support for '/Yc' with more than one input source file not implemented yet, ignoring flag
+
+// /Yc /Yu /Y-
+// RUN: %clang_cl -Werror /Ycpchfile.h /Yupchfile.h /FIpchfile.h /Y- /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-YU-Y_ %s
+// CHECK-YC-YU-Y_-NOT: -emit-pch
+// CHECK-YC-YU-Y_-NOT: -include-pch
+
+// Test computation of pch filename in various cases.
+
+// /Yu /Fpout.pch => out.pch is filename
+// RUN: %clang_cl -Werror /Yupchfile.h /FIpchfile.h /Fpout.pch /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFP1 %s
+// Use .pch file, but don't build it.
+// CHECK-YUFP1: -include-pch
+// CHECK-YUFP1: out.pch
+
+// /Yu /Fpout => out.pch is filename (.pch gets added if no extension present)
+// RUN: %clang_cl -Werror /Yupchfile.h /FIpchfile.h /Fpout.pch /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFP2 %s
+// Use .pch file, but don't build it.
+// CHECK-YUFP2: -include-pch
+// CHECK-YUFP2: out.pch
+
+// /Yu /Fpout.bmp => out.bmp is filename (.pch not added when extension present)
+// RUN: %clang_cl -Werror /Yupchfile.h /FIpchfile.h /Fpout.bmp /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFP3 %s
+// Use .pch file, but don't build it.
+// CHECK-YUFP3: -include-pch
+// CHECK-YUFP3: out.bmp
+
+// /Yusub/dir.h => sub/dir.pch
+// RUN: %clang_cl -Werror /Yusub/pchfile.h /FIsub/pchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFP4 %s
+// Use .pch file, but don't build it.
+// CHECK-YUFP4: -include-pch
+// CHECK-YUFP4: sub/pchfile.pch
+
+// /Yudir.h /Isub => dir.pch
+// RUN: %clang_cl -Werror /Yupchfile.h /FIpchfile.h /Isub /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFP5 %s
+// Use .pch file, but don't build it.
+// CHECK-YUFP5: -include-pch
+// CHECK-YUFP5: pchfile.pch
+
+// FIXME: /Fpdir: use dir/VCx0.pch when dir is directory, where x is major MSVS
+// version in use.
+
+// Spot-check one use of /Fp with /Yc too, else trust the /Yu test cases above
+// also all assume to /Yc.
+// RUN: %clang_cl -Werror /Ycpchfile.h /FIpchfile.h /Fpsub/file.pch /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCFP %s
+// 1. Build .pch file.
+// CHECK-YCFP: cc1
+// CHECK-YCFP: -emit-pch
+// CHECK-YCFP: -o
+// CHECK-YCFP: sub/file.pch
+// 2. Use .pch file.
+// CHECK-YCFP: cc1
+// CHECK-YCFP: -emit-obj
+// CHECK-YCFP: -include-pch
+// CHECK-YCFP: sub/file.pch
+
+// /Ycfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h
+// => foo1 and foo2 go into pch, foo3 into main compilation
+// /Yc
+// RUN: %clang_cl -Werror /Ycfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCFIFIFI %s
+// 1. Build .pch file: Includes foo1.h (but NOT foo3.h) and compiles foo2.h
+// CHECK-YCFIFIFI: cc1
+// CHECK-YCFIFIFI: -emit-pch
+// CHECK-YCFIFIFI: -include
+// CHECK-YCFIFIFI: foo1.h
+// CHECK-YCFIFIFI-NOT: foo2.h
+// CHECK-YCFIFIFI-NOT: foo3.h
+// CHECK-YCFIFIFI: -o
+// CHECK-YCFIFIFI: foo2.pch
+// CHECK-YCFIFIFI: -x
+// CHECK-YCFIFIFI: "c++"
+// CHECK-YCFIFIFI: foo2.h
+// 2. Use .pch file: Inlucdes foo2.pch and foo3.h
+// CHECK-YCFIFIFI: cc1
+// CHECK-YCFIFIFI: -emit-obj
+// CHECK-YCFIFIFI-NOT: foo1.h
+// CHECK-YCFIFIFI-NOT: foo2.h
+// CHECK-YCFIFIFI: -include-pch
+// CHECK-YCFIFIFI: foo2.pch
+// CHECK-YCFIFIFI: -include
+// CHECK-YCFIFIFI: foo3.h
+
+// /Yucfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h
+// => foo1 foo2 filtered out, foo3 into main compilation
+// RUN: %clang_cl -Werror /Yufoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFIFIFI %s
+// Use .pch file, but don't build it.
+// CHECK-YUFIFIFI-NOT: -emit-pch
+// CHECK-YUFIFIFI: cc1
+// CHECK-YUFIFIFI: -emit-obj
+// CHECK-YUFIFIFI-NOT: foo1.h
+// CHECK-YUFIFIFI-NOT: foo2.h
+// CHECK-YUFIFIFI: -include-pch
+// CHECK-YUFIFIFI: foo2.pch
+// CHECK-YUFIFIFI: -include
+// CHECK-YUFIFIFI: foo3.h
+
+// FIXME: Implement support for /Ycfoo.h / /Yufoo.h without /FIfoo.h
+// RUN: %clang_cl -Werror /Ycfoo.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-NOFI %s
+// CHECK-YC-NOFI: error: support for '/Yc' without a corresponding /FI flag not implemented yet, ignoring flag
+// RUN: %clang_cl -Werror /Yufoo.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-NOFI %s
+// CHECK-YU-NOFI: error: support for '/Yu' without a corresponding /FI flag not implemented yet, ignoring flag
+
+// /Yc and /FI relative to /I paths...
+// The rules are:
+// Yu/Yc and FI parameter must match exactly, else it's not found
+// Must match literally exactly: /FI./foo.h /Ycfoo.h does _not_ work.
+// However, the path can be relative to /I paths.
+// FIXME: Update the error messages below once /FI is no longer required, but
+// these test cases all should stay failures as they fail with cl.exe.
+
+// Check that ./ isn't canonicalized away.
+// RUN: %clang_cl -Werror /Ycpchfile.h /FI./pchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-I1 %s
+// CHECK-YC-I1: support for '/Yc' without a corresponding /FI flag not implemented yet, ignoring flag
+
+// Check that ./ isn't canonicalized away.
+// RUN: %clang_cl -Werror /Yc./pchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-I2 %s
+// CHECK-YC-I2: support for '/Yc' without a corresponding /FI flag not implemented yet, ignoring flag
+
+// With an actual /I argument.
+// RUN: %clang_cl -Werror /Ifoo /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-I3 %s
+// 1. This writes pchfile.pch into the root dir, even if this will pick up
+//    foo/pchfile.h
+// CHECK-YC-I3: cc1
+// CHECK-YC-I3: -emit-pch
+// CHECK-YC-I3: -o
+// CHECK-YC-I3: pchfile.pch
+// 2. Use .pch file.
+// CHECK-YC-I3: cc1
+// CHECK-YC-I3: -emit-obj
+// CHECK-YC-I3: -include-pch
+// CHECK-YC-I3: pchfile.pch
+
+// Check that ./ isn't canonicalized away for /Yu either.
+// RUN: %clang_cl -Werror /Yupchfile.h /FI./pchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-I1 %s
+// CHECK-YU-I1: support for '/Yu' without a corresponding /FI flag not implemented yet, ignoring flag
+
+// But /FIfoo/bar.h /Ycfoo\bar.h does work, as does /FIfOo.h /Ycfoo.H
+// FIXME: This part isn't implemented yet. The following two tests should not
+// show an error but do regular /Yu handling.
+// RUN: %clang_cl -Werror /YupchFILE.h /FI./pchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-CASE %s
+// CHECK-YU-CASE: support for '/Yu' without a corresponding /FI flag not implemented yet, ignoring flag
+// RUN: %clang_cl -Werror /Yu./pchfile.h /FI.\pchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-SLASH %s
+// CHECK-YU-SLASH: support for '/Yu' without a corresponding /FI flag not implemented yet, ignoring flag
+
+// cl.exe warns on multiple /Yc, /Yu, /Fp arguments, but clang-cl silently just
+// uses the last one.  This is true for e.g. /Fo too, so not warning on this
+// is self-consistent with clang-cl's flag handling.
+
+// Interaction with /fallback
+
+// /Yc /fallback => /Yc not passed on (but /FI is)
+// RUN: %clang_cl -Werror /Ycpchfile.h /FIpchfile.h /Fpfoo.pch /fallback /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-FALLBACK %s
+// Note that in /fallback builds, if creation of the pch fails the main compile
+// does still run so that /fallback can have an effect (this part is not tested)
+// CHECK-YC-FALLBACK: cc1
+// CHECK-YC-FALLBACK: -emit-obj
+// CHECK-YC-FALLBACK: -include-pch
+// CHECK-YC-FALLBACK: foo.pch
+// CHECK-YC-FALLBACK: ||
+// CHECK-YC-FALLBACK: cl.exe
+// CHECK-YC-FALLBACK-NOT: -include-pch
+// CHECK-YC-FALLBACK-NOT: /Ycpchfile.h
+// CHECK-YC-FALLBACK: /FIpchfile.h
+// CHECK-YC-FALLBACK-NOT: /Fpfoo.pch
+
+// /Yu /fallback => /Yu not passed on (but /FI is)
+// RUN: %clang_cl -Werror /Yupchfile.h /FIpchfile.h /Fpfoo.pch /fallback /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-FALLBACK %s
+// CHECK-YU-FALLBACK-NOT: -emit-pch
+// CHECK-YU-FALLBACK: cc1
+// CHECK-YU-FALLBACK: -emit-obj
+// CHECK-YU-FALLBACK: -include-pch
+// CHECK-YU-FALLBACK: foo.pch
+// CHECK-YU-FALLBACK: ||
+// CHECK-YU-FALLBACK: cl.exe
+// CHECK-YU-FALLBACK-NOT: -include-pch
+// CHECK-YU-FALLBACK-NOT: /Yupchfile.h
+// CHECK-YU-FALLBACK: /FIpchfile.h
+// CHECK-YU-FALLBACK-NOT: /Fpfoo.pch
+
+// /FI without /Yu => pch file not used, even if it exists (different from
+// -include, which picks up .gch files if they exist).
+// RUN: touch %t.pch
+// RUN: %clang_cl -Werror /FI%t.pch /Fp%t.pch /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-FI %s
+// CHECK-FI-NOT: -include-pch
+// CHECK-FI: -include
+
+// Test interaction of /Yc with language mode flags.
+
+// If /TC changes the input language to C, a c pch file should be produced.
+// RUN: %clang_cl /TC -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCTC %s
+// CHECK-YCTC: cc1
+// CHECK-YCTC: -emit-pch
+// CHECK-YCTC: -o
+// CHECK-YCTC: pchfile.pch
+// CHECK-YCTC: -x
+// CHECK-YCTP: "c"
+
+// Also check lower-case /Tc variant.
+// RUN: %clang_cl -Werror /Ycpchfile.h /FIpchfile.h /c -### /Tc%s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCTc %s
+// CHECK-YCTc: cc1
+// CHECK-YCTc: -emit-pch
+// CHECK-YCTc: -o
+// CHECK-YCTc: pchfile.pch
+// CHECK-YCTc: -x
+// CHECK-YCTc: "c"
Index: test/Driver/cl-pch.c
===================================================================
--- test/Driver/cl-pch.c
+++ test/Driver/cl-pch.c
@@ -0,0 +1,45 @@
+// Note: %s and %S must be preceded by --, otherwise it may be interpreted as a
+// command-line option, e.g. on Mac where %s is commonly under /Users.
+
+// The main test for clang-cl pch handling is cl-pch.cpp.  This file only checks
+// a few things for .c inputs.
+
+// /Yc with a .c file should build a c pch file.
+// RUN: %clang_cl -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC %s
+// CHECK-YC: cc1
+// CHECK-YC: -emit-pch
+// CHECK-YC: -o
+// CHECK-YC: pchfile.pch
+// CHECK-YC: -x
+// CHECK-YC: "c"
+
+// But not if /TP changes the input language to C++.
+// RUN: %clang_cl /TP -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCTP %s
+// CHECK-YCTP: cc1
+// CHECK-YCTP: -emit-pch
+// CHECK-YCTP: -o
+// CHECK-YCTP: pchfile.pch
+// CHECK-YCTP: -x
+// CHECK-YCTP: "c++"
+
+// Except if a later /TC changes it back.
+// RUN: %clang_cl -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCTPTC %s
+// CHECK-YCTPTC: cc1
+// CHECK-YCTPTC: -emit-pch
+// CHECK-YCTPTC: -o
+// CHECK-YCTPTC: pchfile.pch
+// CHECK-YCTPTC: -x
+// CHECK-YCTPTC: "c"
+
+// Also check lower-case /Tp flag.
+// RUN: %clang_cl -Werror /Tp%s /Ycpchfile.h /FIpchfile.h /c -### 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCTp %s
+// CHECK-YCTp: cc1
+// CHECK-YCTp: -emit-pch
+// CHECK-YCTp: -o
+// CHECK-YCTp: pchfile.pch
+// CHECK-YCTp: -x
+// CHECK-YCTp: "c++"
Index: test/Driver/Inputs/pchfile.h
===================================================================
--- test/Driver/Inputs/pchfile.h
+++ test/Driver/Inputs/pchfile.h
@@ -0,0 +1,3 @@
+#if defined(ERR_HEADER)
+#error nope1
+#endif
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -386,9 +386,63 @@
   // wonky, but we include looking for .gch so we can support seamless
   // replacement into a build system already set up to be generating
   // .gch files.
+  int YcIndex = -1, YuIndex = -1;
+  {
+    int AI = -1;
+    const Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc);
+    const Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu);
+    for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) {
+      // Walk the whole i_Group and the skip non "-include" flags so that the
+      // index here matches the index in the next loop below.
+      ++AI;
+      if (!A->getOption().matches(options::OPT_include))
+        continue;
+      if (YcArg && strcmp(A->getValue(), YcArg->getValue()) == 0)
+        YcIndex = AI;
+      if (YuArg && strcmp(A->getValue(), YuArg->getValue()) == 0)
+        YuIndex = AI;
+    }
+  }
+
   bool RenderedImplicitInclude = false;
+  int AI = -1;
   for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) {
-    if (A->getOption().matches(options::OPT_include)) {
+    ++AI;
+
+    if (getToolChain().getDriver().IsCLMode()) {
+      // In clang-cl mode, /Ycfoo.h means that all code up to a foo.h 
+      // include is compiled into foo.h, and everything after goes into
+      // the .obj file. /Yufoo.h means that all includes prior to and including
+      // foo.h are completely skipped and replaced with a use of the pch file
+      // for foo.h.  (Each flag can have at most one value, multiple /Yc flags
+      // just mean that the last one wins.)  If /Yc and /Yu are both present
+      // and refer to the same file, /Yc wins.
+      // Note that OPT__SLASH_FI gets mapped to OPT_include.
+      // FIXME: The code here assumes that /Yc and /Yu refer to the same file.
+      // cl.exe seems to support both flags with different values, but that
+      // seems strange (which flag does /Fp now refer to?), so don't implement
+      // that until someone needs that.
+      int PchIndex = YcIndex != -1 ? YcIndex : YuIndex;
+      if (PchIndex != -1) {
+        if (isa<PrecompileJobAction>(JA)) {
+          // When building the pch, skip all includes after the pch.
+          assert(YcIndex != -1 && PchIndex == YcIndex);
+          if (AI >= YcIndex)
+            continue;
+        } else {
+          // When using the pch, skip all includes prior to the pch.
+          if (AI < PchIndex)
+            continue;
+          if (AI == PchIndex) {
+            A->claim();
+            CmdArgs.push_back("-include-pch");
+            CmdArgs.push_back(
+                Args.MakeArgString(D.GetClPchPath(C, A->getValue())));
+            continue;
+          }
+        }
+      }
+    } else if (A->getOption().matches(options::OPT_include)) {
       bool IsFirstImplicitInclude = !RenderedImplicitInclude;
       RenderedImplicitInclude = true;
 
Index: lib/Driver/Driver.cpp
===================================================================
--- lib/Driver/Driver.cpp
+++ lib/Driver/Driver.cpp
@@ -1439,6 +1439,58 @@
     }
   }
 
+  // Diagnose unsupported forms of /Yc /Yu. Ignore /Yc/Yu for now if:
+  // * no filename after it
+  // * both /Yc and /Yu passed but with different filenames
+  // * corresponding file not also passed as /FI
+  Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc);
+  Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu);
+  if (YcArg && YcArg->getValue()[0] == '\0') {
+    Diag(clang::diag::warn_drv_ycyu_no_arg_clang_cl) << YcArg->getSpelling();
+    Args.eraseArg(options::OPT__SLASH_Yc);
+    YcArg = nullptr;
+  }
+  if (YuArg && YuArg->getValue()[0] == '\0') {
+    Diag(clang::diag::warn_drv_ycyu_no_arg_clang_cl) << YuArg->getSpelling();
+    Args.eraseArg(options::OPT__SLASH_Yu);
+    YuArg = nullptr;
+  }
+  if (YcArg && YuArg && strcmp(YcArg->getValue(), YuArg->getValue()) != 0) {
+    Diag(clang::diag::warn_drv_ycyu_different_arg_clang_cl);
+    Args.eraseArg(options::OPT__SLASH_Yc);
+    Args.eraseArg(options::OPT__SLASH_Yu);
+    YcArg = YuArg = nullptr;
+  }
+  if (YcArg || YuArg) {
+    StringRef Val = YcArg ? YcArg->getValue() : YuArg->getValue();
+    bool Found = false;
+    for (const Arg *Inc : Args.filtered(options::OPT_include)) {
+      // FIXME: Do case-insensitive matching and consider / and \ as equal.
+      if (Inc->getValue() == Val)
+        Found = true;
+    }
+    if (!Found) {
+      Diag(clang::diag::warn_drv_ycyu_no_fi_arg_clang_cl)
+          << (YcArg ? YcArg : YuArg)->getSpelling();
+      Args.eraseArg(options::OPT__SLASH_Yc);
+      Args.eraseArg(options::OPT__SLASH_Yu);
+      YcArg = YuArg = nullptr;
+    }
+  }
+  if (YcArg && Inputs.size() > 1) {
+    Diag(clang::diag::warn_drv_yc_multiple_inputs_clang_cl);
+    Args.eraseArg(options::OPT__SLASH_Yc);
+    YcArg = nullptr;
+  }
+  if (Args.hasArg(options::OPT__SLASH_Y_)) {
+    // /Y- disables all pch handling.  Rather than check for it everywhere,
+    // just remove clang-cl pch-related flags here.
+    Args.eraseArg(options::OPT__SLASH_Fp);
+    Args.eraseArg(options::OPT__SLASH_Yc);
+    Args.eraseArg(options::OPT__SLASH_Yu);
+    YcArg = YuArg = nullptr;
+  }
+
   // Construct the actions to perform.
   ActionList LinkerInputs;
 
@@ -1450,6 +1502,23 @@
     PL.clear();
     types::getCompilationPhases(InputType, PL);
 
+    Action* ClangClPch = nullptr;
+    if (YcArg) {
+      // Add a separate precompile phase for the compile phase.
+      if (FinalPhase >= phases::Compile) {
+        llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> PCHPL;
+        types::getCompilationPhases(types::TY_CXXHeader, PCHPL);
+        Arg *InputArg = MakeInputArg(Args, Opts, YcArg->getValue());
+
+        // Build the pipeline for the pch file.
+        ClangClPch = C.MakeAction<InputAction>(*InputArg, InputType);
+        for (phases::ID Phase : PCHPL)
+          ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch);
+        assert(ClangClPch);
+        Actions.push_back(ClangClPch);
+      }
+    }
+
     // If the first step comes after the final phase we are doing as part of
     // this compilation, warn the user about it.
     phases::ID InitialPhase = PL[0];
@@ -1513,7 +1582,10 @@
         continue;
 
       // Otherwise construct the appropriate action.
-      Current = ConstructPhaseAction(C, Args, Phase, Current);
+      if (Phase == phases::Compile)
+        Current = ConstructPhaseAction(C, Args, Phase, Current, ClangClPch);
+      else
+        Current = ConstructPhaseAction(C, Args, Phase, Current);
 
       if (InputType == types::TY_CUDA && Phase == CudaInjectionPhase) {
         Current = buildCudaActions(C, Args, InputArg, Current, Actions);
@@ -1551,7 +1623,8 @@
 }
 
 Action *Driver::ConstructPhaseAction(Compilation &C, const ArgList &Args,
-                                     phases::ID Phase, Action *Input) const {
+                                     phases::ID Phase, Action *Input,
+                                     Action *ClangClPchInput) const {
   llvm::PrettyStackTraceString CrashInfo("Constructing phase actions");
   // Build the appropriate action.
   switch (Phase) {
@@ -1582,24 +1655,43 @@
     return C.MakeAction<PrecompileJobAction>(Input, OutputTy);
   }
   case phases::Compile: {
+    ActionList ImplicitInputs;
+
+    // Don't make the pch action an implicit input in /fallback builds, so that
+    // if the pch compilation fails, the main compilation with the fallback
+    // still runs.
+    if (ClangClPchInput && !Args.hasArg(options::OPT__SLASH_fallback))
+      ImplicitInputs.push_back(ClangClPchInput);
+
     if (Args.hasArg(options::OPT_fsyntax_only))
-      return C.MakeAction<CompileJobAction>(Input, types::TY_Nothing);
+      return C.MakeAction<CompileJobAction>(Input, ImplicitInputs,
+                                            types::TY_Nothing);
     if (Args.hasArg(options::OPT_rewrite_objc))
-      return C.MakeAction<CompileJobAction>(Input, types::TY_RewrittenObjC);
+      return C.MakeAction<CompileJobAction>(Input, ImplicitInputs,
+                                            types::TY_RewrittenObjC);
     if (Args.hasArg(options::OPT_rewrite_legacy_objc))
-      return C.MakeAction<CompileJobAction>(Input,
+      return C.MakeAction<CompileJobAction>(Input, ImplicitInputs,
                                             types::TY_RewrittenLegacyObjC);
-    if (Args.hasArg(options::OPT__analyze, options::OPT__analyze_auto))
+    if (Args.hasArg(options::OPT__analyze, options::OPT__analyze_auto)) {
+      assert(!ClangClPchInput);
       return C.MakeAction<AnalyzeJobAction>(Input, types::TY_Plist);
-    if (Args.hasArg(options::OPT__migrate))
+    }
+    if (Args.hasArg(options::OPT__migrate)) {
+      assert(!ClangClPchInput);
       return C.MakeAction<MigrateJobAction>(Input, types::TY_Remap);
+    }
     if (Args.hasArg(options::OPT_emit_ast))
-      return C.MakeAction<CompileJobAction>(Input, types::TY_AST);
+      return C.MakeAction<CompileJobAction>(Input, ImplicitInputs,
+                                            types::TY_AST);
     if (Args.hasArg(options::OPT_module_file_info))
-      return C.MakeAction<CompileJobAction>(Input, types::TY_ModuleFile);
-    if (Args.hasArg(options::OPT_verify_pch))
+      return C.MakeAction<CompileJobAction>(Input, ImplicitInputs,
+                                            types::TY_ModuleFile);
+    if (Args.hasArg(options::OPT_verify_pch)) {
+      assert(!ClangClPchInput);
       return C.MakeAction<VerifyPCHJobAction>(Input, types::TY_Nothing);
-    return C.MakeAction<CompileJobAction>(Input, types::TY_LLVM_BC);
+    }
+    return C.MakeAction<CompileJobAction>(Input, ImplicitInputs,
+                                          types::TY_LLVM_BC);
   }
   case phases::Backend: {
     if (isUsingLTO()) {
@@ -2085,6 +2177,8 @@
       NamedOutput = C.getArgs().MakeArgString(Output.c_str());
     } else
       NamedOutput = getDefaultImageName();
+  } else if (JA.getType() == types::TY_PCH && IsCLMode()) {
+    NamedOutput = C.getArgs().MakeArgString(GetClPchPath(C, BaseName).c_str());
   } else {
     const char *Suffix = types::getTypeTempSuffix(JA.getType(), IsCLMode());
     assert(Suffix && "All types used for output should have a suffix.");
@@ -2250,6 +2344,25 @@
   return Path.str();
 }
 
+std::string Driver::GetClPchPath(Compilation &C, StringRef BaseName) const {
+  SmallString<128> Output;
+  if (Arg *FpArg = C.getArgs().getLastArg(options::OPT__SLASH_Fp)) {
+    // FIXME: If anybody needs it, implement this obscure rule:
+    // "If you specify a directory without a file name, the default file name
+    // is VCx0.pch., where x is the major version of Visual C++ in use."
+    Output = FpArg->getValue();
+
+    // Add pch if needed: "If you do not specify an extension as part of the
+    // path name, an extension of .pch is assumed. "
+    if (!llvm::sys::path::has_extension(Output))
+      Output += ".pch";
+  } else {
+    Output = BaseName;
+    llvm::sys::path::replace_extension(Output, ".pch");
+  }
+  return Output.str();
+}
+
 const ToolChain &Driver::getToolChain(const ArgList &Args,
                                       const llvm::Triple &Target) const {
 
Index: lib/Driver/Compilation.cpp
===================================================================
--- lib/Driver/Compilation.cpp
+++ lib/Driver/Compilation.cpp
@@ -176,9 +176,13 @@
     if (A == &(CI->second->getSource()))
       return true;
 
+  // Check if any of the actions that A depends on failed.
   for (const Action *AI : A->inputs())
     if (ActionFailed(AI, FailingCommands))
       return true;
+  for (const Action *AI : A->implicit_inputs())
+    if (ActionFailed(AI, FailingCommands))
+      return true;
 
   return false;
 }
Index: lib/Driver/Action.cpp
===================================================================
--- lib/Driver/Action.cpp
+++ lib/Driver/Action.cpp
@@ -96,6 +96,10 @@
 JobAction::JobAction(ActionClass Kind, Action *Input, types::ID Type)
     : Action(Kind, Input, Type) {}
 
+JobAction::JobAction(ActionClass Kind, Action *Input,
+                     const ActionList &ImplicitInputs, types::ID Type)
+    : Action(Kind, Input, ImplicitInputs, Type) {}
+
 JobAction::JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type)
   : Action(Kind, Inputs, Type) {
 }
@@ -122,8 +126,10 @@
 
 void CompileJobAction::anchor() {}
 
-CompileJobAction::CompileJobAction(Action *Input, types::ID OutputType)
-    : JobAction(CompileJobClass, Input, OutputType) {}
+CompileJobAction::CompileJobAction(Action *Input,
+                                   const ActionList &ImplicitInputs,
+                                   types::ID OutputType)
+    : JobAction(CompileJobClass, Input, ImplicitInputs, OutputType) {}
 
 void BackendJobAction::anchor() {}
 
Index: include/clang/Driver/Driver.h
===================================================================
--- include/clang/Driver/Driver.h
+++ include/clang/Driver/Driver.h
@@ -375,7 +375,8 @@
   /// \p Phase on the \p Input, taking in to account arguments
   /// like -fsyntax-only or --analyze.
   Action *ConstructPhaseAction(Compilation &C, const llvm::opt::ArgList &Args,
-                               phases::ID Phase, Action *Input) const;
+                               phases::ID Phase, Action *Input,
+                               Action *ClangClPchInput = nullptr) const;
 
   /// BuildJobsForAction - Construct the jobs to perform for the action \p A and
   /// return an InputInfo for the result of running \p A.  Will only construct
@@ -414,6 +415,9 @@
   /// GCC goes to extra lengths here to be a bit more robust.
   std::string GetTemporaryPath(StringRef Prefix, const char *Suffix) const;
 
+  /// Return the pathname of the pch file in clang-cl mode.
+  std::string GetClPchPath(Compilation &C, StringRef BaseName) const;
+
   /// ShouldUseClangCompiler - Should the clang compiler be used to
   /// handle this action.
   bool ShouldUseClangCompiler(const JobAction &JA) const;
Index: include/clang/Driver/CLCompatOptions.td
===================================================================
--- include/clang/Driver/CLCompatOptions.td
+++ include/clang/Driver/CLCompatOptions.td
@@ -252,6 +252,11 @@
 def _SLASH_Zl : CLFlag<"Zl">,
   HelpText<"Don't mention any default libraries in the object file">;
 
+def _SLASH_Yc : CLJoined<"Yc">;
+def _SLASH_Y_ : CLFlag<"Y-">;
+def _SLASH_Yu : CLJoined<"Yu">;
+def _SLASH_Fp : CLJoined<"Fp">;
+
 // Ignored:
 
 def _SLASH_analyze_ : CLIgnoredFlag<"analyze-">;
@@ -294,7 +299,6 @@
 def _SLASH_FC : CLFlag<"FC">;
 def _SLASH_F : CLFlag<"F">;
 def _SLASH_Fm : CLJoined<"Fm">;
-def _SLASH_Fp : CLJoined<"Fp">;
 def _SLASH_Fr : CLJoined<"Fr">;
 def _SLASH_FR : CLJoined<"FR">;
 def _SLASH_FU : CLJoinedOrSeparate<"FU">;
@@ -332,11 +336,8 @@
 def _SLASH_WL : CLFlag<"WL">;
 def _SLASH_Wp64 : CLFlag<"Wp64">;
 def _SLASH_X : CLFlag<"X">;
-def _SLASH_Yc : CLJoined<"Yc">;
-def _SLASH_Y_ : CLFlag<"Y-">;
 def _SLASH_Yd : CLFlag<"Yd">;
 def _SLASH_Yl : CLJoined<"Yl">;
-def _SLASH_Yu : CLJoined<"Yu">;
 def _SLASH_Za : CLFlag<"Za">;
 def _SLASH_Zc : CLJoined<"Zc:">;
 def _SLASH_Ze : CLFlag<"Ze">;
Index: include/clang/Driver/Action.h
===================================================================
--- include/clang/Driver/Action.h
+++ include/clang/Driver/Action.h
@@ -78,10 +78,23 @@
 
   ActionList Inputs;
 
+  /// Inputs that this Action depends on, but that aren't explicitly passed
+  /// as arguments to the action.
+  ///
+  /// For example, `clang-cl /Yc /Fifoo.h foo.cc` will first compile foo.h into
+  /// a pch file, and then load the pch file when compiling foo.cc.  If building
+  /// the pch file fails, foo.cc should not be compiled, so the action building
+  /// the pch file is an implicit input of the compile action.
+  ActionList ImplicitInputs;
+
 protected:
   Action(ActionClass Kind, types::ID Type) : Action(Kind, ActionList(), Type) {}
   Action(ActionClass Kind, Action *Input, types::ID Type)
       : Action(Kind, ActionList({Input}), Type) {}
+  Action(ActionClass Kind, Action *Input, const ActionList &ImplicitInputs,
+         types::ID Type)
+      : Kind(Kind), Type(Type), Inputs({Input}),
+        ImplicitInputs(ImplicitInputs) {}
   Action(ActionClass Kind, Action *Input)
       : Action(Kind, ActionList({Input}), Input->getType()) {}
   Action(ActionClass Kind, const ActionList &Inputs, types::ID Type)
@@ -108,6 +121,16 @@
   input_const_range inputs() const {
     return input_const_range(input_begin(), input_end());
   }
+
+  input_const_iterator implicit_input_begin() const {
+    return ImplicitInputs.begin();
+  }
+  input_const_iterator implicit_input_end() const {
+    return ImplicitInputs.end();
+  }
+  input_const_range implicit_inputs() const {
+    return input_const_range(implicit_input_begin(), implicit_input_end());
+  }
 };
 
 class InputAction : public Action {
@@ -183,6 +206,8 @@
   virtual void anchor();
 protected:
   JobAction(ActionClass Kind, Action *Input, types::ID Type);
+  JobAction(ActionClass Kind, Action *Input, const ActionList &ImplicitInputs,
+            types::ID Type);
   JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type);
 
 public:
@@ -235,7 +260,8 @@
 class CompileJobAction : public JobAction {
   void anchor() override;
 public:
-  CompileJobAction(Action *Input, types::ID OutputType);
+  CompileJobAction(Action *Input, const ActionList &ImplicitInputs,
+                   types::ID OutputType);
 
   static bool classof(const Action *A) {
     return A->getKind() == CompileJobClass;
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -785,6 +785,8 @@
      MicrosoftConstInit, MicrosoftVoidPseudoDtor, MicrosoftAnonTag,
      MicrosoftCommentPaste, MicrosoftEndOfFile]>;
 
+def ClangClPch : DiagGroup<"clang-cl-pch">;
+
 def ObjCNonUnifiedException : DiagGroup<"objc-nonunified-exceptions">;
 
 def ObjCProtocolMethodImpl : DiagGroup<"objc-protocol-method-implementation">;
Index: include/clang/Basic/DiagnosticDriverKinds.td
===================================================================
--- include/clang/Basic/DiagnosticDriverKinds.td
+++ include/clang/Basic/DiagnosticDriverKinds.td
@@ -98,6 +98,20 @@
 def warn_drv_unknown_argument_clang_cl : Warning<
   "unknown argument ignored in clang-cl: '%0'">,
   InGroup<UnknownArgument>;
+
+def warn_drv_ycyu_no_arg_clang_cl : Warning<
+  "support for '%0' without a filename not implemented yet, ignoring flag">,
+  InGroup<ClangClPch>;
+def warn_drv_ycyu_different_arg_clang_cl : Warning<
+  "support for '/Yc' and '/Yu' with different filenames not implemented yet, ignoring flags">,
+  InGroup<ClangClPch>;
+def warn_drv_ycyu_no_fi_arg_clang_cl : Warning<
+  "support for '%0' without a corresponding /FI flag not implemented yet, ignoring flag">,
+  InGroup<ClangClPch>;
+def warn_drv_yc_multiple_inputs_clang_cl : Warning<
+  "support for '/Yc' with more than one input source file not implemented yet, ignoring flag">,
+  InGroup<ClangClPch>;
+
 def err_drv_invalid_value : Error<"invalid value '%1' in '%0'">;
 def err_drv_invalid_int_value : Error<"invalid integral value '%1' in '%0'">;
 def err_drv_invalid_remap_file : Error<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to