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. 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 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 117 void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {} 118 119 const char PublicAPICommand::Name[] = "public_api"; 120 121 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