From ff26efceb44ad0ced3bf1006d5a85e21426e9956 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Wed, 18 Apr 2012 07:41:50 +0000 Subject: [PATCH] Adds a FixedCompilationDatabase to be able to specify tool parameters at the command line. llvm-svn: 154989 --- .../clang/Tooling/CompilationDatabase.h | 59 +++++++++++++++- clang/lib/Tooling/CompilationDatabase.cpp | 27 ++++++++ clang/test/Tooling/clang-check-args.cpp | 8 +++ clang/tools/clang-check/ClangCheck.cpp | 16 +++-- .../Tooling/CompilationDatabaseTest.cpp | 69 +++++++++++++++++++ 5 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 clang/test/Tooling/clang-check-args.cpp diff --git a/clang/include/clang/Tooling/CompilationDatabase.h b/clang/include/clang/Tooling/CompilationDatabase.h index 60e9a080dc59..625c8eced4b8 100644 --- a/clang/include/clang/Tooling/CompilationDatabase.h +++ b/clang/include/clang/Tooling/CompilationDatabase.h @@ -33,6 +33,7 @@ #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/YAMLParser.h" @@ -45,8 +46,8 @@ namespace tooling { /// \brief Specifies the working directory and command of a compilation. struct CompileCommand { CompileCommand() {} - CompileCommand(StringRef Directory, ArrayRef CommandLine) - : Directory(Directory), CommandLine(CommandLine) {} + CompileCommand(Twine Directory, ArrayRef CommandLine) + : Directory(Directory.str()), CommandLine(CommandLine) {} /// \brief The working directory the command was executed from. std::string Directory; @@ -93,6 +94,59 @@ public: StringRef FilePath) const = 0; }; +/// \brief A compilation database that returns a single compile command line. +/// +/// Useful when we want a tool to behave more like a compiler invocation. +class FixedCompilationDatabase : public CompilationDatabase { +public: + /// \brief Creates a FixedCompilationDatabase from the arguments after "--". + /// + /// Parses the given command line for "--". If "--" is found, the rest of + /// the arguments will make up the command line in the returned + /// FixedCompilationDatabase. + /// The arguments after "--" must not include positional parameters or the + /// argv[0] of the tool. Those will be added by the FixedCompilationDatabase + /// when a CompileCommand is requested. The argv[0] of the returned command + /// line will be "clang-tool". + /// + /// Returns NULL in case "--" is not found. + /// + /// The argument list is meant to be compatible with normal llvm command line + /// parsing in main methods. + /// int main(int argc, char **argv) { + /// llvm::OwningPtr Compilations( + /// FixedCompilationDatabase::loadFromCommandLine(argc, argv)); + /// cl::ParseCommandLineOptions(argc, argv); + /// ... + /// } + /// + /// \param Argc The number of command line arguments - will be changed to + /// the number of arguments before "--", if "--" was found in the argument + /// list. + /// \param Argv Points to the command line arguments. + /// \param Directory The base directory used in the FixedCompilationDatabase. + static FixedCompilationDatabase *loadFromCommandLine(int &Argc, + const char **Argv, + Twine Directory = "."); + + /// \brief Constructs a compilation data base from a specified directory + /// and command line. + FixedCompilationDatabase(Twine Directory, ArrayRef CommandLine); + + /// \brief Returns the given compile command. + /// + /// Will always return a vector with one entry that contains the directory + /// and command line specified at construction with "clang-tool" as argv[0] + /// and 'FilePath' as positional argument. + virtual std::vector getCompileCommands( + StringRef FilePath) const; + +private: + /// This is built up to contain a single entry vector to be returned from + /// getCompileCommands after adding the positional argument. + std::vector CompileCommands; +}; + /// \brief A JSON based compilation database. /// /// JSON compilation database files must contain a list of JSON objects which @@ -112,7 +166,6 @@ public: /// by setting the flag -DCMAKE_EXPORT_COMPILE_COMMANDS. class JSONCompilationDatabase : public CompilationDatabase { public: - /// \brief Loads a JSON compilation database from the specified file. /// /// Returns NULL and sets ErrorMessage if the database could not be diff --git a/clang/lib/Tooling/CompilationDatabase.cpp b/clang/lib/Tooling/CompilationDatabase.cpp index f8993658da35..dd9ccc07b60b 100644 --- a/clang/lib/Tooling/CompilationDatabase.cpp +++ b/clang/lib/Tooling/CompilationDatabase.cpp @@ -121,6 +121,33 @@ CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, return Database.take(); } +FixedCompilationDatabase * +FixedCompilationDatabase::loadFromCommandLine(int &Argc, + const char **Argv, + Twine Directory) { + const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--")); + if (DoubleDash == Argv + Argc) + return NULL; + std::vector CommandLine(DoubleDash + 1, Argv + Argc); + Argc = DoubleDash - Argv; + return new FixedCompilationDatabase(Directory, CommandLine); +} + +FixedCompilationDatabase:: +FixedCompilationDatabase(Twine Directory, ArrayRef CommandLine) { + std::vector ToolCommandLine(1, "clang-tool"); + ToolCommandLine.insert(ToolCommandLine.end(), + CommandLine.begin(), CommandLine.end()); + CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine)); +} + +std::vector +FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { + std::vector Result(CompileCommands); + Result[0].CommandLine.push_back(FilePath); + return Result; +} + JSONCompilationDatabase * JSONCompilationDatabase::loadFromFile(StringRef FilePath, std::string &ErrorMessage) { diff --git a/clang/test/Tooling/clang-check-args.cpp b/clang/test/Tooling/clang-check-args.cpp new file mode 100644 index 000000000000..a14fc7bcd647 --- /dev/null +++ b/clang/test/Tooling/clang-check-args.cpp @@ -0,0 +1,8 @@ +// RUN: clang-check . "%s" -- -c 2>&1 | FileCheck %s + +// CHECK: C++ requires +invalid; + +// FIXME: JSON doesn't like path separator '\', on Win32 hosts. +// FIXME: clang-check doesn't like gcc driver on cygming. +// XFAIL: cygwin,mingw32,win32 diff --git a/clang/tools/clang-check/ClangCheck.cpp b/clang/tools/clang-check/ClangCheck.cpp index b5b6bd5a14ae..d68e282949c8 100644 --- a/clang/tools/clang-check/ClangCheck.cpp +++ b/clang/tools/clang-check/ClangCheck.cpp @@ -50,13 +50,17 @@ cl::list SourcePaths( cl::desc(" [... ]"), cl::OneOrMore); -int main(int argc, char **argv) { - cl::ParseCommandLineOptions(argc, argv); - std::string ErrorMessage; +int main(int argc, const char **argv) { llvm::OwningPtr Compilations( - CompilationDatabase::loadFromDirectory(BuildPath, ErrorMessage)); - if (!Compilations) - llvm::report_fatal_error(ErrorMessage); + FixedCompilationDatabase::loadFromCommandLine(argc, argv)); + cl::ParseCommandLineOptions(argc, argv); + if (!Compilations) { + std::string ErrorMessage; + Compilations.reset(CompilationDatabase::loadFromDirectory(BuildPath, + ErrorMessage)); + if (!Compilations) + llvm::report_fatal_error(ErrorMessage); + } ClangTool Tool(*Compilations, SourcePaths); return Tool.run(newFrontendActionFactory()); } diff --git a/clang/unittests/Tooling/CompilationDatabaseTest.cpp b/clang/unittests/Tooling/CompilationDatabaseTest.cpp index 9747de25548c..68d2896f1212 100644 --- a/clang/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/clang/unittests/Tooling/CompilationDatabaseTest.cpp @@ -219,5 +219,74 @@ TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) { EXPECT_EQ("", Empty[0]); } +TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) { + std::vector CommandLine; + CommandLine.push_back("one"); + CommandLine.push_back("two"); + FixedCompilationDatabase Database(".", CommandLine); + std::vector Result = + Database.getCompileCommands("source"); + ASSERT_EQ(1ul, Result.size()); + std::vector ExpectedCommandLine(1, "clang-tool"); + ExpectedCommandLine.insert(ExpectedCommandLine.end(), + CommandLine.begin(), CommandLine.end()); + ExpectedCommandLine.push_back("source"); + EXPECT_EQ(".", Result[0].Directory); + EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine); +} + +TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) { + int Argc = 0; + llvm::OwningPtr Database( + FixedCompilationDatabase::loadFromCommandLine(Argc, NULL)); + EXPECT_FALSE(Database); + EXPECT_EQ(0, Argc); +} + +TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) { + int Argc = 2; + const char *Argv[] = { "1", "2" }; + llvm::OwningPtr Database( + FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); + EXPECT_FALSE(Database); + EXPECT_EQ(2, Argc); +} + +TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) { + int Argc = 5; + const char *Argv[] = { "1", "2", "--\0no-constant-folding", "3", "4" }; + llvm::OwningPtr Database( + FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); + ASSERT_TRUE(Database); + std::vector Result = + Database->getCompileCommands("source"); + ASSERT_EQ(1ul, Result.size()); + ASSERT_EQ(".", Result[0].Directory); + std::vector CommandLine; + CommandLine.push_back("clang-tool"); + CommandLine.push_back("3"); + CommandLine.push_back("4"); + CommandLine.push_back("source"); + ASSERT_EQ(CommandLine, Result[0].CommandLine); + EXPECT_EQ(2, Argc); +} + +TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) { + int Argc = 3; + const char *Argv[] = { "1", "2", "--\0no-constant-folding" }; + llvm::OwningPtr Database( + FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); + ASSERT_TRUE(Database); + std::vector Result = + Database->getCompileCommands("source"); + ASSERT_EQ(1ul, Result.size()); + ASSERT_EQ(".", Result[0].Directory); + std::vector CommandLine; + CommandLine.push_back("clang-tool"); + CommandLine.push_back("source"); + ASSERT_EQ(CommandLine, Result[0].CommandLine); + EXPECT_EQ(2, Argc); +} + } // end namespace tooling } // end namespace clang