From 399aea300f220ee3715cb22b32ac90bb57eca30d Mon Sep 17 00:00:00 2001 From: Joerg Sonnenberger Date: Thu, 1 Dec 2016 23:37:45 +0000 Subject: [PATCH] Extend CompilationDatabase by a field for the output filename 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 llvm-svn: 288436 --- clang/docs/JSONCompilationDatabase.rst | 3 +++ clang/include/clang/Tooling/CompilationDatabase.h | 8 ++++++-- clang/include/clang/Tooling/JSONCompilationDatabase.h | 8 +++++--- clang/lib/Tooling/CompilationDatabase.cpp | 3 ++- clang/lib/Tooling/JSONCompilationDatabase.cpp | 10 ++++++++-- clang/unittests/Tooling/CompilationDatabaseTest.cpp | 8 +++++++- 6 files changed, 31 insertions(+), 9 deletions(-) diff --git a/clang/docs/JSONCompilationDatabase.rst b/clang/docs/JSONCompilationDatabase.rst index 2b219e536b59..8631e8365cea 100644 --- a/clang/docs/JSONCompilationDatabase.rst +++ b/clang/docs/JSONCompilationDatabase.rst @@ -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 ======================== diff --git a/clang/include/clang/Tooling/CompilationDatabase.h b/clang/include/clang/Tooling/CompilationDatabase.h index 08a0ffec9d63..4611d3cdae5e 100644 --- a/clang/include/clang/Tooling/CompilationDatabase.h +++ b/clang/include/clang/Tooling/CompilationDatabase.h @@ -43,10 +43,11 @@ namespace tooling { struct CompileCommand { CompileCommand() {} CompileCommand(Twine Directory, Twine Filename, - std::vector CommandLine) + std::vector 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 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. diff --git a/clang/include/clang/Tooling/JSONCompilationDatabase.h b/clang/include/clang/Tooling/JSONCompilationDatabase.h index baf868e74d62..9a6866240b08 100644 --- a/clang/include/clang/Tooling/JSONCompilationDatabase.h +++ b/clang/include/clang/Tooling/JSONCompilationDatabase.h @@ -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> CompileCommandRef; + std::vector, + llvm::yaml::ScalarNode *> CompileCommandRef; /// \brief Converts the given array of CompileCommandRefs to CompileCommands. void getCommands(ArrayRef CommandsRef, diff --git a/clang/lib/Tooling/CompilationDatabase.cpp b/clang/lib/Tooling/CompilationDatabase.cpp index 01f62616fb0d..8ca0b2df7013 100644 --- a/clang/lib/Tooling/CompilationDatabase.cpp +++ b/clang/lib/Tooling/CompilationDatabase.cpp @@ -300,7 +300,8 @@ FixedCompilationDatabase(Twine Directory, ArrayRef CommandLine) { ToolCommandLine.insert(ToolCommandLine.end(), CommandLine.begin(), CommandLine.end()); CompileCommands.emplace_back(Directory, StringRef(), - std::move(ToolCommandLine)); + std::move(ToolCommandLine), + StringRef()); } std::vector diff --git a/clang/lib/Tooling/JSONCompilationDatabase.cpp b/clang/lib/Tooling/JSONCompilationDatabase.cpp index 152508fb7df1..738e610ed946 100644 --- a/clang/lib/Tooling/JSONCompilationDatabase.cpp +++ b/clang/lib/Tooling/JSONCompilationDatabase.cpp @@ -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> Command; llvm::yaml::ScalarNode *File = nullptr; + llvm::yaml::ScalarNode *Output = nullptr; for (auto& NextKeyValue : *Object) { llvm::yaml::ScalarNode *KeyString = dyn_cast(NextKeyValue.getKey()); @@ -331,6 +335,8 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { Command = std::vector(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); diff --git a/clang/unittests/Tooling/CompilationDatabaseTest.cpp b/clang/unittests/Tooling/CompilationDatabaseTest.cpp index 48539836e5f1..1a6fffec9392 100644 --- a/clang/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/clang/unittests/Tooling/CompilationDatabaseTest.cpp @@ -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 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 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; -- GitLab