diff --git a/include/clang/Tooling/CompilationDatabase.h b/include/clang/Tooling/CompilationDatabase.h index 625c8eced4b8f0b94fc6b19f4ff40ade4ebb2b9b..0dec6f8f87deef208bc4d1518eeaa40d67636c67 100644 --- a/include/clang/Tooling/CompilationDatabase.h +++ b/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/include/clang/Tooling/Tooling.h b/include/clang/Tooling/Tooling.h index 0e1dd58ac9ce7f2f66c4d37ab6dd8a59b3b52f66..03f9d0b4bcac8d5ed091489ddca7e63728f41afa 100644 --- a/include/clang/Tooling/Tooling.h +++ b/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/lib/Tooling/CompilationDatabase.cpp b/lib/Tooling/CompilationDatabase.cpp index 227fa82926c3631cb07f8a8bce569395063155eb..8e911123bc800e45cb68c24d1cea9f64223f1d84 100644 --- a/lib/Tooling/CompilationDatabase.cpp +++ b/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/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp index abac182b215dfcfa5cfaa87dedf9e1915cd099dc..5d41172a919275a6731f40ef86c8ab8adff9c76c 100644 --- a/lib/Tooling/Tooling.cpp +++ b/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<std::string> 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<CompileCommand> CompileCommandsForFile = Compilations.getCompileCommands(File.str()); diff --git a/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp b/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7a4f9af6c4f3a90fbcaa7d3ea3cb45df0305c02b --- /dev/null +++ b/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/test/Tooling/auto-detect-from-source-parent.cpp b/test/Tooling/auto-detect-from-source-parent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb5fd56328ef9069a089c3c34f890841ed80c945 --- /dev/null +++ b/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/test/Tooling/auto-detect-from-source.cpp b/test/Tooling/auto-detect-from-source.cpp new file mode 100644 index 0000000000000000000000000000000000000000..40a2a1c9fe392489b53561f4466acd92bda1e11a --- /dev/null +++ b/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/test/Tooling/clang-check-args.cpp b/test/Tooling/clang-check-args.cpp index ab1585f073323807a52ce551d1268345318b5382..9ba5d45f5088d11649b48b14c4ceaccfa59ccd32 100644 --- a/test/Tooling/clang-check-args.cpp +++ b/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/test/Tooling/clang-check-builtin-headers.cpp b/test/Tooling/clang-check-builtin-headers.cpp index fda68ccbc0767831aeaffcd14caee39dca1fc503..504d197ea9af080b3a489d3a6e14b39a2f224810 100644 --- a/test/Tooling/clang-check-builtin-headers.cpp +++ b/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 <stddef.h> diff --git a/test/Tooling/clang-check-chdir.cpp b/test/Tooling/clang-check-chdir.cpp index d9f172848b4c2c8eb6754e81316343abef367458..29b5abb4c946684301e595b06cda3aaef29701ee 100644 --- a/test/Tooling/clang-check-chdir.cpp +++ b/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/test/Tooling/clang-check-pwd.cpp b/test/Tooling/clang-check-pwd.cpp index 8dd9e6e926dabee60e7ddcef312b74a93fd6af1c..374c579245b721028c1cb662a878f76c935d23f7 100644 --- a/test/Tooling/clang-check-pwd.cpp +++ b/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/test/Tooling/clang-check.cpp b/test/Tooling/clang-check.cpp index ff90b8841738ad69031f6d191187718d782e0be3..91ab01b01be555c6f7b2ddb9db79eefb23e7e6f9 100644 --- a/test/Tooling/clang-check.cpp +++ b/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/test/Tooling/multi-jobs.cpp b/test/Tooling/multi-jobs.cpp index 1de56345cfbb3e88cce3810daeff0a1b511d4ad0..a3eb7039c04fd47baa5aa364667e9659ef239bd5 100644 --- a/test/Tooling/multi-jobs.cpp +++ b/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/tools/clang-check/ClangCheck.cpp b/tools/clang-check/ClangCheck.cpp index d68e282949c813ba719400126c9f4aaa969cbe14..ef4a3ace0c3a6326b75906c5d59419d316bcd552 100644 --- a/tools/clang-check/ClangCheck.cpp +++ b/tools/clang-check/ClangCheck.cpp @@ -42,8 +42,9 @@ using namespace clang::tooling; using namespace llvm; cl::opt<std::string> BuildPath( - cl::Positional, - cl::desc("<build-path>")); + "p", + cl::desc("<build-path>"), + cl::Optional); cl::list<std::string> 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); }