1 //===-- Implementation of PublicAPICommand --------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "PublicAPICommand.h"
10
11 #include "utils/LibcTableGenUtil/APIIndexer.h"
12
13 #include "llvm/ADT/StringExtras.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/Support/SourceMgr.h"
16 #include "llvm/TableGen/Record.h"
17
18 // Text blocks for macro definitions and type decls can be indented to
19 // suit the surrounding tablegen listing. We need to dedent such blocks
20 // before writing them out.
dedentAndWrite(llvm::StringRef Text,llvm::raw_ostream & OS)21 static void dedentAndWrite(llvm::StringRef Text, llvm::raw_ostream &OS) {
22 llvm::SmallVector<llvm::StringRef, 10> Lines;
23 llvm::SplitString(Text, Lines, "\n");
24 size_t shortest_indent = 1024;
25 for (llvm::StringRef L : Lines) {
26 llvm::StringRef Indent = L.take_while([](char c) { return c == ' '; });
27 size_t IndentSize = Indent.size();
28 if (Indent.size() == L.size()) {
29 // Line is all spaces so no point noting the indent.
30 continue;
31 }
32 if (IndentSize < shortest_indent)
33 shortest_indent = IndentSize;
34 }
35 for (llvm::StringRef L : Lines) {
36 if (L.size() >= shortest_indent)
37 OS << L.drop_front(shortest_indent) << '\n';
38 }
39 }
40
41 namespace llvm_libc {
42
writeAPIFromIndex(APIIndexer & G,std::vector<std::string> EntrypointNameList,llvm::raw_ostream & OS)43 void writeAPIFromIndex(APIIndexer &G,
44 std::vector<std::string> EntrypointNameList,
45 llvm::raw_ostream &OS) {
46 for (auto &Pair : G.MacroDefsMap) {
47 const std::string &Name = Pair.first;
48 if (G.MacroSpecMap.find(Name) == G.MacroSpecMap.end())
49 llvm::PrintFatalError(Name + " not found in any standard spec.\n");
50
51 llvm::Record *MacroDef = Pair.second;
52 dedentAndWrite(MacroDef->getValueAsString("Defn"), OS);
53
54 OS << '\n';
55 }
56
57 for (auto &Pair : G.TypeDeclsMap) {
58 const std::string &Name = Pair.first;
59 if (G.TypeSpecMap.find(Name) == G.TypeSpecMap.end())
60 llvm::PrintFatalError(Name + " not found in any standard spec.\n");
61
62 llvm::Record *TypeDecl = Pair.second;
63 dedentAndWrite(TypeDecl->getValueAsString("Decl"), OS);
64
65 OS << '\n';
66 }
67
68 if (G.Enumerations.size() != 0)
69 OS << "enum {" << '\n';
70 for (const auto &Name : G.Enumerations) {
71 if (G.EnumerationSpecMap.find(Name) == G.EnumerationSpecMap.end())
72 llvm::PrintFatalError(
73 Name + " is not listed as an enumeration in any standard spec.\n");
74
75 llvm::Record *EnumerationSpec = G.EnumerationSpecMap[Name];
76 OS << " " << EnumerationSpec->getValueAsString("Name");
77 auto Value = EnumerationSpec->getValueAsString("Value");
78 if (Value == "__default__") {
79 OS << ",\n";
80 } else {
81 OS << " = " << Value << ",\n";
82 }
83 }
84 if (G.Enumerations.size() != 0)
85 OS << "};\n\n";
86
87 OS << "__BEGIN_C_DECLS\n\n";
88 for (auto &Name : EntrypointNameList) {
89 if (G.FunctionSpecMap.find(Name) == G.FunctionSpecMap.end()) {
90 continue; // Functions that aren't in this header file are skipped as
91 // opposed to erroring out because the list of functions being
92 // iterated over is the complete list of functions with
93 // entrypoints. Thus this is filtering out the functions that
94 // don't go to this header file, whereas the other, similar
95 // conditionals above are more of a sanity check.
96 }
97
98 llvm::Record *FunctionSpec = G.FunctionSpecMap[Name];
99 llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
100 llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
101
102 OS << G.getTypeAsString(ReturnType) << " " << Name << "(";
103
104 auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
105 for (size_t i = 0; i < ArgsList.size(); ++i) {
106 llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType");
107 OS << G.getTypeAsString(ArgType);
108 if (i < ArgsList.size() - 1)
109 OS << ", ";
110 }
111
112 OS << ");\n\n";
113 }
114 OS << "__END_C_DECLS\n";
115 }
116
writePublicAPI(llvm::raw_ostream & OS,llvm::RecordKeeper & Records)117 void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {}
118
119 const char PublicAPICommand::Name[] = "public_api";
120
run(llvm::raw_ostream & OS,const ArgVector & Args,llvm::StringRef StdHeader,llvm::RecordKeeper & Records,const Command::ErrorReporter & Reporter) const121 void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args,
122 llvm::StringRef StdHeader,
123 llvm::RecordKeeper &Records,
124 const Command::ErrorReporter &Reporter) const {
125 if (Args.size() != 0) {
126 Reporter.printFatalError("public_api command does not take any arguments.");
127 }
128
129 APIIndexer G(StdHeader, Records);
130 writeAPIFromIndex(G, EntrypointNameList, OS);
131 }
132
133 } // namespace llvm_libc
134