dang updated this revision to Diff 417551.
dang added a comment.
Rebasing on top of latest main which includes
https://reviews.llvm.org/rG89f6b26f1beb2c1344f5cfeb34e405128544c76b
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D122141/new/
https://reviews.llvm.org/D122141
Files:
clang/include/clang/Driver/Options.td
clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
clang/include/clang/Frontend/FrontendOptions.h
clang/include/clang/SymbolGraph/Serialization.h
clang/lib/Driver/ToolChains/Clang.cpp
clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
clang/lib/SymbolGraph/Serialization.cpp
clang/test/ExtractAPI/global_record.c
Index: clang/test/ExtractAPI/global_record.c
===================================================================
--- clang/test/ExtractAPI/global_record.c
+++ clang/test/ExtractAPI/global_record.c
@@ -2,7 +2,7 @@
// 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 -target arm64-apple-macosx \
+// RUN: %clang -extract-api --product-name=GlobalRecord -target arm64-apple-macosx \
// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
// Generator version is not consistent across test runs, normalize it.
@@ -37,7 +37,7 @@
"generator": "?"
},
"module": {
- "name": "",
+ "name": "GlobalRecord",
"platform": {
"architecture": "arm64",
"operatingSystem": {
Index: clang/lib/SymbolGraph/Serialization.cpp
===================================================================
--- /dev/null
+++ clang/lib/SymbolGraph/Serialization.cpp
@@ -0,0 +1,331 @@
+//===- SymbolGraph/Serialization.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Defines the SymbolGraph serializer and parser.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/SymbolGraph/Serialization.h"
+#include "clang/Basic/Version.h"
+#include "clang/SymbolGraph/API.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/VersionTuple.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace clang::symbolgraph;
+using namespace llvm;
+using namespace llvm::json;
+
+namespace {
+
+static void serializeObject(Object &Paren, StringRef Key,
+ Optional<Object> Obj) {
+ if (Obj)
+ Paren[Key] = std::move(Obj.getValue());
+}
+
+static void serializeArray(Object &Paren, StringRef Key,
+ Optional<Array> Array) {
+ if (Array)
+ Paren[Key] = std::move(Array.getValue());
+}
+
+// SymbolGraph: SemanticVersion
+static Optional<Object> serializeSemanticVersion(const VersionTuple &V) {
+ if (V.empty())
+ return None;
+
+ Object Version;
+ Version["major"] = V.getMajor();
+ Version["minor"] = V.getMinor().getValueOr(0);
+ Version["patch"] = V.getSubminor().getValueOr(0);
+ return Version;
+}
+
+static Object serializeOperatingSystem(const Triple &T) {
+ Object OS;
+ OS["name"] = T.getOSTypeName(T.getOS());
+ serializeObject(OS, "minimumVersion",
+ serializeSemanticVersion(T.getMinimumSupportedOSVersion()));
+ return OS;
+}
+
+// SymbolGraph: Platform
+static Object serializePlatform(const Triple &T) {
+ Object Platform;
+ Platform["architecture"] = T.getArchName();
+ Platform["vendor"] = T.getVendorName();
+ Platform["operatingSystem"] = serializeOperatingSystem(T);
+ return Platform;
+}
+
+// SymbolGraph: SourcePosition
+static Object serializeSourcePosition(const PresumedLoc &Loc,
+ bool IncludeFileURI = false) {
+ assert(Loc.isValid() && "invalid source position");
+
+ Object SourcePosition;
+ SourcePosition["line"] = Loc.getLine();
+ SourcePosition["character"] = Loc.getColumn();
+
+ if (IncludeFileURI) {
+ std::string FileURI = "file://";
+ FileURI += sys::path::convert_to_slash(Loc.getFilename());
+ SourcePosition["uri"] = FileURI;
+ }
+
+ return SourcePosition;
+}
+
+// SymbolGraph: SourceRange
+static Object serializeSourceRange(const PresumedLoc &BeginLoc,
+ const PresumedLoc &EndLoc) {
+ Object SourceRange;
+ serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc));
+ serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc));
+ return SourceRange;
+}
+
+// SymbolGraph: AvailabilityItem
+static Optional<Object> serializeAvailability(const AvailabilityInfo &Avail) {
+ if (Avail.isDefault())
+ return None;
+
+ Object Availbility;
+ serializeObject(Availbility, "introducedVersion",
+ serializeSemanticVersion(Avail.Introduced));
+ serializeObject(Availbility, "deprecatedVersion",
+ serializeSemanticVersion(Avail.Deprecated));
+ serializeObject(Availbility, "obsoletedVersion",
+ serializeSemanticVersion(Avail.Obsoleted));
+ if (Avail.isUnavailable())
+ Availbility["isUnconditionallyUnavailable"] = true;
+ if (Avail.isUnconditionallyDeprecated())
+ Availbility["isUnconditionallyDeprecated"] = true;
+
+ return Availbility;
+}
+
+static StringRef getLanguageName(const LangOptions &LangOpts) {
+ auto Language =
+ LangStandard::getLangStandardForKind(LangOpts.LangStd).getLanguage();
+ switch (Language) {
+ case Language::C:
+ return "c";
+ case Language::ObjC:
+ return "objc";
+
+ // Unsupported language currently
+ case Language::CXX:
+ case Language::ObjCXX:
+ case Language::OpenCL:
+ case Language::OpenCLCXX:
+ case Language::CUDA:
+ case Language::RenderScript:
+ case Language::HIP:
+
+ // Languages that the frontend cannot parse and compile
+ case Language::Unknown:
+ case Language::Asm:
+ case Language::LLVM_IR:
+ llvm_unreachable("Unsupported language kind");
+ }
+
+ llvm_unreachable("Unhandled language kind");
+}
+
+// SymbolGraph: Symbol::identifier
+static Object serializeIdentifier(const APIRecord &Record,
+ const LangOptions &LangOpts) {
+ Object Identifier;
+ Identifier["precise"] = Record.USR;
+ Identifier["interfaceLanguage"] = getLanguageName(LangOpts);
+
+ return Identifier;
+}
+
+// SymbolGraph: DocComment
+static Optional<Object> serializeDocComment(const DocComment &Comment) {
+ if (Comment.empty())
+ return None;
+
+ Object DocComment;
+ Array LinesArray;
+ for (const auto &CommentLine : Comment) {
+ Object Line;
+ Line["text"] = CommentLine.Text;
+ serializeObject(Line, "range",
+ serializeSourceRange(CommentLine.Begin, CommentLine.End));
+ LinesArray.emplace_back(std::move(Line));
+ }
+ serializeArray(DocComment, "lines", LinesArray);
+
+ return DocComment;
+}
+
+static Optional<Array>
+serializeDeclarationFragments(const DeclarationFragments &DF) {
+ if (DF.getFragments().empty())
+ return None;
+
+ Array Fragments;
+ for (const auto &F : DF.getFragments()) {
+ Object Fragment;
+ Fragment["spelling"] = F.Spelling;
+ Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind);
+ if (!F.PreciseIdentifier.empty())
+ Fragment["preciseIdentifier"] = F.PreciseIdentifier;
+ Fragments.emplace_back(std::move(Fragment));
+ }
+
+ return Fragments;
+}
+
+static Optional<Object>
+serializeFunctionSignature(const FunctionSignature &FS) {
+ if (FS.empty())
+ return None;
+
+ Object Signature;
+ serializeArray(Signature, "returns",
+ serializeDeclarationFragments(FS.getReturnType()));
+
+ Array Parameters;
+ for (const auto &P : FS.getParameters()) {
+ Object Parameter;
+ Parameter["name"] = P.Name;
+ serializeArray(Parameter, "declarationFragments",
+ serializeDeclarationFragments(P.Fragments));
+ Parameters.emplace_back(std::move(Parameter));
+ }
+
+ if (!Parameters.empty())
+ Signature["parameters"] = std::move(Parameters);
+
+ return Signature;
+}
+
+static Object serializeNames(const APIRecord &Record) {
+ Object Names;
+ Names["title"] = Record.Name;
+ serializeArray(Names, "subHeading",
+ serializeDeclarationFragments(Record.SubHeading));
+
+ return Names;
+}
+
+// SymbolGraph: Symbol::kind
+static Object serializeSymbolKind(const APIRecord &Record,
+ const LangOptions &LangOpts) {
+ Object Kind;
+ switch (Record.getKind()) {
+ case APIRecord::RK_Global:
+ auto *GR = dyn_cast<GlobalRecord>(&Record);
+ switch (GR->GlobalKind) {
+ case GVKind::Function:
+ Kind["identifier"] = (getLanguageName(LangOpts) + ".func").str();
+ Kind["displayName"] = "Function";
+ break;
+ case GVKind::Variable:
+ Kind["identifier"] = (getLanguageName(LangOpts) + ".var").str();
+ Kind["displayName"] = "Global Variable";
+ break;
+ case GVKind::Unknown:
+ // Unknown global kind
+ break;
+ }
+ break;
+ }
+
+ return Kind;
+}
+
+} // namespace
+
+const VersionTuple Serializer::FormatVersion{0, 5, 3};
+
+Object Serializer::serializeMetadata() const {
+ Object Metadata;
+ serializeObject(Metadata, "formatVersion",
+ serializeSemanticVersion(FormatVersion));
+ Metadata["generator"] = clang::getClangFullVersion();
+ return Metadata;
+}
+
+Object Serializer::serializeModule() const {
+ Object Module;
+ Module["name"] = ProductName;
+ serializeObject(Module, "platform", serializePlatform(API.getTarget()));
+ return Module;
+}
+
+bool Serializer::shouldSkip(const APIRecord &Record) const {
+ // Skip unconditionally unavailable symbols
+ if (Record.Availability.isUnconditionallyUnavailable())
+ return true;
+
+ return false;
+}
+
+Optional<Object> Serializer::serializeAPIRecord(const APIRecord &Record) const {
+ if (shouldSkip(Record))
+ return None;
+
+ Object Obj;
+ serializeObject(Obj, "identifier",
+ serializeIdentifier(Record, API.getLangOpts()));
+ serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLangOpts()));
+ serializeObject(Obj, "names", serializeNames(Record));
+ serializeObject(
+ Obj, "location",
+ serializeSourcePosition(Record.Location, /*IncludeFileURI=*/true));
+ serializeObject(Obj, "availbility",
+ serializeAvailability(Record.Availability));
+ serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
+ serializeArray(Obj, "declarationFragments",
+ serializeDeclarationFragments(Record.Declaration));
+
+ return Obj;
+}
+
+void Serializer::serializeGlobalRecord(const GlobalRecord &Record) {
+ auto Obj = serializeAPIRecord(Record);
+ if (!Obj)
+ return;
+
+ if (Record.GlobalKind == GVKind::Function)
+ serializeObject(*Obj, "parameters",
+ serializeFunctionSignature(Record.Signature));
+
+ Symbols.emplace_back(std::move(*Obj));
+}
+
+Object Serializer::serialize() {
+ Object Root;
+ serializeObject(Root, "metadata", serializeMetadata());
+ serializeObject(Root, "module", serializeModule());
+
+ for (const auto &Global : API.getGlobals())
+ serializeGlobalRecord(*Global.second);
+
+ Root["symbols"] = std::move(Symbols);
+ Root["relationhips"] = std::move(Relationships);
+
+ return Root;
+}
+
+void Serializer::serialize(raw_ostream &os) {
+ Object root = serialize();
+ if (Options.Compact)
+ os << formatv("{0}", Value(std::move(root))) << "\n";
+ else
+ os << formatv("{0:2}", Value(std::move(root))) << "\n";
+}
Index: clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
===================================================================
--- clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -376,10 +376,9 @@
Object SymbolGraphSerializer::serializeModule() const {
Object Module;
- // FIXME: We might not be building a module, some Clang-based languages might
- // not have a "module" concept. Figure out a way to provide a name to
- // describe the API set.
- Module["name"] = "";
+ // The user is expected to always pass `--product-name=` on the command line
+ // to populate this field.
+ Module["name"] = ProductName;
serializeObject(Module, "platform", serializePlatform(API.getTarget()));
return Module;
}
Index: clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
===================================================================
--- clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -183,8 +183,9 @@
class ExtractAPIConsumer : public ASTConsumer {
public:
- ExtractAPIConsumer(ASTContext &Context, std::unique_ptr<raw_pwrite_stream> OS)
- : Visitor(Context), OS(std::move(OS)) {}
+ ExtractAPIConsumer(ASTContext &Context, StringRef ProductName,
+ std::unique_ptr<raw_pwrite_stream> OS)
+ : Visitor(Context), ProductName(ProductName), OS(std::move(OS)) {}
void HandleTranslationUnit(ASTContext &Context) override {
// Use ExtractAPIVisitor to traverse symbol declarations in the context.
@@ -199,6 +200,7 @@
private:
ExtractAPIVisitor Visitor;
+ std::string ProductName;
std::unique_ptr<raw_pwrite_stream> OS;
};
@@ -209,8 +211,9 @@
std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
if (!OS)
return nullptr;
- return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
- std::move(OS));
+ return std::make_unique<ExtractAPIConsumer>(
+ CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName,
+ std::move(OS));
}
std::unique_ptr<raw_pwrite_stream>
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -4641,6 +4641,8 @@
assert(JA.getType() == types::TY_API_INFO &&
"Extract API actions must generate a API information.");
CmdArgs.push_back("-extract-api");
+ if (Arg *ProductNameArg = Args.getLastArg(options::OPT_product_name_EQ))
+ ProductNameArg->render(Args, CmdArgs);
} else {
assert((isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) &&
"Invalid action for clang tool.");
Index: clang/include/clang/SymbolGraph/Serialization.h
===================================================================
--- /dev/null
+++ clang/include/clang/SymbolGraph/Serialization.h
@@ -0,0 +1,60 @@
+//===- SymbolGraph/Serialization.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Defines the SymbolGraph serializer and parser.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SYMBOLGRAPH_SERIALIZATION_H
+#define LLVM_CLANG_SYMBOLGRAPH_SERIALIZATION_H
+
+#include "clang/SymbolGraph/API.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/VersionTuple.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace symbolgraph {
+
+using namespace llvm::json;
+
+struct SerializerOption {
+ bool Compact;
+};
+
+class Serializer {
+public:
+ Serializer(const APISet &API, StringRef ProductName,
+ SerializerOption Options = {})
+ : API(API), ProductName(ProductName), Options(Options) {}
+
+ Object serialize();
+ void serialize(raw_ostream &os);
+
+private:
+ Object serializeMetadata() const;
+ Object serializeModule() const;
+ Optional<Object> serializeAPIRecord(const APIRecord &Record) const;
+ void serializeGlobalRecord(const GlobalRecord &Record);
+
+ bool shouldSkip(const APIRecord &Record) const;
+
+ const APISet &API;
+ StringRef ProductName;
+ SerializerOption Options;
+ Array Symbols;
+ Array Relationships;
+
+ static const VersionTuple FormatVersion;
+};
+
+} // namespace symbolgraph
+} // namespace clang
+
+#endif // LLVM_CLANG_SYMBOLGRAPH_SERIALIZATION_H
Index: clang/include/clang/Frontend/FrontendOptions.h
===================================================================
--- clang/include/clang/Frontend/FrontendOptions.h
+++ clang/include/clang/Frontend/FrontendOptions.h
@@ -410,6 +410,10 @@
/// The name of the action to run when using a plugin action.
std::string ActionName;
+ // Currently this is only used as part of the `-extract-api` action.
+ /// The name of the product the input files belong too.
+ std::string ProductName;
+
/// Args to pass to the plugins
std::map<std::string, std::vector<std::string>> PluginArgs;
Index: clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
===================================================================
--- clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
+++ clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
@@ -34,6 +34,12 @@
protected:
const APISet &API;
+
+ /// The product name of API.
+ ///
+ /// Note: This should be used for populating metadata about the API.
+ StringRef ProductName;
+
APISerializerOption Options;
public:
@@ -44,8 +50,9 @@
APISerializer &operator=(APISerializer &&) = delete;
protected:
- APISerializer(const APISet &API, APISerializerOption Options = {})
- : API(API), Options(Options) {}
+ APISerializer(const APISet &API, StringRef ProductName,
+ APISerializerOption Options = {})
+ : API(API), ProductName(ProductName), Options(Options) {}
virtual ~APISerializer() = default;
};
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -1091,6 +1091,8 @@
def exported__symbols__list : Separate<["-"], "exported_symbols_list">;
def extract_api : Flag<["-"], "extract-api">, Flags<[CC1Option]>, Group<Action_Group>,
HelpText<"Extract API information">;
+def product_name_EQ: Joined<["--"], "product-name=">, Flags<[CC1Option]>,
+ MarshallingInfoString<FrontendOpts<"ProductName">>;
def e : JoinedOrSeparate<["-"], "e">, Flags<[LinkerInput]>, Group<Link_Group>;
def fmax_tokens_EQ : Joined<["-"], "fmax-tokens=">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Max total number of preprocessed tokens for -Wmax-tokens.">,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits