diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -152,6 +152,9 @@ /// The version string to put into coverage files. char CoverageVersion[4]; + /// The directory all source files are relative to. + std::string SourceDir; + /// Enable additional debugging information. std::string DebugPass; 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 @@ -857,6 +857,13 @@ Group, Flags<[CC1Option, CoreOption]>, HelpText<"Generate instrumented code to collect order file into default.profraw file (overridden by '=' form of option or LLVM_PROFILE_FILE env var)">; +def fsource_dir : Separate<["-"], "fsource-dir">, + Group, Flags<[CC1Option, CC1AsOption, CoreOption]>, + HelpText<"The directory all source files are relative to.">; +def fsource_dir_EQ : Joined<["-"], "fsource-dir=">, + Group, Flags<[CC1Option, CC1AsOption, CoreOption]>, + Alias; + defm addrsig : OptInFFlag<"addrsig", "Emit", "Don't emit", " an address-significance table", [CoreOption]>; defm blocks : OptInFFlag<"blocks", "Enable the 'blocks' language feature", "", "", [CoreOption]>; def fbootclasspath_EQ : Joined<["-"], "fbootclasspath=">, Group; diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h --- a/clang/include/clang/Lex/PreprocessorOptions.h +++ b/clang/include/clang/Lex/PreprocessorOptions.h @@ -175,6 +175,9 @@ /// build it again. std::shared_ptr FailedModules; + /// The directory all source files are relative to. + std::string SourceDir; + /// A prefix map for __FILE__ and __BASE_FILE__. std::map> MacroPrefixMap; diff --git a/clang/lib/CodeGen/CoverageMappingGen.h b/clang/lib/CodeGen/CoverageMappingGen.h --- a/clang/lib/CodeGen/CoverageMappingGen.h +++ b/clang/lib/CodeGen/CoverageMappingGen.h @@ -87,6 +87,8 @@ std::vector FunctionNames; std::vector FunctionRecords; + std::string normalizeFilename(StringRef Filename); + /// Emit a function record. void emitFunctionMappingRecord(const FunctionInfo &Info, uint64_t FilenamesRef); diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -1354,13 +1354,6 @@ } }; -std::string normalizeFilename(StringRef Filename) { - llvm::SmallString<256> Path(Filename); - llvm::sys::fs::make_absolute(Path); - llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); - return std::string(Path); -} - } // end anonymous namespace static void dump(llvm::raw_ostream &OS, StringRef FunctionName, @@ -1399,6 +1392,15 @@ SK, CGM.getContext().getTargetInfo().getTriple().getObjectFormat()); } +std::string CoverageMappingModuleGen::normalizeFilename(StringRef Filename) { + llvm::SmallString<256> Path(Filename); + llvm::sys::fs::make_absolute(Path); + llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + if (!CGM.getCodeGenOpts().SourceDir.empty()) + llvm::sys::path::make_relative(Path, CGM.getCodeGenOpts().SourceDir, Path); + return std::string(Path); +} + void CoverageMappingModuleGen::emitFunctionMappingRecord( const FunctionInfo &Info, uint64_t FilenamesRef) { llvm::LLVMContext &Ctx = CGM.getLLVMContext(); 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 @@ -5107,6 +5107,11 @@ if (!ShouldEnableAutolink(Args, TC, JA)) CmdArgs.push_back("-fno-autolink"); + if (Arg *A = Args.getLastArg(options::OPT_fsource_dir)) { + CmdArgs.push_back("-fsource-dir"); + CmdArgs.push_back(A->getValue()); + } + // Add in -fdebug-compilation-dir if necessary. addDebugCompDirArg(Args, CmdArgs, D.getVFS()); 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 @@ -1181,8 +1181,16 @@ } Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations); + + SmallString<128> P(Args.getLastArgValue(OPT_fsource_dir)); + if (!P.empty() && !llvm::sys::path::is_absolute(P)) + llvm::sys::fs::make_absolute(P); + llvm::sys::path::remove_dots(P, /*remove_dot_dot=*/true); + Opts.SourceDir = std::string(P.str()); + Opts.DebugCompilationDir = std::string(Args.getLastArgValue(OPT_fdebug_compilation_dir)); + for (auto *A : Args.filtered(OPT_mlink_bitcode_file, OPT_mlink_builtin_bitcode)) { CodeGenOptions::BitcodeFileToLink F; @@ -3549,6 +3557,12 @@ for (const auto *A : Args.filtered(OPT_error_on_deserialized_pch_decl)) Opts.DeserializedPCHDeclsToErrorOn.insert(A->getValue()); + SmallString<128> P(Args.getLastArgValue(OPT_fsource_dir)); + if (!P.empty() && !llvm::sys::path::is_absolute(P)) + llvm::sys::fs::make_absolute(P); + llvm::sys::path::remove_dots(P, /*remove_dot_dot=*/true); + Opts.SourceDir = std::string(P.str()); + for (const auto &A : Args.getAllArgValues(OPT_fmacro_prefix_map_EQ)) { auto Split = StringRef(A).split('='); Opts.MacroPrefixMap.insert( 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 @@ -1542,6 +1542,8 @@ FN += PLoc.getFilename(); } remapMacroPath(FN, PPOpts->MacroPrefixMap); + if (!PPOpts->SourceDir.empty()) + llvm::sys::path::make_relative(FN, PPOpts->SourceDir, FN); Lexer::Stringify(FN); OS << '"' << FN << '"'; } diff --git a/clang/test/CoverageMapping/source_dir.c b/clang/test/CoverageMapping/source_dir.c new file mode 100644 --- /dev/null +++ b/clang/test/CoverageMapping/source_dir.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm %s -o - | FileCheck -check-prefix=ABSPATH %s + +// ABSPATH: @__llvm_coverage_mapping = {{.*}}"\01 +// ABSPATH: {{[/\\].*(/|\\\\)test(/|\\\\)CoverageMapping(/|\\\\)source_dir}}.c +// ABSPATH: " + +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm -fsource-dir %S %s -o - | FileCheck -check-prefix=RELPATH %s + +// RELPATH: @__llvm_coverage_mapping = {{.*}}"\01 +// RELPATH: source_dir.c +// RELPATH: " + +void f1() {} 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,7 @@ // 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 -fsource-dir %S -c -o - %s | FileCheck %s --check-prefix CHECK-RELATIVE filename: __FILE__ #include "Inputs/include-file-test/file_test.h" @@ -21,3 +22,8 @@ // CHECK-REMOVE: filename: "Inputs/include-file-test/file_test.h" // CHECK-REMOVE: basefile: "file_test.c" // CHECK-REMOVE-NOT: filename: + +// CHECK-RELATIVE: filename: "file_test.c" +// CHECK-RELATIVE: filename: "Inputs/include-file-test/file_test.h" +// CHECK-RELATIVE: basefile: "file_test.c" +// CHECK-RELATIVE-NOT: filename: diff --git a/llvm/include/llvm/Support/Path.h b/llvm/include/llvm/Support/Path.h --- a/llvm/include/llvm/Support/Path.h +++ b/llvm/include/llvm/Support/Path.h @@ -476,6 +476,10 @@ bool remove_dots(SmallVectorImpl &path, bool remove_dot_dot = false, Style style = Style::native); +/// Return a path that when appeneded to from resolves to the same as to. +void make_relative(StringRef from, StringRef to, SmallVectorImpl &result, + Style style = Style::native); + } // end namespace path } // end namespace sys } // end namespace llvm diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp --- a/llvm/lib/Support/Path.cpp +++ b/llvm/lib/Support/Path.cpp @@ -763,6 +763,22 @@ return true; } +void make_relative(StringRef from, StringRef to, SmallVectorImpl &result, + Style style) { + SmallString<256> buffer; + auto fromIt = begin(from, style), fromE = end(from); + auto toIt = begin(to, style), toE = end(to); + // Find a common base. + for (; fromIt != fromE && toIt != toE && *fromIt == *toIt; ++fromIt, ++toIt) + ; + // Navigate backwards to the base. + for (; toIt != toE; ++toIt) + if (*toIt != ".") + append(buffer, style, ".."); + append(buffer, fromIt, fromE, style); + result.swap(buffer); +} + } // end namespace path namespace fs { diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp --- a/llvm/unittests/Support/Path.cpp +++ b/llvm/unittests/Support/Path.cpp @@ -1521,6 +1521,35 @@ EXPECT_EQ(Path, "C:\\old/foo\\bar"); } +static std::string make_relative(StringRef from, StringRef to, + path::Style style) { + SmallString<256> buffer; + path::make_relative(from, to, buffer, style); + return std::string(buffer.str()); +} + +TEST(Support, MakeRelative) { + EXPECT_EQ("", make_relative("/a/b/c/d", "/a/b/c/d", path::Style::posix)); + EXPECT_EQ("d", make_relative("/a/b/c/d", "/a/b/c", path::Style::posix)); + EXPECT_EQ("c/d", make_relative("/a/b/c/d", "/a/b", path::Style::posix)); + EXPECT_EQ("../../c/d", + make_relative("/a/b/c/d", "/a/b/e/f", path::Style::posix)); + EXPECT_EQ("../../../../a/b/c/d", + make_relative("/a/b/c/d", "/e/f/g/h", path::Style::posix)); + + EXPECT_EQ("", make_relative("C:\\a\\b\\c\\d", "C:\\a\\b\\c\\d", + path::Style::windows)); + EXPECT_EQ("d", make_relative("C:\\a\\b\\c\\d", "C:\\a\\b\\c", + path::Style::windows)); + EXPECT_EQ("c\\d", + make_relative("C:\\a\\b\\c\\d", "C:\\a\\b", path::Style::windows)); + EXPECT_EQ("..\\..\\c\\d", make_relative("C:\\a\\b\\c\\d", "C:\\a\\b\\e\\f", + path::Style::windows)); + EXPECT_EQ( + "..\\..\\..\\..\\a\\b\\c\\d", + make_relative("C:\\a\\b\\c\\d", "C:\\e\\f\\g\\h", path::Style::windows)); +} + TEST_F(FileSystemTest, OpenFileForRead) { // Create a temp file. int FileDescriptor;