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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits