1 //===- FileList.cpp ---------------------------------------------*- C++ -*-===// 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 "clang/InstallAPI/FileList.h" 10 #include "clang/Basic/DiagnosticFrontend.h" 11 #include "clang/InstallAPI/FileList.h" 12 #include "llvm/ADT/StringSwitch.h" 13 #include "llvm/Support/Error.h" 14 #include "llvm/Support/JSON.h" 15 #include "llvm/TextAPI/TextAPIError.h" 16 #include <optional> 17 18 // clang-format off 19 /* 20 InstallAPI JSON Input Format specification. 21 22 { 23 "headers" : [ # Required: Key must exist. 24 { # Optional: May contain 0 or more header inputs. 25 "path" : "/usr/include/mach-o/dlfn.h", # Required: Path should point to destination 26 # location where applicable. 27 "type" : "public", # Required: Maps to HeaderType for header. 28 "language": "c++" # Optional: Language mode for header. 29 } 30 ], 31 "version" : "3" # Required: Version 3 supports language mode 32 & project header input. 33 } 34 */ 35 // clang-format on 36 37 using namespace llvm; 38 using namespace llvm::json; 39 using namespace llvm::MachO; 40 using namespace clang::installapi; 41 42 namespace { 43 class Implementation { 44 private: 45 Expected<StringRef> parseString(const Object *Obj, StringRef Key, 46 StringRef Error); 47 Expected<StringRef> parsePath(const Object *Obj); 48 Expected<HeaderType> parseType(const Object *Obj); 49 std::optional<clang::Language> parseLanguage(const Object *Obj); 50 Error parseHeaders(Array &Headers); 51 52 public: 53 std::unique_ptr<MemoryBuffer> InputBuffer; 54 clang::FileManager *FM; 55 unsigned Version; 56 HeaderSeq HeaderList; 57 58 Error parse(StringRef Input); 59 }; 60 61 Expected<StringRef> 62 Implementation::parseString(const Object *Obj, StringRef Key, StringRef Error) { 63 auto Str = Obj->getString(Key); 64 if (!Str) 65 return make_error<StringError>(Error, inconvertibleErrorCode()); 66 return *Str; 67 } 68 69 Expected<HeaderType> Implementation::parseType(const Object *Obj) { 70 auto TypeStr = 71 parseString(Obj, "type", "required field 'type' not specified"); 72 if (!TypeStr) 73 return TypeStr.takeError(); 74 75 if (*TypeStr == "public") 76 return HeaderType::Public; 77 else if (*TypeStr == "private") 78 return HeaderType::Private; 79 else if (*TypeStr == "project" && Version >= 2) 80 return HeaderType::Project; 81 82 return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat, 83 "unsupported header type"); 84 } 85 86 Expected<StringRef> Implementation::parsePath(const Object *Obj) { 87 auto Path = parseString(Obj, "path", "required field 'path' not specified"); 88 if (!Path) 89 return Path.takeError(); 90 91 return *Path; 92 } 93 94 std::optional<clang::Language> 95 Implementation::parseLanguage(const Object *Obj) { 96 auto Language = Obj->getString("language"); 97 if (!Language) 98 return std::nullopt; 99 100 return StringSwitch<clang::Language>(*Language) 101 .Case("c", clang::Language::C) 102 .Case("c++", clang::Language::CXX) 103 .Case("objective-c", clang::Language::ObjC) 104 .Case("objective-c++", clang::Language::ObjCXX) 105 .Default(clang::Language::Unknown); 106 } 107 108 Error Implementation::parseHeaders(Array &Headers) { 109 for (const auto &H : Headers) { 110 auto *Obj = H.getAsObject(); 111 if (!Obj) 112 return make_error<StringError>("expect a JSON object", 113 inconvertibleErrorCode()); 114 auto Type = parseType(Obj); 115 if (!Type) 116 return Type.takeError(); 117 auto Path = parsePath(Obj); 118 if (!Path) 119 return Path.takeError(); 120 auto Language = parseLanguage(Obj); 121 122 StringRef PathStr = *Path; 123 if (*Type == HeaderType::Project) { 124 HeaderList.emplace_back( 125 HeaderFile{PathStr, *Type, /*IncludeName=*/"", Language}); 126 continue; 127 } 128 129 if (FM) 130 if (!FM->getOptionalFileRef(PathStr)) 131 return createFileError( 132 PathStr, make_error_code(std::errc::no_such_file_or_directory)); 133 134 auto IncludeName = createIncludeHeaderName(PathStr); 135 HeaderList.emplace_back(PathStr, *Type, 136 IncludeName.has_value() ? IncludeName.value() : "", 137 Language); 138 } 139 140 return Error::success(); 141 } 142 143 Error Implementation::parse(StringRef Input) { 144 auto Val = json::parse(Input); 145 if (!Val) 146 return Val.takeError(); 147 148 auto *Root = Val->getAsObject(); 149 if (!Root) 150 return make_error<StringError>("not a JSON object", 151 inconvertibleErrorCode()); 152 153 auto VersionStr = Root->getString("version"); 154 if (!VersionStr) 155 return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat, 156 "required field 'version' not specified"); 157 if (VersionStr->getAsInteger(10, Version)) 158 return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat, 159 "invalid version number"); 160 161 if (Version < 1 || Version > 3) 162 return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat, 163 "unsupported version"); 164 165 // Not specifying any header files should be atypical, but valid. 166 auto Headers = Root->getArray("headers"); 167 if (!Headers) 168 return Error::success(); 169 170 Error Err = parseHeaders(*Headers); 171 if (Err) 172 return Err; 173 174 return Error::success(); 175 } 176 } // namespace 177 178 llvm::Error 179 FileListReader::loadHeaders(std::unique_ptr<MemoryBuffer> InputBuffer, 180 HeaderSeq &Destination, clang::FileManager *FM) { 181 Implementation Impl; 182 Impl.InputBuffer = std::move(InputBuffer); 183 Impl.FM = FM; 184 185 if (llvm::Error Err = Impl.parse(Impl.InputBuffer->getBuffer())) 186 return Err; 187 188 Destination.reserve(Destination.size() + Impl.HeaderList.size()); 189 llvm::move(Impl.HeaderList, std::back_inserter(Destination)); 190 191 return Error::success(); 192 } 193