From 65fd0e1fd3c7fe24bb09e654d228c9584c2fa6ff Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Tue, 10 Jul 2012 13:10:51 +0000 Subject: [PATCH] Adds support for auto-detection of compilation databases from a source file and changes clang-check to make use of this. This makes clang-check just work on in-tree builds, and allows easy setup via a symlink per source directory to make clang-check work without any extra configuration. llvm-svn: 159990 --- .../clang/Tooling/CompilationDatabase.h | 8 +++++- clang/include/clang/Tooling/Tooling.h | 16 +++++++++++ clang/lib/Tooling/CompilationDatabase.cpp | 19 ++++++++++++- clang/lib/Tooling/Tooling.cpp | 27 +++++-------------- .../auto-detect-from-source-parent-of-cwd.cpp | 8 ++++++ .../auto-detect-from-source-parent.cpp | 8 ++++++ .../test/Tooling/auto-detect-from-source.cpp | 8 ++++++ clang/test/Tooling/clang-check-args.cpp | 2 +- .../Tooling/clang-check-builtin-headers.cpp | 2 +- clang/test/Tooling/clang-check-chdir.cpp | 2 +- clang/test/Tooling/clang-check-pwd.cpp | 2 +- clang/test/Tooling/clang-check.cpp | 2 +- clang/test/Tooling/multi-jobs.cpp | 2 +- clang/tools/clang-check/ClangCheck.cpp | 14 +++++++--- 14 files changed, 88 insertions(+), 32 deletions(-) create mode 100644 clang/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp create mode 100644 clang/test/Tooling/auto-detect-from-source-parent.cpp create mode 100644 clang/test/Tooling/auto-detect-from-source.cpp diff --git a/clang/include/clang/Tooling/CompilationDatabase.h b/clang/include/clang/Tooling/CompilationDatabase.h index 625c8eced4b8..0dec6f8f87de 100644 --- a/clang/include/clang/Tooling/CompilationDatabase.h +++ b/clang/include/clang/Tooling/CompilationDatabase.h @@ -81,6 +81,13 @@ public: static CompilationDatabase *loadFromDirectory(StringRef BuildDirectory, std::string &ErrorMessage); + /// \brief Tries to detect a compilation database location and load it. + /// + /// Looks for a compilation database in all parent paths by calling + /// loadFromDirectory. + static CompilationDatabase *autoDetectFromSource(StringRef SourceFile, + std::string &ErrorMessage); + /// \brief Returns all compile commands in which the specified file was /// compiled. /// @@ -215,4 +222,3 @@ private: } // end namespace clang #endif // LLVM_CLANG_TOOLING_COMPILATION_DATABASE_H - diff --git a/clang/include/clang/Tooling/Tooling.h b/clang/include/clang/Tooling/Tooling.h index 0e1dd58ac9ce..03f9d0b4bcac 100644 --- a/clang/include/clang/Tooling/Tooling.h +++ b/clang/include/clang/Tooling/Tooling.h @@ -232,6 +232,22 @@ FrontendActionFactory *newFrontendActionFactory(FactoryT *ConsumerFactory) { return new FrontendActionFactoryAdapter(ConsumerFactory); } +/// \brief Returns the absolute path of \c File, by prepending it with +/// the current directory if \c File is not absolute. +/// +/// Otherwise returns \c File. +/// If 'File' starts with "./", the returned path will not contain the "./". +/// Otherwise, the returned path will contain the literal path-concatenation of +/// the current directory and \c File. +/// +/// The difference to llvm::sys::fs::make_absolute is that we prefer +/// ::getenv("PWD") if available. +/// FIXME: Make this functionality available from llvm::sys::fs and delete +/// this function. +/// +/// \param File Either an absolute or relative path. +std::string getAbsolutePath(StringRef File); + } // end namespace tooling } // end namespace clang diff --git a/clang/lib/Tooling/CompilationDatabase.cpp b/clang/lib/Tooling/CompilationDatabase.cpp index 227fa82926c3..8e911123bc80 100644 --- a/clang/lib/Tooling/CompilationDatabase.cpp +++ b/clang/lib/Tooling/CompilationDatabase.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Tooling.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/Path.h" @@ -121,6 +122,23 @@ CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, return Database.take(); } +CompilationDatabase * +CompilationDatabase::autoDetectFromSource(StringRef SourceFile, + std::string &ErrorMessage) { + llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile)); + StringRef Directory = llvm::sys::path::parent_path(AbsolutePath); + while (!Directory.empty()) { + std::string LoadErrorMessage; + if (CompilationDatabase *DB = loadFromDirectory(Directory, + LoadErrorMessage)) + return DB; + Directory = llvm::sys::path::parent_path(Directory); + } + ErrorMessage = ("Could not auto-detect compilation database for file \"" + + SourceFile + "\"").str(); + return NULL; +} + FixedCompilationDatabase * FixedCompilationDatabase::loadFromCommandLine(int &Argc, const char **Argv, @@ -283,4 +301,3 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { } // end namespace tooling } // end namespace clang - diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp index abac182b215d..5d41172a9192 100644 --- a/clang/lib/Tooling/Tooling.cpp +++ b/clang/lib/Tooling/Tooling.cpp @@ -115,20 +115,13 @@ bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, return Invocation.run(); } -/// \brief Returns the absolute path of 'File', by prepending it with -/// 'BaseDirectory' if 'File' is not absolute. -/// -/// Otherwise returns 'File'. -/// If 'File' starts with "./", the returned path will not contain the "./". -/// Otherwise, the returned path will contain the literal path-concatenation of -/// 'BaseDirectory' and 'File'. -/// -/// \param File Either an absolute or relative path. -/// \param BaseDirectory An absolute path. -static std::string getAbsolutePath( - StringRef File, StringRef BaseDirectory) { +std::string getAbsolutePath(StringRef File) { + llvm::SmallString<1024> BaseDirectory; + if (const char *PWD = ::getenv("PWD")) + BaseDirectory = PWD; + else + llvm::sys::fs::current_path(BaseDirectory); SmallString<1024> PathStorage; - assert(llvm::sys::path::is_absolute(BaseDirectory)); if (llvm::sys::path::is_absolute(File)) { llvm::sys::path::native(File, PathStorage); return PathStorage.str(); @@ -240,14 +233,8 @@ ClangTool::ClangTool(const CompilationDatabase &Compilations, ArrayRef SourcePaths) : Files((FileSystemOptions())), ArgsAdjuster(new ClangSyntaxOnlyAdjuster()) { - llvm::SmallString<1024> BaseDirectory; - if (const char *PWD = ::getenv("PWD")) - BaseDirectory = PWD; - else - llvm::sys::fs::current_path(BaseDirectory); for (unsigned I = 0, E = SourcePaths.size(); I != E; ++I) { - llvm::SmallString<1024> File(getAbsolutePath( - SourcePaths[I], BaseDirectory)); + llvm::SmallString<1024> File(getAbsolutePath(SourcePaths[I])); std::vector CompileCommandsForFile = Compilations.getCompileCommands(File.str()); diff --git a/clang/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp b/clang/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp new file mode 100644 index 000000000000..7a4f9af6c4f3 --- /dev/null +++ b/clang/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp @@ -0,0 +1,8 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t/abc/def/ijk/qwe +// RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/abc/def/ijk/qwe/test.cpp\",\"file\":\"%t/abc/def/ijk/qwe/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json +// RUN: cp "%s" "%t/abc/def/ijk/qwe/test.cpp" +// RUN: PWD="%t/abc/def" clang-check "ijk/qwe/test.cpp" 2>&1 | FileCheck %s + +// CHECK: C++ requires +invalid; diff --git a/clang/test/Tooling/auto-detect-from-source-parent.cpp b/clang/test/Tooling/auto-detect-from-source-parent.cpp new file mode 100644 index 000000000000..cb5fd56328ef --- /dev/null +++ b/clang/test/Tooling/auto-detect-from-source-parent.cpp @@ -0,0 +1,8 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t/abc/def/ijk/qwe +// RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/abc/def/ijk/qwe/test.cpp\",\"file\":\"%t/abc/def/ijk/qwe/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json +// RUN: cp "%s" "%t/abc/def/ijk/qwe/test.cpp" +// RUN: clang-check "%t/abc/def/ijk/qwe/test.cpp" 2>&1 | FileCheck %s + +// CHECK: C++ requires +invalid; diff --git a/clang/test/Tooling/auto-detect-from-source.cpp b/clang/test/Tooling/auto-detect-from-source.cpp new file mode 100644 index 000000000000..40a2a1c9fe39 --- /dev/null +++ b/clang/test/Tooling/auto-detect-from-source.cpp @@ -0,0 +1,8 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/test.cpp\",\"file\":\"%t/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-check "%t/test.cpp" 2>&1 | FileCheck %s + +// CHECK: C++ requires +invalid; diff --git a/clang/test/Tooling/clang-check-args.cpp b/clang/test/Tooling/clang-check-args.cpp index ab1585f07332..9ba5d45f5088 100644 --- a/clang/test/Tooling/clang-check-args.cpp +++ b/clang/test/Tooling/clang-check-args.cpp @@ -1,4 +1,4 @@ -// RUN: clang-check . "%s" -- -c 2>&1 | FileCheck %s +// RUN: clang-check "%s" -- -c 2>&1 | FileCheck %s // CHECK: C++ requires invalid; diff --git a/clang/test/Tooling/clang-check-builtin-headers.cpp b/clang/test/Tooling/clang-check-builtin-headers.cpp index fda68ccbc076..504d197ea9af 100644 --- a/clang/test/Tooling/clang-check-builtin-headers.cpp +++ b/clang/test/Tooling/clang-check-builtin-headers.cpp @@ -3,7 +3,7 @@ // Add a path that doesn't exist as argv[0] for the compile command line: // RUN: echo '[{"directory":".","command":"/random/tool -c %t/test.cpp","file":"%t/test.cpp"}]' | sed -e 's/\\/\//g' > %t/compile_commands.json // RUN: cp "%s" "%t/test.cpp" -// RUN: clang-check "%t" "%t/test.cpp" 2>&1|FileCheck %s +// RUN: clang-check -p "%t" "%t/test.cpp" 2>&1|FileCheck %s // FIXME: Make the above easier. #include diff --git a/clang/test/Tooling/clang-check-chdir.cpp b/clang/test/Tooling/clang-check-chdir.cpp index d9f172848b4c..29b5abb4c946 100644 --- a/clang/test/Tooling/clang-check-chdir.cpp +++ b/clang/test/Tooling/clang-check-chdir.cpp @@ -5,7 +5,7 @@ // RUN: echo "[{\"directory\":\"%t\",\"command\":\"clang -c test.cpp -I.\",\"file\":\"%t/test.cpp\"}]" | sed -e 's/\\/\//g' > %t/compile_commands.json // RUN: cp "%s" "%t/test.cpp" // RUN: touch "%t/clang-check-test.h" -// RUN: clang-check "%t" "%t/test.cpp" 2>&1|FileCheck %s +// RUN: clang-check -p "%t" "%t/test.cpp" 2>&1|FileCheck %s // FIXME: Make the above easier. #include "clang-check-test.h" diff --git a/clang/test/Tooling/clang-check-pwd.cpp b/clang/test/Tooling/clang-check-pwd.cpp index 8dd9e6e926da..374c579245b7 100644 --- a/clang/test/Tooling/clang-check-pwd.cpp +++ b/clang/test/Tooling/clang-check-pwd.cpp @@ -2,7 +2,7 @@ // RUN: mkdir %t // RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/test.cpp\",\"file\":\"%t/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json // RUN: cp "%s" "%t/test.cpp" -// RUN: PWD="%t" clang-check "%t" "test.cpp" 2>&1|FileCheck %s +// RUN: PWD="%t" clang-check -p "%t" "test.cpp" 2>&1|FileCheck %s // FIXME: Make the above easier. // CHECK: C++ requires diff --git a/clang/test/Tooling/clang-check.cpp b/clang/test/Tooling/clang-check.cpp index ff90b8841738..91ab01b01be5 100644 --- a/clang/test/Tooling/clang-check.cpp +++ b/clang/test/Tooling/clang-check.cpp @@ -2,7 +2,7 @@ // RUN: mkdir %t // RUN: echo '[{"directory":".","command":"clang++ -c %t/test.cpp","file":"%t/test.cpp"}]' | sed -e 's/\\/\//g' > %t/compile_commands.json // RUN: cp "%s" "%t/test.cpp" -// RUN: clang-check "%t" "%t/test.cpp" 2>&1|FileCheck %s +// RUN: clang-check -p "%t" "%t/test.cpp" 2>&1|FileCheck %s // FIXME: Make the above easier. // CHECK: C++ requires diff --git a/clang/test/Tooling/multi-jobs.cpp b/clang/test/Tooling/multi-jobs.cpp index 1de56345cfbb..a3eb7039c04f 100644 --- a/clang/test/Tooling/multi-jobs.cpp +++ b/clang/test/Tooling/multi-jobs.cpp @@ -1,4 +1,4 @@ -// RUN: clang-check . "%s" -- -no-integrated-as -c 2>&1 | FileCheck %s +// RUN: clang-check "%s" -- -no-integrated-as -c 2>&1 | FileCheck %s // CHECK: C++ requires invalid; diff --git a/clang/tools/clang-check/ClangCheck.cpp b/clang/tools/clang-check/ClangCheck.cpp index d68e282949c8..ef4a3ace0c3a 100644 --- a/clang/tools/clang-check/ClangCheck.cpp +++ b/clang/tools/clang-check/ClangCheck.cpp @@ -42,8 +42,9 @@ using namespace clang::tooling; using namespace llvm; cl::opt BuildPath( - cl::Positional, - cl::desc("")); + "p", + cl::desc(""), + cl::Optional); cl::list SourcePaths( cl::Positional, @@ -56,8 +57,13 @@ int main(int argc, const char **argv) { cl::ParseCommandLineOptions(argc, argv); if (!Compilations) { std::string ErrorMessage; - Compilations.reset(CompilationDatabase::loadFromDirectory(BuildPath, - ErrorMessage)); + if (!BuildPath.empty()) { + Compilations.reset(CompilationDatabase::loadFromDirectory(BuildPath, + ErrorMessage)); + } else { + Compilations.reset(CompilationDatabase::autoDetectFromSource( + SourcePaths[0], ErrorMessage)); + } if (!Compilations) llvm::report_fatal_error(ErrorMessage); }