diff --git a/clang/include/clang/ExtractAPI/FrontendActions.h b/clang/include/clang/ExtractAPI/FrontendActions.h --- a/clang/include/clang/ExtractAPI/FrontendActions.h +++ b/clang/include/clang/ExtractAPI/FrontendActions.h @@ -24,9 +24,22 @@ std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override; +private: + /// The synthesized input buffer that contains all the provided input header + /// files. + std::unique_ptr Buffer; + public: + /// Prepare to execute the action on the given CompilerInstance. + /// + /// This is called before executing the action on any inputs. This generates a + /// single header that includes all of CI's inputs and replaces CI's input + /// list with it before actually executing the action. + bool PrepareToExecuteAction(CompilerInstance &CI) override; + static std::unique_ptr CreateOutputFile(CompilerInstance &CI, StringRef InFile); + static StringRef getInputBufferName() { return ""; } }; } // namespace clang diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp --- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -27,6 +27,9 @@ #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" #include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendOptions.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -339,6 +342,35 @@ std::move(OS)); } +bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { + auto &Inputs = CI.getFrontendOpts().Inputs; + if (Inputs.empty()) + return true; + + auto Kind = Inputs[0].getKind(); + + // Convert the header file inputs into a single input buffer. + SmallString<256> HeaderContents; + for (const FrontendInputFile &FIF : Inputs) { + if (Kind.isObjectiveC()) + HeaderContents += "#import"; + else + HeaderContents += "#include"; + HeaderContents += " \""; + HeaderContents += FIF.getFile(); + HeaderContents += "\"\n"; + } + + Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, + getInputBufferName()); + + // Set that buffer up as our "real" input in the CompilerInstance. + Inputs.clear(); + Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); + + return true; +} + std::unique_ptr ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { std::unique_ptr OS = diff --git a/clang/test/ExtractAPI/global_record_multifile.c b/clang/test/ExtractAPI/global_record_multifile.c new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/global_record_multifile.c @@ -0,0 +1,371 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%/t@g" %t/reference.output.json.in >> \ +// RUN: %t/reference.output.json +// RUN: %clang -extract-api --product-name=GlobalRecord -target arm64-apple-macosx \ +// RUN: %t/input1.h %t/input2.h %t/input3.h -o %t/output.json | FileCheck -allow-empty %s + +// Generator version is not consistent across test runs, normalize it. +// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \ +// RUN: %t/output.json >> %t/output-normalized.json +// RUN: diff %t/reference.output.json %t/output-normalized.json + +// CHECK-NOT: error: +// CHECK-NOT: warning: + +//--- input1.h +int num; + +//--- input2.h +/** + * \brief Add two numbers. + * \param [in] x A number. + * \param [in] y Another number. + * \param [out] res The result of x + y. + */ +void add(const int x, const int y, int *res); + +//--- input3.h +char unavailable __attribute__((unavailable)); + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "GlobalRecord", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationhips": [], + "symbols": [ + { + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "num" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@num" + }, + "kind": { + "displayName": "Global Variable", + "identifier": "c.var" + }, + "location": { + "character": 5, + "line": 1, + "uri": "file://INPUT_DIR/input1.h" + }, + "names": { + "subHeading": [ + { + "kind": "identifier", + "spelling": "num" + } + ], + "title": "num" + } + }, + { + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "add" + }, + { + "kind": "text", + "spelling": "(" + }, + { + "kind": "keyword", + "spelling": "const" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "internalParam", + "spelling": "x" + }, + { + "kind": "text", + "spelling": ", " + }, + { + "kind": "keyword", + "spelling": "const" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "internalParam", + "spelling": "y" + }, + { + "kind": "text", + "spelling": ", " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " *" + }, + { + "kind": "internalParam", + "spelling": "res" + }, + { + "kind": "text", + "spelling": ")" + } + ], + "docComment": { + "lines": [ + { + "range": { + "end": { + "character": 4, + "line": 1 + }, + "start": { + "character": 4, + "line": 1 + } + }, + "text": "" + }, + { + "range": { + "end": { + "character": 27, + "line": 2 + }, + "start": { + "character": 3, + "line": 2 + } + }, + "text": " \\brief Add two numbers." + }, + { + "range": { + "end": { + "character": 30, + "line": 3 + }, + "start": { + "character": 3, + "line": 3 + } + }, + "text": " \\param [in] x A number." + }, + { + "range": { + "end": { + "character": 36, + "line": 4 + }, + "start": { + "character": 3, + "line": 4 + } + }, + "text": " \\param [in] y Another number." + }, + { + "range": { + "end": { + "character": 41, + "line": 5 + }, + "start": { + "character": 3, + "line": 5 + } + }, + "text": " \\param [out] res The result of x + y." + }, + { + "range": { + "end": { + "character": 4, + "line": 6 + }, + "start": { + "character": 1, + "line": 6 + } + }, + "text": " " + } + ] + }, + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@F@add" + }, + "kind": { + "displayName": "Function", + "identifier": "c.func" + }, + "location": { + "character": 6, + "line": 7, + "uri": "file://INPUT_DIR/input2.h" + }, + "names": { + "subHeading": [ + { + "kind": "identifier", + "spelling": "add" + } + ], + "title": "add" + }, + "parameters": { + "parameters": [ + { + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "const" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "internalParam", + "spelling": "x" + } + ], + "name": "x" + }, + { + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "const" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "internalParam", + "spelling": "y" + } + ], + "name": "y" + }, + { + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " *" + }, + { + "kind": "internalParam", + "spelling": "res" + } + ], + "name": "res" + } + ], + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + } + } + ] +}