diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -452,6 +452,14 @@ /// The seed used by the randomize structure layout feature. std::string RandstructSeed; + /// Indicates whether the __FILE__ macro should use the target's + /// platform-specific file separator or whether it should use the build + /// environment's platform-specific file separator. + /// + /// The plaform-specific path separator is the backslash(\) for Windows and + /// forward slash (/) elsewhere. + bool UseTargetPathSeparator = false; + LangOptions(); /// Set language defaults for the given input language and @@ -577,7 +585,7 @@ bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; } /// Remap path prefix according to -fmacro-prefix-path option. - void remapPathPrefix(SmallString<256> &Path) const; + void remapPathPrefix(SmallVectorImpl &Path) const; }; /// Floating point control options diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1507,6 +1507,14 @@ def : Flag<["-"], "fno-extended-identifiers">, Group, Flags<[Unsupported]>; def fhosted : Flag<["-"], "fhosted">, Group; def fdenormal_fp_math_EQ : Joined<["-"], "fdenormal-fp-math=">, Group, Flags<[CC1Option]>; +def ffile_reproducible : Flag<["-"], "ffile-reproducible">, Group, + Flags<[CoreOption, CC1Option]>, + HelpText<"Use the target's platform-specific path separator character when " + "expanding the __FILE__ macro">; +def fno_file_reproducible : Flag<["-"], "fno-file-reproducible">, + Group, Flags<[CoreOption, CC1Option]>, + HelpText<"Use the host's platform-specific path separator character when " + "expanding the __FILE__ macro">; def ffp_eval_method_EQ : Joined<["-"], "ffp-eval-method=">, Group, Flags<[CC1Option]>, HelpText<"Specifies the evaluation method to use for floating-point arithmetic.">, Values<"source,double,extended">, NormalizedValuesScope<"LangOptions">, @@ -3009,10 +3017,12 @@ HelpText<"remap file source paths in coverage mapping">; def ffile_prefix_map_EQ : Joined<["-"], "ffile-prefix-map=">, Group, - HelpText<"remap file source paths in debug info, predefined preprocessor macros and __builtin_FILE()">; + HelpText<"remap file source paths in debug info, predefined preprocessor " + "macros and __builtin_FILE(). Implies -ffile-reproducible.">; def fmacro_prefix_map_EQ : Joined<["-"], "fmacro-prefix-map=">, Group, Flags<[CC1Option]>, - HelpText<"remap file source paths in predefined preprocessor macros and __builtin_FILE()">; + HelpText<"remap file source paths in predefined preprocessor macros and " + "__builtin_FILE(). Implies -ffile-reproducible.">; defm force_dwarf_frame : BoolFOption<"force-dwarf-frame", CodeGenOpts<"ForceDwarfFrameSection">, DefaultFalse, PosFlag, NegFlag>; diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -2577,6 +2577,10 @@ emitRestrictExpansionWarning(Identifier); } + static void processPathForFileMacro(SmallVectorImpl &Path, + const LangOptions &LangOpts, + const TargetInfo &TI); + private: void emitMacroDeprecationWarning(const Token &Identifier) const; void emitRestrictExpansionWarning(const Token &Identifier) const; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -31,6 +31,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/LiteralSupport.h" +#include "clang/Lex/Preprocessor.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" @@ -2189,7 +2190,8 @@ switch (getIdentKind()) { case SourceLocExpr::File: { SmallString<256> Path(PLoc.getFilename()); - Ctx.getLangOpts().remapPathPrefix(Path); + clang::Preprocessor::processPathForFileMacro(Path, Ctx.getLangOpts(), + Ctx.getTargetInfo()); return MakeStringLiteral(Path); } case SourceLocExpr::Function: { @@ -2222,7 +2224,8 @@ StringRef Name = F->getName(); if (Name == "_M_file_name") { SmallString<256> Path(PLoc.getFilename()); - Ctx.getLangOpts().remapPathPrefix(Path); + clang::Preprocessor::processPathForFileMacro(Path, Ctx.getLangOpts(), + Ctx.getTargetInfo()); Value.getStructField(F->getFieldIndex()) = MakeStringLiteral(Path); } else if (Name == "_M_function_name") { // Note: this emits the PrettyFunction name -- different than what diff --git a/clang/lib/Basic/LangOptions.cpp b/clang/lib/Basic/LangOptions.cpp --- a/clang/lib/Basic/LangOptions.cpp +++ b/clang/lib/Basic/LangOptions.cpp @@ -62,7 +62,7 @@ llvm_unreachable("Unknown OpenCL version"); } -void LangOptions::remapPathPrefix(SmallString<256> &Path) const { +void LangOptions::remapPathPrefix(SmallVectorImpl &Path) const { for (const auto &Entry : MacroPrefixMap) if (llvm::sys::path::replace_path_prefix(Path, Entry.first, Entry.second)) break; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1485,6 +1485,9 @@ addMacroPrefixMapArg(D, Args, CmdArgs); addCoveragePrefixMapArg(D, Args, CmdArgs); + + Args.AddLastArg(CmdArgs, options::OPT_ffile_reproducible, + options::OPT_fno_file_reproducible); } // FIXME: Move to target hook. diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3541,6 +3541,11 @@ else GenerateArg(Args, OPT_fno_experimental_relative_cxx_abi_vtables, SA); + if (Opts.UseTargetPathSeparator) + GenerateArg(Args, OPT_ffile_reproducible, SA); + else + GenerateArg(Args, OPT_fno_file_reproducible, SA); + for (const auto &MP : Opts.MacroPrefixMap) GenerateArg(Args, OPT_fmacro_prefix_map_EQ, MP.first + "=" + MP.second, SA); @@ -4072,6 +4077,12 @@ {std::string(Split.first), std::string(Split.second)}); } + Opts.UseTargetPathSeparator = + !Args.getLastArg(OPT_fno_file_reproducible) && + (Args.getLastArg(OPT_ffile_compilation_dir_EQ) || + Args.getLastArg(OPT_fmacro_prefix_map_EQ) || + Args.getLastArg(OPT_ffile_reproducible)); + // Error if -mvscale-min is unbounded. if (Arg *A = Args.getLastArg(options::OPT_mvscale_min_EQ)) { unsigned VScaleMin; diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1511,7 +1511,7 @@ } else { FN += PLoc.getFilename(); } - getLangOpts().remapPathPrefix(FN); + processPathForFileMacro(FN, getLangOpts(), getTargetInfo()); Lexer::Stringify(FN); OS << '"' << FN << '"'; } @@ -1886,3 +1886,16 @@ WarnUnusedMacroLocs.erase(MI->getDefinitionLoc()); MI->setIsUsed(true); } + +void Preprocessor::processPathForFileMacro(SmallVectorImpl &Path, + const LangOptions &LangOpts, + const TargetInfo &TI) { + LangOpts.remapPathPrefix(Path); + if (LangOpts.UseTargetPathSeparator) { + if (TI.getTriple().isOSWindows()) + llvm::sys::path::make_preferred( + Path, llvm::sys::path::Style::windows_backslash); + else + llvm::sys::path::make_preferred(Path, llvm::sys::path::Style::posix); + } +} diff --git a/clang/test/CodeGenCXX/builtin-source-location.cpp b/clang/test/CodeGenCXX/builtin-source-location.cpp --- a/clang/test/CodeGenCXX/builtin-source-location.cpp +++ b/clang/test/CodeGenCXX/builtin-source-location.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -no-opaque-pointers -std=c++2a -fblocks %s -triple x86_64-unknown-unknown -emit-llvm -o %t.ll // This needs to be performed before #line directives which alter filename -// RUN: %clang_cc1 -no-opaque-pointers -fmacro-prefix-map=%p=/UNLIKELY/PATH -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-PREFIX-MAP +// RUN: %clang_cc1 -no-opaque-pointers -fno-file-reproducible -fmacro-prefix-map=%p=/UNLIKELY/PATH -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-PREFIX-MAP // // CHECK-PREFIX-MAP: /UNLIKELY/PATH{{/|\\\\}}builtin-source-location.cpp void testRemap() { diff --git a/clang/test/Preprocessor/file_test.c b/clang/test/Preprocessor/file_test.c --- a/clang/test/Preprocessor/file_test.c +++ b/clang/test/Preprocessor/file_test.c @@ -3,6 +3,11 @@ // RUN: %clang -E -fmacro-prefix-map=%p=/UNLIKELY_PATH/empty -c -o - %s | FileCheck %s // RUN: %clang -E -fmacro-prefix-map=%p=/UNLIKELY_PATH=empty -c -o - %s | FileCheck %s -check-prefix CHECK-EVIL // RUN: %clang -E -fmacro-prefix-map=%p/= -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE +// RUN: %clang -E -fno-file-reproducible -fmacro-prefix-map=%p/= -target x86_64-pc-win32 -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE +// RUN: %clang -E -fno-file-reproducible -ffile-prefix-map=%p/= -target x86_64-pc-win32 -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE +// RUN: %clang -E -fmacro-prefix-map=%p/= -target x86_64-pc-win32 -c -o - %s | FileCheck %s --check-prefix CHECK-WINDOWS +// RUN: %clang -E -ffile-prefix-map=%p/= -target x86_64-pc-win32 -c -o - %s | FileCheck %s --check-prefix CHECK-WINDOWS +// RUN: %clang -E -ffile-reproducible -target x86_64-pc-win32 -c -o - %s | FileCheck %s --check-prefix CHECK-WINDOWS-REPRODUCIBLE filename: __FILE__ #include "Inputs/include-file-test/file_test.h" @@ -21,3 +26,13 @@ // CHECK-REMOVE: filename: "Inputs/include-file-test/file_test.h" // CHECK-REMOVE: basefile: "file_test.c" // CHECK-REMOVE-NOT: filename: + +// CHECK-WINDOWS: filename: "file_test.c" +// CHECK-WINDOWS: filename: "Inputs\\include-file-test\\file_test.h" +// CHECK-WINDOWS: basefile: "file_test.c" +// CHECK-WINDOWS-NOT: filename: + +// CHECK-WINDOWS-REPRODUCIBLE: filename: "{{[^/]*}}file_test.c" +// CHECK-WINDOWS-REPRODUCIBLE: filename: "{{[^/]*}}Inputs\\include-file-test\\file_test.h" +// CHECK-WINDOWS-REPRODUCIBLE: basefile: "{{[^/]*}}file_test.c" +// CHECK-WINDOWS-REPRODUCIBLE-NOT: filename: diff --git a/clang/test/Preprocessor/file_test_windows.c b/clang/test/Preprocessor/file_test_windows.c --- a/clang/test/Preprocessor/file_test_windows.c +++ b/clang/test/Preprocessor/file_test_windows.c @@ -1,9 +1,25 @@ // REQUIRES: system-windows -// RUN: %clang -E -ffile-prefix-map=%p=A:\UNLIKELY_PATH\empty -c -o - %s | FileCheck %s -// RUN: %clang -E -fmacro-prefix-map=%p=A:\UNLIKELY_PATH\empty -c -o - %s | FileCheck %s -// RUN: %clang -E -fmacro-prefix-map=%p=A:\UNLIKELY_PATH=empty -c -o - %s | FileCheck %s -check-prefix CHECK-EVIL -// RUN: %clang -E -fmacro-prefix-map=%p/iNPUTS\=A:\UNLIKELY_PATH_INC\ -fmacro-prefix-map=%p/=A:\UNLIKELY_PATH_BASE\ -c -o - %s | FileCheck %s -check-prefix CHECK-CASE -// RUN: %clang -E -fmacro-prefix-map=%p\= -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE +// RUN: %clang -E -fno-file-reproducible -ffile-prefix-map=%p=A:\UNLIKELY_PATH\empty -c -o - %s | FileCheck %s +// RUN: %clang -E -fno-file-reproducible -fmacro-prefix-map=%p=A:\UNLIKELY_PATH\empty -c -o - %s | FileCheck %s +// RUN: %clang -E -fno-file-reproducible -fmacro-prefix-map=%p=A:\UNLIKELY_PATH=empty -c -o - %s | FileCheck %s -check-prefix CHECK-EVIL +// RUN: %clang -E -fno-file-reproducible -fmacro-prefix-map=%p/iNPUTS\=A:\UNLIKELY_PATH_INC\ -fmacro-prefix-map=%p/=A:\UNLIKELY_PATH_BASE\ -c -o - %s | FileCheck %s -check-prefix CHECK-CASE +// RUN: %clang -E -fno-file-reproducible -fmacro-prefix-map=%p\= -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE + +// RUN: %clang -E -ffile-prefix-map=%p=A:\UNLIKELY_PATH\empty -c -o - %s | FileCheck %s --check-prefix CHECK-REPRODUCIBLE +// RUN: %clang -E -fmacro-prefix-map=%p=A:\UNLIKELY_PATH\empty -c -o - %s | FileCheck %s --check-prefix CHECK-REPRODUCIBLE +// RUN: %clang -E -fmacro-prefix-map=%p=A:\UNLIKELY_PATH=empty -c -o - %s | FileCheck %s -check-prefix CHECK-EVIL-REPRODUCIBLE +// RUN: %clang -E -fmacro-prefix-map=%p/iNPUTS\=A:\UNLIKELY_PATH_INC\ -fmacro-prefix-map=%p/=A:\UNLIKELY_PATH_BASE\ -c -o - %s | FileCheck %s -check-prefix CHECK-CASE-REPRODUCIBLE +// RUN: %clang -E -fmacro-prefix-map=%p\= -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE-REPRODUCIBLE + +// RUN: %clang -E -target x86_64-pc-linux-gnu -fmacro-prefix-map=%p\= -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE +// RUN: %clang -E -target x86_64-pc-linux-gnu -ffile-prefix-map=%p\= -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE + +// Clang defaults to forward slashes for the non-prefix portion of the path even if the build environment is Windows. +// RUN: %clang -E -fno-file-reproducible -target x86_64-pc-linux-gnu -fmacro-prefix-map=%p\= -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE +// RUN: %clang -E -fno-file-reproducible -target x86_64-pc-linux-gnu -ffile-prefix-map=%p\= -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE + +// RUN: %clang -E -ffile-reproducible -target x86_64-pc-win32 -c -o - %s | FileCheck %s --check-prefix CHECK-WINDOWS-FULL +// RUN: %clang -E -ffile-reproducible -target x86_64-pc-linux-gnu -c -o - %s | FileCheck %s --check-prefix CHECK-LINUX-FULL filename: __FILE__ #include "Inputs/include-file-test/file_test.h" @@ -27,3 +43,34 @@ // CHECK-REMOVE: filename: "Inputs/include-file-test/file_test.h" // CHECK-REMOVE: basefile: "file_test_windows.c" // CHECK-REMOVE-NOT: filename: + +// CHECK-REPRODUCIBLE: filename: "A:\\UNLIKELY_PATH\\empty\\file_test_windows.c" +// CHECK-REPRODUCIBLE: filename: "A:\\UNLIKELY_PATH\\empty\\Inputs\\include-file-test\\file_test.h" +// CHECK-REPRODUCIBLE: basefile: "A:\\UNLIKELY_PATH\\empty\\file_test_windows.c" +// CHECK-REPRODUCIBLE-NOT: filename: + +// CHECK-EVIL-REPRODUCIBLE: filename: "A:\\UNLIKELY_PATH=empty\\file_test_windows.c" +// CHECK-EVIL-REPRODUCIBLE: filename: "A:\\UNLIKELY_PATH=empty\\Inputs\\include-file-test\\file_test.h" +// CHECK-EVIL-REPRODUCIBLE: basefile: "A:\\UNLIKELY_PATH=empty\\file_test_windows.c" +// CHECK-EVIL-REPRODUCIBLE-NOT: filename: + +// CHECK-CASE-REPRODUCIBLE: filename: "A:\\UNLIKELY_PATH_BASE\\file_test_windows.c" +// CHECK-CASE-REPRODUCIBLE: filename: "A:\\UNLIKELY_PATH_INC\\include-file-test\\file_test.h" +// CHECK-CASE-REPRODUCIBLE: basefile: "A:\\UNLIKELY_PATH_BASE\\file_test_windows.c" +// CHECK-CASE-REPRODUCIBLE-NOT: filename: + +// CHECK-REMOVE-REPRODUCIBLE: filename: "file_test_windows.c" +// CHECK-REMOVE-REPRODUCIBLE: filename: "Inputs\\include-file-test\\file_test.h" +// CHECK-REMOVE-REPRODUCIBLE: basefile: "file_test_windows.c" +// CHECK-REMOVE-REPRODUCIBLE-NOT: filename: + +// CHECK-WINDOWS-FULL: filename: "{{[^/]*}}file_test_windows.c" +// CHECK-WINDOWS-FULL: filename: "{{[^/]*}}Inputs\\include-file-test\\file_test.h" +// CHECK-WINDOWS-FULL: basefile: "{{[^/]*}}file_test_windows.c" +// CHECK-WINDOWS-FULL-NOT: filename: + +// Clang does not modify the prefix for POSIX style, so it may have backslashes. +// CHECK-LINUX-FULL: filename: "{{.*}}file_test_windows.c" +// CHECK-LINUX-FULL: filename: "{{.*}}Inputs/include-file-test/file_test.h" +// CHECK-LINUX-FULL: basefile: "{{.*}}file_test_windows.c" +// CHECK-LINUX-FULL-NOT: filename: