]> granicus.if.org Git - clang/commitdiff
Extend CompilationDatabase by a field for the output filename
authorJoerg Sonnenberger <joerg@bec.de>
Thu, 1 Dec 2016 23:37:45 +0000 (23:37 +0000)
committerJoerg Sonnenberger <joerg@bec.de>
Thu, 1 Dec 2016 23:37:45 +0000 (23:37 +0000)
In bigger projects like an Operating System, the same source code is
often compiled in slightly different ways. This could be the difference
between PIC and non-PIC code for static vs dynamic libraries, it could
also be the difference between size optimised versions of tools for
ramdisk images. At the moment, the compilation database has no way to
distinguish such cases. As first step, add a field in the JSON format
for it and process it accordingly.

Differential Revision: https://reviews.llvm.org/D27138

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@288436 91177308-0d34-0410-b5e6-96231b3b80d8

docs/JSONCompilationDatabase.rst
include/clang/Tooling/CompilationDatabase.h
include/clang/Tooling/JSONCompilationDatabase.h
lib/Tooling/CompilationDatabase.cpp
lib/Tooling/JSONCompilationDatabase.cpp
unittests/Tooling/CompilationDatabaseTest.cpp

index 2b219e536b59cc673671574d37f50715e4f1142d..8631e8365cead5018c446fad7a8a4e7f710d4988 100644 (file)
@@ -80,6 +80,9 @@ The contracts for each field in the command object are:
    supported.
 -  **arguments:** The compile command executed as list of strings.
    Either **arguments** or **command** is required.
+-  **output:** The name of the output created by this compilation step.
+   This field is optional. It can be used to distinguish different processing
+   modes of the same input file.
 
 Build System Integration
 ========================
index 08a0ffec9d63adee77c10722914805c4957ba4c2..4611d3cdae5ed1616a78cef360ddbb77d2cd0ae9 100644 (file)
@@ -43,10 +43,11 @@ namespace tooling {
 struct CompileCommand {
   CompileCommand() {}
   CompileCommand(Twine Directory, Twine Filename,
-                 std::vector<std::string> CommandLine)
+                 std::vector<std::string> CommandLine, Twine Output)
       : Directory(Directory.str()),
         Filename(Filename.str()),
-        CommandLine(std::move(CommandLine)) {}
+        CommandLine(std::move(CommandLine)),
+        Output(Output.str()){}
 
   /// \brief The working directory the command was executed from.
   std::string Directory;
@@ -57,6 +58,9 @@ struct CompileCommand {
   /// \brief The command line that was executed.
   std::vector<std::string> CommandLine;
 
+  /// The output file associated with the command.
+  std::string Output;
+
   /// \brief An optional mapping from each file's path to its content for all
   /// files needed for the compilation that are not available via the file
   /// system.
index baf868e74d622f888c6cd65c31c3565e1d257332..9a6866240b08caffdb2dede15950c2fea12c43a0 100644 (file)
@@ -103,15 +103,17 @@ private:
   /// failed.
   bool parse(std::string &ErrorMessage);
 
-  // Tuple (directory, filename, commandline) where 'commandline' points to the
-  // corresponding scalar nodes in the YAML stream.
+  // Tuple (directory, filename, commandline, output) where 'commandline'
+  // points 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.
   typedef std::tuple<llvm::yaml::ScalarNode *,
                      llvm::yaml::ScalarNode *,
-                    std::vector<llvm::yaml::ScalarNode *>> CompileCommandRef;
+                     std::vector<llvm::yaml::ScalarNode *>,
+                     llvm::yaml::ScalarNode *> CompileCommandRef;
 
   /// \brief Converts the given array of CompileCommandRefs to CompileCommands.
   void getCommands(ArrayRef<CompileCommandRef> CommandsRef,
index 01f62616fb0d5cd4227c4b7705dbc3d11bc940b7..8ca0b2df70130f1eae69371e9a0145bc07debd3b 100644 (file)
@@ -300,7 +300,8 @@ FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
   ToolCommandLine.insert(ToolCommandLine.end(),
                          CommandLine.begin(), CommandLine.end());
   CompileCommands.emplace_back(Directory, StringRef(),
-                               std::move(ToolCommandLine));
+                               std::move(ToolCommandLine),
+                               StringRef());
 }
 
 std::vector<CompileCommand>
index 152508fb7df126b1a1fb37bd99acb4134f887d0a..738e610ed946998e52dce3c995265098f02ab70c 100644 (file)
@@ -257,10 +257,13 @@ void JSONCompilationDatabase::getCommands(
   for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
     SmallString<8> DirectoryStorage;
     SmallString<32> FilenameStorage;
+    SmallString<32> OutputStorage;
+    auto Output = std::get<3>(CommandsRef[I]);
     Commands.emplace_back(
         std::get<0>(CommandsRef[I])->getValue(DirectoryStorage),
         std::get<1>(CommandsRef[I])->getValue(FilenameStorage),
-        nodeToCommandLine(Syntax, std::get<2>(CommandsRef[I])));
+        nodeToCommandLine(Syntax, std::get<2>(CommandsRef[I])),
+        Output ? Output->getValue(OutputStorage) : "");
   }
 }
 
@@ -289,6 +292,7 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
     llvm::yaml::ScalarNode *Directory = nullptr;
     llvm::Optional<std::vector<llvm::yaml::ScalarNode *>> Command;
     llvm::yaml::ScalarNode *File = nullptr;
+    llvm::yaml::ScalarNode *Output = nullptr;
     for (auto& NextKeyValue : *Object) {
       llvm::yaml::ScalarNode *KeyString =
           dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
@@ -331,6 +335,8 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
           Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString);
       } else if (KeyValue == "file") {
         File = ValueString;
+      } else if (KeyValue == "output") {
+        Output = ValueString;
       } else {
         ErrorMessage = ("Unknown key: \"" +
                         KeyString->getRawValue() + "\"").str();
@@ -361,7 +367,7 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
     } else {
       llvm::sys::path::native(FileName, NativeFilePath);
     }
-    auto Cmd = CompileCommandRef(Directory, File, *Command);
+    auto Cmd = CompileCommandRef(Directory, File, *Command, Output);
     IndexByFile[NativeFilePath].push_back(Cmd);
     AllCommands.push_back(Cmd);
     MatchTrie.insert(NativeFilePath);
index 48539836e5f1c1d0662b34bcac94b83bd5ded37e..1a6fffec9392a5ed1f81ac6945731be1662b3dd6 100644 (file)
@@ -44,6 +44,7 @@ TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) {
   expectFailure("[{\"directory\":\"\",\"command\":[],\"file\":\"\"}]", "Command not string");
   expectFailure("[{\"directory\":\"\",\"arguments\":[[]],\"file\":\"\"}]",
                 "Arguments contain non-string");
+  expectFailure("[{\"output\":[]}]", "Expected strings as value.");
 }
 
 static std::vector<std::string> getAllFiles(StringRef JSONDatabase,
@@ -105,16 +106,19 @@ TEST(JSONCompilationDatabase, GetAllCompileCommands) {
   StringRef Directory1("//net/dir1");
   StringRef FileName1("file1");
   StringRef Command1("command1");
+  StringRef Output1("file1.o");
   StringRef Directory2("//net/dir2");
   StringRef FileName2("file2");
   StringRef Command2("command2");
+  StringRef Output2("");
 
   std::vector<CompileCommand> Commands = getAllCompileCommands(
       JSONCommandLineSyntax::Gnu,
       ("[{\"directory\":\"" + Directory1 + "\"," + "\"command\":\"" + Command1 +
        "\","
        "\"file\":\"" +
-       FileName1 + "\"},"
+       FileName1 + "\", \"output\":\"" +
+       Output1 + "\"},"
                    " {\"directory\":\"" +
        Directory2 + "\"," + "\"command\":\"" + Command2 + "\","
                                                           "\"file\":\"" +
@@ -124,10 +128,12 @@ TEST(JSONCompilationDatabase, GetAllCompileCommands) {
   EXPECT_EQ(2U, Commands.size()) << ErrorMessage;
   EXPECT_EQ(Directory1, Commands[0].Directory) << ErrorMessage;
   EXPECT_EQ(FileName1, Commands[0].Filename) << ErrorMessage;
+  EXPECT_EQ(Output1, Commands[0].Output) << ErrorMessage;
   ASSERT_EQ(1u, Commands[0].CommandLine.size());
   EXPECT_EQ(Command1, Commands[0].CommandLine[0]) << ErrorMessage;
   EXPECT_EQ(Directory2, Commands[1].Directory) << ErrorMessage;
   EXPECT_EQ(FileName2, Commands[1].Filename) << ErrorMessage;
+  EXPECT_EQ(Output2, Commands[1].Output) << ErrorMessage;
   ASSERT_EQ(1u, Commands[1].CommandLine.size());
   EXPECT_EQ(Command2, Commands[1].CommandLine[0]) << ErrorMessage;