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