r245036 - Add structed way to express command line options in the compilation database.

Manuel Klimek via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 14 02:55:37 PDT 2015


Author: klimek
Date: Fri Aug 14 04:55:36 2015
New Revision: 245036

URL: http://llvm.org/viewvc/llvm-project?rev=245036&view=rev
Log:
Add structed way to express command line options in the compilation database.

Currently, arguments are passed via the string attribute 'command',
assuming a shell-escaped / quoted command line to extract the original
arguments. This works well enough on Unix systems, but turns out to be
problematic for Windows tools to generate.

This CL adds a new attribute 'arguments', an array of strings, which
specifies the exact command line arguments. If 'arguments' is available
in the compilation database, it is preferred to 'commands'.

Currently there is no plan to retire 'commands': there are enough
different use cases where users want to create their own mechanism for
creating compilation databases, that it doesn't make sense to force them
all to implement shell command line parsing.

Patch by Daniel Dilts.

Modified:
    cfe/trunk/include/clang/Tooling/JSONCompilationDatabase.h
    cfe/trunk/lib/Tooling/JSONCompilationDatabase.cpp
    cfe/trunk/unittests/Tooling/CompilationDatabaseTest.cpp

Modified: cfe/trunk/include/clang/Tooling/JSONCompilationDatabase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/JSONCompilationDatabase.h?rev=245036&r1=245035&r2=245036&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/JSONCompilationDatabase.h (original)
+++ cfe/trunk/include/clang/Tooling/JSONCompilationDatabase.h Fri Aug 14 04:55:36 2015
@@ -33,18 +33,26 @@ namespace tooling {
 /// \brief A JSON based compilation database.
 ///
 /// JSON compilation database files must contain a list of JSON objects which
-/// provide the command lines in the attributes 'directory', 'command' and
-/// 'file':
+/// provide the command lines in the attributes 'directory', 'command',
+/// 'arguments' and 'file':
 /// [
 ///   { "directory": "<working directory of the compile>",
 ///     "command": "<compile command line>",
 ///     "file": "<path to source file>"
 ///   },
+///   { "directory": "<working directory of the compile>",
+///     "arguments": ["<raw>", "<command>" "<line>" "<parameters>"],
+///     "file": "<path to source file>"
+///   },
 ///   ...
 /// ]
 /// Each object entry defines one compile action. The specified file is
 /// considered to be the main source file for the translation unit.
 ///
+/// 'command' is a full command line that will be unescaped.
+///
+/// 'arguments' is a list of command line arguments that will not be unescaped.
+///
 /// JSON compilation databases can for example be generated in CMake projects
 /// by setting the flag -DCMAKE_EXPORT_COMPILE_COMMANDS.
 class JSONCompilationDatabase : public CompilationDatabase {
@@ -94,7 +102,7 @@ private:
   // Tuple (directory, commandline) where 'commandline' pointing to the
   // corresponding nodes in the YAML stream.
   typedef std::pair<llvm::yaml::ScalarNode*,
-                    llvm::yaml::ScalarNode*> CompileCommandRef;
+                    std::vector<std::string>> CompileCommandRef;
 
   /// \brief Converts the given array of CompileCommandRefs to CompileCommands.
   void getCommands(ArrayRef<CompileCommandRef> CommandsRef,

Modified: cfe/trunk/lib/Tooling/JSONCompilationDatabase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/JSONCompilationDatabase.cpp?rev=245036&r1=245035&r2=245036&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/JSONCompilationDatabase.cpp (original)
+++ cfe/trunk/lib/Tooling/JSONCompilationDatabase.cpp Fri Aug 14 04:55:36 2015
@@ -221,9 +221,8 @@ void JSONCompilationDatabase::getCommand
     SmallString<8> DirectoryStorage;
     SmallString<1024> CommandStorage;
     Commands.emplace_back(
-        // FIXME: Escape correctly:
-        CommandsRef[I].first->getValue(DirectoryStorage),
-        unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)));
+      CommandsRef[I].first->getValue(DirectoryStorage),
+      CommandsRef[I].second);
   }
 }
 
@@ -243,43 +242,59 @@ bool JSONCompilationDatabase::parse(std:
     ErrorMessage = "Expected array.";
     return false;
   }
-  for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
-                                          AE = Array->end();
-       AI != AE; ++AI) {
-    llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
+  for (auto& NextObject : *Array) {
+    llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&NextObject);
     if (!Object) {
       ErrorMessage = "Expected object.";
       return false;
     }
     llvm::yaml::ScalarNode *Directory = nullptr;
-    llvm::yaml::ScalarNode *Command = nullptr;
+    std::vector<std::string> Arguments;
+    std::vector<std::string> Command;
     llvm::yaml::ScalarNode *File = nullptr;
-    for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
-                                           KVE = Object->end();
-         KVI != KVE; ++KVI) {
-      llvm::yaml::Node *Value = (*KVI).getValue();
+    bool ArgumentsFound = false;
+    bool CommandFound = false;
+    for (auto& NextKeyValue : *Object) {
+      llvm::yaml::ScalarNode *KeyString =
+          dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+      if (!KeyString) {
+        ErrorMessage = "Expected strings as key.";
+        return false;
+      }
+      SmallString<10> KeyStorage;
+      StringRef KeyValue = KeyString->getValue(KeyStorage);
+      llvm::yaml::Node *Value = NextKeyValue.getValue();
       if (!Value) {
         ErrorMessage = "Expected value.";
         return false;
       }
       llvm::yaml::ScalarNode *ValueString =
           dyn_cast<llvm::yaml::ScalarNode>(Value);
-      if (!ValueString) {
-        ErrorMessage = "Expected string as value.";
+      llvm::yaml::SequenceNode *SequenceString =
+          dyn_cast<llvm::yaml::SequenceNode>(Value);
+      if (KeyValue == "arguments" && !SequenceString) {
+        ErrorMessage = "Expected sequence as value.";
         return false;
-      }
-      llvm::yaml::ScalarNode *KeyString =
-          dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
-      if (!KeyString) {
-        ErrorMessage = "Expected strings as key.";
+      } else if (KeyValue != "arguments" && !ValueString) {
+        ErrorMessage = "Expected string as value.";
         return false;
       }
-      SmallString<8> KeyStorage;
-      if (KeyString->getValue(KeyStorage) == "directory") {
+      if (KeyValue == "directory") {
         Directory = ValueString;
-      } else if (KeyString->getValue(KeyStorage) == "command") {
-        Command = ValueString;
-      } else if (KeyString->getValue(KeyStorage) == "file") {
+      } else if (KeyValue == "arguments") {
+        for (auto& NextArgument : *SequenceString) {
+          SmallString<128> CommandStorage;
+          auto ValueString = dyn_cast<llvm::yaml::ScalarNode>(&NextArgument);
+
+          Arguments.push_back(ValueString->getValue(CommandStorage));
+        }
+        ArgumentsFound = true;
+      } else if (KeyValue == "command") {
+        SmallString<1024> CommandStorage;
+        // FIXME: Escape correctly:
+        Command = unescapeCommandLine(ValueString->getValue(CommandStorage));
+        CommandFound = true;
+      } else if (KeyValue == "file") {
         File = ValueString;
       } else {
         ErrorMessage = ("Unknown key: \"" +
@@ -291,8 +306,8 @@ bool JSONCompilationDatabase::parse(std:
       ErrorMessage = "Missing key: \"file\".";
       return false;
     }
-    if (!Command) {
-      ErrorMessage = "Missing key: \"command\".";
+    if (!ArgumentsFound && !CommandFound) {
+      ErrorMessage = "Missing key: \"command\" or \"arguments\".";
       return false;
     }
     if (!Directory) {
@@ -312,7 +327,7 @@ bool JSONCompilationDatabase::parse(std:
       llvm::sys::path::native(FileName, NativeFilePath);
     }
     IndexByFile[NativeFilePath].push_back(
-        CompileCommandRef(Directory, Command));
+        CompileCommandRef(Directory, ArgumentsFound ? Arguments : Command));
     MatchTrie.insert(NativeFilePath);
   }
   return true;

Modified: cfe/trunk/unittests/Tooling/CompilationDatabaseTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/CompilationDatabaseTest.cpp?rev=245036&r1=245035&r2=245036&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/CompilationDatabaseTest.cpp (original)
+++ cfe/trunk/unittests/Tooling/CompilationDatabaseTest.cpp Fri Aug 14 04:55:36 2015
@@ -36,8 +36,12 @@ TEST(JSONCompilationDatabase, ErrsOnInva
   expectFailure("[{[]:\"\"}]", "Incorrectly typed entry");
   expectFailure("[{}]", "Empty entry");
   expectFailure("[{\"directory\":\"\",\"command\":\"\"}]", "Missing file");
-  expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command");
+  expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command or arguments");
   expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory");
+  expectFailure("[{\"directory\":\"\",\"arguments\":[]}]", "Missing file");
+  expectFailure("[{\"arguments\":\"\",\"file\":\"\"}]", "Missing directory");
+  expectFailure("[{\"directory\":\"\",\"arguments\":\"\",\"file\":\"\"}]", "Arguments not array");
+  expectFailure("[{\"directory\":\"\",\"command\":[],\"file\":\"\"}]", "Command not string");
 }
 
 static std::vector<std::string> getAllFiles(StringRef JSONDatabase,
@@ -126,6 +130,25 @@ static CompileCommand findCompileArgsInJ
   return Commands[0];
 }
 
+TEST(JSONCompilationDatabase, ArgumentsPreferredOverCommand) {
+   StringRef Directory("//net/dir");
+   StringRef FileName("//net/dir/filename");
+   StringRef Command("command");
+   StringRef Arguments = "arguments";
+   Twine ArgumentsAccumulate;
+   std::string ErrorMessage;
+   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
+      FileName,
+      ("[{\"directory\":\"" + Directory + "\","
+         "\"command\":\"" + Command + "\","
+         "\"arguments\":[\"" + Arguments + "\"],"
+         "\"file\":\"" + FileName + "\"}]").str(),
+      ErrorMessage);
+   EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
+   EXPECT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
+   EXPECT_EQ(Arguments, FoundCommand.CommandLine[0]) << ErrorMessage;
+}
+
 struct FakeComparator : public PathComparator {
   ~FakeComparator() override {}
   bool equivalent(StringRef FileA, StringRef FileB) const override {




More information about the cfe-commits mailing list