1 //===-- Implementation of PublicAPICommand --------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "PublicAPICommand.h"
11 #include "utils/LibcTableGenUtil/APIIndexer.h"
13 #include "llvm/ADT/SmallVector.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/SourceMgr.h"
17 #include "llvm/TableGen/Record.h"
19 // Text blocks for macro definitions and type decls can be indented to
20 // suit the surrounding tablegen listing. We need to dedent such blocks
21 // before writing them out.
22 static void dedentAndWrite(llvm::StringRef Text
, llvm::raw_ostream
&OS
) {
23 llvm::SmallVector
<llvm::StringRef
, 10> Lines
;
24 llvm::SplitString(Text
, Lines
, "\n");
25 size_t shortest_indent
= 1024;
26 for (llvm::StringRef L
: Lines
) {
27 llvm::StringRef Indent
= L
.take_while([](char c
) { return c
== ' '; });
28 size_t IndentSize
= Indent
.size();
29 if (Indent
.size() == L
.size()) {
30 // Line is all spaces so no point noting the indent.
33 if (IndentSize
< shortest_indent
)
34 shortest_indent
= IndentSize
;
36 for (llvm::StringRef L
: Lines
) {
37 if (L
.size() >= shortest_indent
)
38 OS
<< L
.drop_front(shortest_indent
) << '\n';
42 static std::string
getTypeHdrName(const std::string
&Name
) {
43 llvm::SmallVector
<llvm::StringRef
> Parts
;
44 llvm::SplitString(llvm::StringRef(Name
), Parts
);
45 return llvm::join(Parts
.begin(), Parts
.end(), "_");
50 void writeAPIFromIndex(APIIndexer
&G
,
51 std::vector
<std::string
> EntrypointNameList
,
52 llvm::raw_ostream
&OS
) {
53 for (auto &Pair
: G
.MacroDefsMap
) {
54 const std::string
&Name
= Pair
.first
;
55 if (G
.MacroSpecMap
.find(Name
) == G
.MacroSpecMap
.end())
56 llvm::PrintFatalError(Name
+ " not found in any standard spec.\n");
58 llvm::Record
*MacroDef
= Pair
.second
;
59 dedentAndWrite(MacroDef
->getValueAsString("Defn"), OS
);
64 for (auto &TypeName
: G
.RequiredTypes
) {
65 if (G
.TypeSpecMap
.find(TypeName
) == G
.TypeSpecMap
.end())
66 llvm::PrintFatalError(TypeName
+ " not found in any standard spec.\n");
67 OS
<< "#include <llvm-libc-types/" << getTypeHdrName(TypeName
) << ".h>\n";
71 if (G
.Enumerations
.size() != 0)
72 OS
<< "enum {" << '\n';
73 for (const auto &Name
: G
.Enumerations
) {
74 if (G
.EnumerationSpecMap
.find(Name
) == G
.EnumerationSpecMap
.end())
75 llvm::PrintFatalError(
76 Name
+ " is not listed as an enumeration in any standard spec.\n");
78 llvm::Record
*EnumerationSpec
= G
.EnumerationSpecMap
[Name
];
79 OS
<< " " << EnumerationSpec
->getValueAsString("Name");
80 auto Value
= EnumerationSpec
->getValueAsString("Value");
81 if (Value
== "__default__") {
84 OS
<< " = " << Value
<< ",\n";
87 if (G
.Enumerations
.size() != 0)
90 OS
<< "__BEGIN_C_DECLS\n\n";
91 for (auto &Name
: EntrypointNameList
) {
92 if (G
.FunctionSpecMap
.find(Name
) == G
.FunctionSpecMap
.end()) {
93 continue; // Functions that aren't in this header file are skipped as
94 // opposed to erroring out because the list of functions being
95 // iterated over is the complete list of functions with
96 // entrypoints. Thus this is filtering out the functions that
97 // don't go to this header file, whereas the other, similar
98 // conditionals above are more of a sanity check.
101 llvm::Record
*FunctionSpec
= G
.FunctionSpecMap
[Name
];
102 llvm::Record
*RetValSpec
= FunctionSpec
->getValueAsDef("Return");
103 llvm::Record
*ReturnType
= RetValSpec
->getValueAsDef("ReturnType");
105 OS
<< G
.getTypeAsString(ReturnType
) << " " << Name
<< "(";
107 auto ArgsList
= FunctionSpec
->getValueAsListOfDefs("Args");
108 for (size_t i
= 0; i
< ArgsList
.size(); ++i
) {
109 llvm::Record
*ArgType
= ArgsList
[i
]->getValueAsDef("ArgType");
110 OS
<< G
.getTypeAsString(ArgType
);
111 if (i
< ArgsList
.size() - 1)
115 OS
<< ") __NOEXCEPT;\n\n";
118 // Make another pass over entrypoints to emit object declarations.
119 for (const auto &Name
: EntrypointNameList
) {
120 if (G
.ObjectSpecMap
.find(Name
) == G
.ObjectSpecMap
.end())
122 llvm::Record
*ObjectSpec
= G
.ObjectSpecMap
[Name
];
123 auto Type
= ObjectSpec
->getValueAsString("Type");
124 OS
<< "extern " << Type
<< " " << Name
<< ";\n";
126 OS
<< "__END_C_DECLS\n";
129 void writePublicAPI(llvm::raw_ostream
&OS
, llvm::RecordKeeper
&Records
) {}
131 const char PublicAPICommand::Name
[] = "public_api";
133 void PublicAPICommand::run(llvm::raw_ostream
&OS
, const ArgVector
&Args
,
134 llvm::StringRef StdHeader
,
135 llvm::RecordKeeper
&Records
,
136 const Command::ErrorReporter
&Reporter
) const {
137 if (Args
.size() != 0) {
138 Reporter
.printFatalError("public_api command does not take any arguments.");
141 APIIndexer
G(StdHeader
, Records
);
142 writeAPIFromIndex(G
, EntrypointNameList
, OS
);
145 } // namespace llvm_libc