https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/235400
>From de7f66235b5c0923b94c66b617bff3b14da54f65 Mon Sep 17 00:00:00 2001 From: Fangrui Song <[email protected]> Date: Sat, 24 Oct 2026 21:24:00 -0700 Subject: [PATCH 1/2] [Tooling] Resolve relative "directory" against the compilation database JSONCompilationDatabase does not resolve a relative "directory", e.g. `{"directory": ".", "command": "cc ../src/a.c", "file": "../src/a.c"}`. getCompileCommands finds nothing, so clang-check reports "Compile command not found" and clangd uses a fallback command. getAllCompileCommands returns the directory verbatim, so each consumer resolves it against its own working directory. Resolve it against the directory containing compile_commands.json, after following symlinks, so that a compile_commands.json symlinked from the source root into the build directory keeps its meaning. Since loadFromBuffer has no file path, it takes the base directory as a new first parameter. LLM-aided --- .../clangd/GlobalCompilationDatabase.cpp | 18 +++-- clang/docs/JSONCompilationDatabase.md | 1 + clang/docs/ReleaseNotes.md | 2 + .../clang/Tooling/JSONCompilationDatabase.h | 27 +++++--- clang/lib/Tooling/JSONCompilationDatabase.cpp | 37 ++++++---- .../Tooling/CompilationDatabaseTest.cpp | 68 +++++++++++++++++-- clang/unittests/Tooling/ToolingTest.cpp | 2 +- 7 files changed, 120 insertions(+), 35 deletions(-) diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp index adb771ecbbaada..7e12a040345a17 100644 --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp @@ -247,9 +247,15 @@ DirectoryBasedGlobalCompilationDatabase::DirectoryCache::CachedFile::load( // Adapt CDB-loading functions to a common interface for DirectoryCache::load(). static std::unique_ptr<tooling::CompilationDatabase> -parseJSON(PathRef Path, llvm::StringRef Data, std::string &Error) { +parseJSON(PathRef Path, llvm::StringRef Data, llvm::vfs::FileSystem &FS, + std::string &Error) { + llvm::SmallString<256> RealPath; + llvm::StringRef BaseDirectory; + if (!FS.getRealPath(Path, RealPath)) + BaseDirectory = llvm::sys::path::parent_path(RealPath); if (auto CDB = tooling::JSONCompilationDatabase::loadFromBuffer( - Data, Error, tooling::JSONCommandLineSyntax::AutoDetect)) { + BaseDirectory, Data, Error, + tooling::JSONCommandLineSyntax::AutoDetect)) { // FS used for expanding response files. // FIXME: ExpandResponseFilesDatabase appears not to provide the usual // thread-safety guarantees, as the access to FS is not locked! @@ -264,7 +270,8 @@ parseJSON(PathRef Path, llvm::StringRef Data, std::string &Error) { return nullptr; } static std::unique_ptr<tooling::CompilationDatabase> -parseFixed(PathRef Path, llvm::StringRef Data, std::string &Error) { +parseFixed(PathRef Path, llvm::StringRef Data, llvm::vfs::FileSystem &, + std::string &Error) { return tooling::FixedCompilationDatabase::loadFromBuffer( llvm::sys::path::parent_path(Path), Data, Error); } @@ -283,7 +290,7 @@ bool DirectoryBasedGlobalCompilationDatabase::DirectoryCache::load( // Wrapper for {Fixed,JSON}CompilationDatabase::loadFromBuffer. std::unique_ptr<tooling::CompilationDatabase> (*Parser)( PathRef, - /*Data*/ llvm::StringRef, + /*Data*/ llvm::StringRef, llvm::vfs::FileSystem &, /*ErrorMsg*/ std::string &); }; for (const auto &Entry : {CDBFile{&CompileCommandsJson, parseJSON}, @@ -309,7 +316,8 @@ bool DirectoryBasedGlobalCompilationDatabase::DirectoryCache::load( return true; case CachedFile::LoadResult::FoundNewData: // We have a new CDB! - CDB = Entry.Parser(Entry.File->Path, Loaded.Buffer->getBuffer(), Error); + CDB = + Entry.Parser(Entry.File->Path, Loaded.Buffer->getBuffer(), FS, Error); if (CDB) log("{0} compilation database from {1}", Active ? "Reloaded" : "Loaded", Entry.File->Path); diff --git a/clang/docs/JSONCompilationDatabase.md b/clang/docs/JSONCompilationDatabase.md index 35fdea8fc3b1a5..649d164d2d52d3 100644 --- a/clang/docs/JSONCompilationDatabase.md +++ b/clang/docs/JSONCompilationDatabase.md @@ -76,6 +76,7 @@ The contracts for each field in the command object are: - **directory:** The working directory of the compilation. All paths specified in the **command** or **file** fields must be either absolute or relative to this directory. + A relative **directory** is relative to the directory containing the compilation database file, after resolving symlinks. - **file:** The main translation unit source processed by this compilation step. This is used by tools as the key into the diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 44f30666457f94..e7ee0001f4e4a3 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -312,6 +312,8 @@ documentation](CIR/index.md) for more information regarding ClangIR's status and - Clang tools now resolve tool names without a path in compilation databases through `PATH`. +- A relative `directory` in a JSON compilation database is now resolved against the directory containing the compilation database file, after resolving symlinks. + - On musl targets, the driver now links ``libssp_nonshared.a`` when stack protection is enabled and the library is present in the toolchain library paths, matching what musl distributions configure GCC to do. diff --git a/clang/include/clang/Tooling/JSONCompilationDatabase.h b/clang/include/clang/Tooling/JSONCompilationDatabase.h index 96582457c63d58..8350ca6a65d134 100644 --- a/clang/include/clang/Tooling/JSONCompilationDatabase.h +++ b/clang/include/clang/Tooling/JSONCompilationDatabase.h @@ -20,6 +20,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/YAMLParser.h" @@ -60,7 +61,9 @@ namespace tooling { enum class JSONCommandLineSyntax { Windows, Gnu, AutoDetect }; class JSONCompilationDatabase : public CompilationDatabase { public: - /// Loads a JSON compilation database from the specified file. + /// Loads a JSON compilation database from the specified file. A relative + /// "directory" is resolved against the directory containing the file, after + /// resolving symlinks. /// /// Returns NULL and sets ErrorMessage if the database could not be /// loaded from the given file. @@ -68,12 +71,13 @@ class JSONCompilationDatabase : public CompilationDatabase { loadFromFile(StringRef FilePath, std::string &ErrorMessage, JSONCommandLineSyntax Syntax); - /// Loads a JSON compilation database from a data buffer. + /// Loads a JSON compilation database from a data buffer. If BaseDirectory + /// is non-empty, a relative "directory" is resolved against it. /// /// Returns NULL and sets ErrorMessage if the database could not be loaded. static std::unique_ptr<JSONCompilationDatabase> - loadFromBuffer(StringRef DatabaseString, std::string &ErrorMessage, - JSONCommandLineSyntax Syntax); + loadFromBuffer(StringRef BaseDirectory, StringRef DatabaseString, + std::string &ErrorMessage, JSONCommandLineSyntax Syntax); /// Returns all compile commands in which the specified file was /// compiled. @@ -103,19 +107,19 @@ class JSONCompilationDatabase : public CompilationDatabase { /// /// Returns whether parsing succeeded. Sets ErrorMessage if parsing /// failed. - bool parse(std::string &ErrorMessage); + bool parse(StringRef BaseDirectory, std::string &ErrorMessage); - // Tuple (directory, filename, commandline, output) where 'commandline' - // points to the corresponding scalar nodes in the YAML stream. + // Tuple (directory, filename, commandline, output) where 'directory' is + // owned by Directories and the others point to the corresponding scalar + // nodes in the YAML stream. // If the command line contains a single argument, it is a shell-escaped // command line. // Otherwise, each entry in the command line vector is a literal // argument to the compiler. // The output field may be a nullptr. - using CompileCommandRef = - std::tuple<llvm::yaml::ScalarNode *, llvm::yaml::ScalarNode *, - std::vector<llvm::yaml::ScalarNode *>, - llvm::yaml::ScalarNode *>; + using CompileCommandRef = std::tuple<StringRef, llvm::yaml::ScalarNode *, + std::vector<llvm::yaml::ScalarNode *>, + llvm::yaml::ScalarNode *>; /// Converts the given array of CompileCommandRefs to CompileCommands. void getCommands(ArrayRef<CompileCommandRef> CommandsRef, @@ -130,6 +134,7 @@ class JSONCompilationDatabase : public CompilationDatabase { FileMatchTrie MatchTrie; + llvm::StringSet<> Directories; std::unique_ptr<llvm::MemoryBuffer> Database; JSONCommandLineSyntax Syntax; llvm::SourceMgr SM; diff --git a/clang/lib/Tooling/JSONCompilationDatabase.cpp b/clang/lib/Tooling/JSONCompilationDatabase.cpp index 0efa75970d9864..5f54a74b308161 100644 --- a/clang/lib/Tooling/JSONCompilationDatabase.cpp +++ b/clang/lib/Tooling/JSONCompilationDatabase.cpp @@ -197,22 +197,27 @@ JSONCompilationDatabase::loadFromFile(StringRef FilePath, ErrorMessage = "Error while opening JSON database: " + Result.message(); return nullptr; } + SmallString<128> RealPath; + StringRef BaseDirectory; + if (!llvm::sys::fs::real_path(FilePath, RealPath)) + BaseDirectory = llvm::sys::path::parent_path(RealPath); std::unique_ptr<JSONCompilationDatabase> Database( new JSONCompilationDatabase(std::move(*DatabaseBuffer), Syntax)); - if (!Database->parse(ErrorMessage)) + if (!Database->parse(BaseDirectory, ErrorMessage)) return nullptr; return Database; } std::unique_ptr<JSONCompilationDatabase> -JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, +JSONCompilationDatabase::loadFromBuffer(StringRef BaseDirectory, + StringRef DatabaseString, std::string &ErrorMessage, JSONCommandLineSyntax Syntax) { std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer( llvm::MemoryBuffer::getMemBufferCopy(DatabaseString)); std::unique_ptr<JSONCompilationDatabase> Database( new JSONCompilationDatabase(std::move(DatabaseBuffer), Syntax)); - if (!Database->parse(ErrorMessage)) + if (!Database->parse(BaseDirectory, ErrorMessage)) return nullptr; return Database; } @@ -308,19 +313,18 @@ void JSONCompilationDatabase::getCommands( ArrayRef<CompileCommandRef> CommandsRef, std::vector<CompileCommand> &Commands) const { for (const auto &CommandRef : CommandsRef) { - SmallString<8> DirectoryStorage; SmallString<32> FilenameStorage; SmallString<32> OutputStorage; auto Output = std::get<3>(CommandRef); - Commands.emplace_back( - std::get<0>(CommandRef)->getValue(DirectoryStorage), - std::get<1>(CommandRef)->getValue(FilenameStorage), - nodeToCommandLine(Syntax, std::get<2>(CommandRef)), - Output ? Output->getValue(OutputStorage) : ""); + Commands.emplace_back(std::get<0>(CommandRef), + std::get<1>(CommandRef)->getValue(FilenameStorage), + nodeToCommandLine(Syntax, std::get<2>(CommandRef)), + Output ? Output->getValue(OutputStorage) : ""); } } -bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { +bool JSONCompilationDatabase::parse(StringRef BaseDirectory, + std::string &ErrorMessage) { llvm::yaml::document_iterator I = YAMLStream.begin(); if (I == YAMLStream.end()) { ErrorMessage = "Error while parsing YAML."; @@ -409,19 +413,26 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { ErrorMessage = "Missing key: \"directory\"."; return false; } + SmallString<8> DirectoryStorage; + SmallString<128> DirectoryPath(Directory->getValue(DirectoryStorage)); + if (!BaseDirectory.empty() && !DirectoryPath.empty() && + !llvm::sys::path::is_absolute(DirectoryPath)) { + llvm::sys::path::make_absolute(BaseDirectory, DirectoryPath); + llvm::sys::path::remove_dots(DirectoryPath); + } + StringRef DirectoryName = Directories.insert(DirectoryPath).first->getKey(); SmallString<8> FileStorage; StringRef FileName = File->getValue(FileStorage); SmallString<128> NativeFilePath; if (llvm::sys::path::is_relative(FileName)) { - SmallString<8> DirectoryStorage; - SmallString<128> AbsolutePath(Directory->getValue(DirectoryStorage)); + SmallString<128> AbsolutePath(DirectoryName); llvm::sys::path::append(AbsolutePath, FileName); llvm::sys::path::native(AbsolutePath, NativeFilePath); } else { llvm::sys::path::native(FileName, NativeFilePath); } llvm::sys::path::remove_dots(NativeFilePath, /*remove_dot_dot=*/true); - auto Cmd = CompileCommandRef(Directory, File, *Command, Output); + auto Cmd = CompileCommandRef(DirectoryName, File, *Command, Output); IndexByFile[NativeFilePath].push_back(Cmd); AllCommands.push_back(Cmd); MatchTrie.insert(NativeFilePath); diff --git a/clang/unittests/Tooling/CompilationDatabaseTest.cpp b/clang/unittests/Tooling/CompilationDatabaseTest.cpp index d571f992e763e6..5ce5223f0a4be7 100644 --- a/clang/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/clang/unittests/Tooling/CompilationDatabaseTest.cpp @@ -12,6 +12,7 @@ #include "clang/Tooling/Tooling.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Testing/Support/SupportHelpers.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include <algorithm> @@ -27,8 +28,8 @@ using testing::UnorderedElementsAreArray; static void expectFailure(StringRef JSONDatabase, StringRef Explanation) { std::string ErrorMessage; EXPECT_EQ(nullptr, - JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, - JSONCommandLineSyntax::Gnu)) + JSONCompilationDatabase::loadFromBuffer( + "", JSONDatabase, ErrorMessage, JSONCommandLineSyntax::Gnu)) << "Expected an error because of: " << Explanation.str(); } @@ -56,7 +57,7 @@ static std::vector<std::string> getAllFiles(StringRef JSONDatabase, std::string &ErrorMessage, JSONCommandLineSyntax Syntax) { std::unique_ptr<CompilationDatabase> Database( - JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, + JSONCompilationDatabase::loadFromBuffer("", JSONDatabase, ErrorMessage, Syntax)); if (!Database) { ADD_FAILURE() << ErrorMessage; @@ -71,7 +72,7 @@ static std::vector<CompileCommand> getAllCompileCommands(JSONCommandLineSyntax Syntax, StringRef JSONDatabase, std::string &ErrorMessage) { std::unique_ptr<CompilationDatabase> Database( - JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, + JSONCompilationDatabase::loadFromBuffer("", JSONDatabase, ErrorMessage, Syntax)); if (!Database) { ADD_FAILURE() << ErrorMessage; @@ -193,7 +194,7 @@ static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName, std::string JSONDatabase, std::string &ErrorMessage) { std::unique_ptr<CompilationDatabase> Database( - JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, + JSONCompilationDatabase::loadFromBuffer("", JSONDatabase, ErrorMessage, JSONCommandLineSyntax::Gnu)); if (!Database) return CompileCommand(); @@ -225,6 +226,63 @@ TEST(JSONCompilationDatabase, ArgumentsPreferredOverCommand) { EXPECT_EQ(Arguments, FoundCommand.CommandLine[0]) << ErrorMessage; } +static std::string native(StringRef Path) { + SmallString<32> Result; + llvm::sys::path::native(Path, Result); + return std::string(Result); +} + +TEST(JSONCompilationDatabase, RelativeDirectory) { + StringRef JSONDatabase = R"([ + {"directory": ".", "command": "cc a.cc", "file": "a.cc"}, + {"directory": "../out", "command": "cc b.cc", "file": "b.cc"}, + {"directory": "//net/abs/.", "command": "cc c.cc", "file": "c.cc"}, + {"directory": "", "command": "cc d.cc", "file": "d.cc"} + ])"; + std::string ErrorMessage; + std::unique_ptr<JSONCompilationDatabase> Database = + JSONCompilationDatabase::loadFromBuffer( + "//net/db", JSONDatabase, ErrorMessage, JSONCommandLineSyntax::Gnu); + ASSERT_TRUE(Database) << ErrorMessage; + std::vector<CompileCommand> Commands = Database->getAllCompileCommands(); + ASSERT_EQ(4u, Commands.size()); + EXPECT_EQ(native("//net/db"), Commands[0].Directory); + EXPECT_EQ(native("//net/db/../out"), Commands[1].Directory); + EXPECT_EQ("//net/abs/.", Commands[2].Directory); + EXPECT_EQ("", Commands[3].Directory); + EXPECT_EQ(1u, Database->getCompileCommands("//net/out/b.cc").size()); + + Database = JSONCompilationDatabase::loadFromBuffer( + "", JSONDatabase, ErrorMessage, JSONCommandLineSyntax::Gnu); + ASSERT_TRUE(Database) << ErrorMessage; + EXPECT_EQ(".", Database->getAllCompileCommands()[0].Directory); +} + +TEST(JSONCompilationDatabase, RelativeDirectoryFromFile) { + llvm::unittest::TempDir Dir("relative-directory", /*Unique=*/true); + llvm::unittest::TempDir Build(Dir.path("build")); + llvm::unittest::TempFile File( + Build.path("compile_commands.json"), "", + R"([{"directory": ".", "command": "cc a.cc", "file": "../a.cc"}])"); + auto Check = [&](StringRef DatabasePath) { + std::string ErrorMessage; + std::unique_ptr<JSONCompilationDatabase> Database = + JSONCompilationDatabase::loadFromFile(DatabasePath, ErrorMessage, + JSONCommandLineSyntax::Gnu); + ASSERT_TRUE(Database) << ErrorMessage; + std::vector<CompileCommand> Commands = + Database->getCompileCommands(Dir.path("a.cc")); + ASSERT_EQ(1u, Commands.size()); + EXPECT_EQ(Build.path(), Commands[0].Directory); + }; + Check(File.path()); +#ifndef _WIN32 + // A symlink to the database does not change the base directory. + llvm::unittest::TempLink Link(File.path(), Dir.path("compile_commands.json")); + Check(Link.path()); +#endif +} + struct FakeComparator : public PathComparator { ~FakeComparator() override {} bool equivalent(StringRef FileA, StringRef FileB) const override { diff --git a/clang/unittests/Tooling/ToolingTest.cpp b/clang/unittests/Tooling/ToolingTest.cpp index 11a5491dd35fb1..96240f2bbcf26a 100644 --- a/clang/unittests/Tooling/ToolingTest.cpp +++ b/clang/unittests/Tooling/ToolingTest.cpp @@ -1096,7 +1096,7 @@ std::string runToolWithProgress(llvm::ArrayRef<TestCommand> Commands, OS << llvm::json::Value(std::move(Entries)); std::unique_ptr<CompilationDatabase> Database( - JSONCompilationDatabase::loadFromBuffer(DatabaseContent, ErrorMessage, + JSONCompilationDatabase::loadFromBuffer("", DatabaseContent, ErrorMessage, JSONCommandLineSyntax::Gnu)); if (!Database) { ADD_FAILURE() << "Failed to load compilation database: " << ErrorMessage; >From ba336a14d90c4b115f6c67c8a3a657bbecd7d227 Mon Sep 17 00:00:00 2001 From: Fangrui Song <[email protected]> Date: Sun, 25 Oct 2026 10:32:51 -0700 Subject: [PATCH 2/2] dont resolve symlink --- .../clangd/GlobalCompilationDatabase.cpp | 17 +++++------------ clang/docs/JSONCompilationDatabase.md | 3 ++- clang/docs/ReleaseNotes.md | 2 +- .../clang/Tooling/JSONCompilationDatabase.h | 3 +-- clang/lib/Tooling/JSONCompilationDatabase.cpp | 7 +++---- .../Tooling/CompilationDatabaseTest.cpp | 17 ++++++++--------- 6 files changed, 20 insertions(+), 29 deletions(-) diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp index 7e12a040345a17..b7bb7f426d6150 100644 --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp @@ -247,14 +247,9 @@ DirectoryBasedGlobalCompilationDatabase::DirectoryCache::CachedFile::load( // Adapt CDB-loading functions to a common interface for DirectoryCache::load(). static std::unique_ptr<tooling::CompilationDatabase> -parseJSON(PathRef Path, llvm::StringRef Data, llvm::vfs::FileSystem &FS, - std::string &Error) { - llvm::SmallString<256> RealPath; - llvm::StringRef BaseDirectory; - if (!FS.getRealPath(Path, RealPath)) - BaseDirectory = llvm::sys::path::parent_path(RealPath); +parseJSON(PathRef Path, llvm::StringRef Data, std::string &Error) { if (auto CDB = tooling::JSONCompilationDatabase::loadFromBuffer( - BaseDirectory, Data, Error, + llvm::sys::path::parent_path(Path), Data, Error, tooling::JSONCommandLineSyntax::AutoDetect)) { // FS used for expanding response files. // FIXME: ExpandResponseFilesDatabase appears not to provide the usual @@ -270,8 +265,7 @@ parseJSON(PathRef Path, llvm::StringRef Data, llvm::vfs::FileSystem &FS, return nullptr; } static std::unique_ptr<tooling::CompilationDatabase> -parseFixed(PathRef Path, llvm::StringRef Data, llvm::vfs::FileSystem &, - std::string &Error) { +parseFixed(PathRef Path, llvm::StringRef Data, std::string &Error) { return tooling::FixedCompilationDatabase::loadFromBuffer( llvm::sys::path::parent_path(Path), Data, Error); } @@ -290,7 +284,7 @@ bool DirectoryBasedGlobalCompilationDatabase::DirectoryCache::load( // Wrapper for {Fixed,JSON}CompilationDatabase::loadFromBuffer. std::unique_ptr<tooling::CompilationDatabase> (*Parser)( PathRef, - /*Data*/ llvm::StringRef, llvm::vfs::FileSystem &, + /*Data*/ llvm::StringRef, /*ErrorMsg*/ std::string &); }; for (const auto &Entry : {CDBFile{&CompileCommandsJson, parseJSON}, @@ -316,8 +310,7 @@ bool DirectoryBasedGlobalCompilationDatabase::DirectoryCache::load( return true; case CachedFile::LoadResult::FoundNewData: // We have a new CDB! - CDB = - Entry.Parser(Entry.File->Path, Loaded.Buffer->getBuffer(), FS, Error); + CDB = Entry.Parser(Entry.File->Path, Loaded.Buffer->getBuffer(), Error); if (CDB) log("{0} compilation database from {1}", Active ? "Reloaded" : "Loaded", Entry.File->Path); diff --git a/clang/docs/JSONCompilationDatabase.md b/clang/docs/JSONCompilationDatabase.md index 649d164d2d52d3..e4101a964d6dff 100644 --- a/clang/docs/JSONCompilationDatabase.md +++ b/clang/docs/JSONCompilationDatabase.md @@ -76,7 +76,8 @@ The contracts for each field in the command object are: - **directory:** The working directory of the compilation. All paths specified in the **command** or **file** fields must be either absolute or relative to this directory. - A relative **directory** is relative to the directory containing the compilation database file, after resolving symlinks. + A relative **directory** is relative to the directory containing the compilation database file. + Symlinks are not resolved: a symlink to the file is relative to the symlink's directory. - **file:** The main translation unit source processed by this compilation step. This is used by tools as the key into the diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index e7ee0001f4e4a3..0509fdfe679312 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -312,7 +312,7 @@ documentation](CIR/index.md) for more information regarding ClangIR's status and - Clang tools now resolve tool names without a path in compilation databases through `PATH`. -- A relative `directory` in a JSON compilation database is now resolved against the directory containing the compilation database file, after resolving symlinks. +- A relative `directory` in a JSON compilation database is now resolved against the directory containing the compilation database file. - On musl targets, the driver now links ``libssp_nonshared.a`` when stack protection is enabled and the library is present in the toolchain library diff --git a/clang/include/clang/Tooling/JSONCompilationDatabase.h b/clang/include/clang/Tooling/JSONCompilationDatabase.h index 8350ca6a65d134..f34a163ddd158b 100644 --- a/clang/include/clang/Tooling/JSONCompilationDatabase.h +++ b/clang/include/clang/Tooling/JSONCompilationDatabase.h @@ -62,8 +62,7 @@ enum class JSONCommandLineSyntax { Windows, Gnu, AutoDetect }; class JSONCompilationDatabase : public CompilationDatabase { public: /// Loads a JSON compilation database from the specified file. A relative - /// "directory" is resolved against the directory containing the file, after - /// resolving symlinks. + /// "directory" is resolved against the directory containing the file. /// /// Returns NULL and sets ErrorMessage if the database could not be /// loaded from the given file. diff --git a/clang/lib/Tooling/JSONCompilationDatabase.cpp b/clang/lib/Tooling/JSONCompilationDatabase.cpp index 5f54a74b308161..cb47e4eb8c96c1 100644 --- a/clang/lib/Tooling/JSONCompilationDatabase.cpp +++ b/clang/lib/Tooling/JSONCompilationDatabase.cpp @@ -197,10 +197,9 @@ JSONCompilationDatabase::loadFromFile(StringRef FilePath, ErrorMessage = "Error while opening JSON database: " + Result.message(); return nullptr; } - SmallString<128> RealPath; - StringRef BaseDirectory; - if (!llvm::sys::fs::real_path(FilePath, RealPath)) - BaseDirectory = llvm::sys::path::parent_path(RealPath); + SmallString<128> BaseDirectory(FilePath); + llvm::sys::fs::make_absolute(BaseDirectory); + llvm::sys::path::remove_filename(BaseDirectory); std::unique_ptr<JSONCompilationDatabase> Database( new JSONCompilationDatabase(std::move(*DatabaseBuffer), Syntax)); if (!Database->parse(BaseDirectory, ErrorMessage)) diff --git a/clang/unittests/Tooling/CompilationDatabaseTest.cpp b/clang/unittests/Tooling/CompilationDatabaseTest.cpp index 5ce5223f0a4be7..780881990376b1 100644 --- a/clang/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/clang/unittests/Tooling/CompilationDatabaseTest.cpp @@ -263,24 +263,23 @@ TEST(JSONCompilationDatabase, RelativeDirectoryFromFile) { llvm::unittest::TempDir Build(Dir.path("build")); llvm::unittest::TempFile File( Build.path("compile_commands.json"), "", - R"([{"directory": ".", "command": "cc a.cc", "file": "../a.cc"}])"); - auto Check = [&](StringRef DatabasePath) { + R"([{"directory": ".", "command": "cc a.cc", "file": "a.cc"}])"); + auto Check = [](StringRef DatabasePath, const llvm::unittest::TempDir &Base) { std::string ErrorMessage; std::unique_ptr<JSONCompilationDatabase> Database = JSONCompilationDatabase::loadFromFile(DatabasePath, ErrorMessage, JSONCommandLineSyntax::Gnu); ASSERT_TRUE(Database) << ErrorMessage; std::vector<CompileCommand> Commands = - Database->getCompileCommands(Dir.path("a.cc")); + Database->getCompileCommands(Base.path("a.cc")); ASSERT_EQ(1u, Commands.size()); - EXPECT_EQ(Build.path(), Commands[0].Directory); + EXPECT_EQ(Base.path(), Commands[0].Directory); }; - Check(File.path()); -#ifndef _WIN32 - // A symlink to the database does not change the base directory. + Check(File.path(), Build); + // Symlinks are not resolved: a link to the database is relative to the + // link's directory. llvm::unittest::TempLink Link(File.path(), Dir.path("compile_commands.json")); - Check(Link.path()); -#endif + Check(Link.path(), Dir); } struct FakeComparator : public PathComparator { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
