xref: /llvm-project/clang/lib/InstallAPI/FileList.cpp (revision 278774e428c280b6ab62c147ac33b6837dad6dd5)
14c6043deSCyndy Ishida //===- FileList.cpp ---------------------------------------------*- C++ -*-===//
24c6043deSCyndy Ishida //
34c6043deSCyndy Ishida // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44c6043deSCyndy Ishida // See https://llvm.org/LICENSE.txt for license information.
54c6043deSCyndy Ishida // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64c6043deSCyndy Ishida //
74c6043deSCyndy Ishida //===----------------------------------------------------------------------===//
84c6043deSCyndy Ishida 
94c6043deSCyndy Ishida #include "clang/InstallAPI/FileList.h"
104c6043deSCyndy Ishida #include "clang/Basic/DiagnosticFrontend.h"
114c6043deSCyndy Ishida #include "clang/InstallAPI/FileList.h"
124c6043deSCyndy Ishida #include "llvm/ADT/StringSwitch.h"
134c6043deSCyndy Ishida #include "llvm/Support/Error.h"
144c6043deSCyndy Ishida #include "llvm/Support/JSON.h"
154c6043deSCyndy Ishida #include "llvm/TextAPI/TextAPIError.h"
164c6043deSCyndy Ishida #include <optional>
174c6043deSCyndy Ishida 
184c6043deSCyndy Ishida // clang-format off
194c6043deSCyndy Ishida /*
204c6043deSCyndy Ishida InstallAPI JSON Input Format specification.
214c6043deSCyndy Ishida 
224c6043deSCyndy Ishida {
234c6043deSCyndy Ishida   "headers" : [                              # Required: Key must exist.
244c6043deSCyndy Ishida     {                                        # Optional: May contain 0 or more header inputs.
254c6043deSCyndy Ishida       "path" : "/usr/include/mach-o/dlfn.h", # Required: Path should point to destination
264c6043deSCyndy Ishida                                              #           location where applicable.
274c6043deSCyndy Ishida       "type" : "public",                     # Required: Maps to HeaderType for header.
284c6043deSCyndy Ishida       "language": "c++"                      # Optional: Language mode for header.
294c6043deSCyndy Ishida     }
304c6043deSCyndy Ishida   ],
314c6043deSCyndy Ishida   "version" : "3"                            # Required: Version 3 supports language mode
324c6043deSCyndy Ishida                                                          & project header input.
334c6043deSCyndy Ishida }
344c6043deSCyndy Ishida */
354c6043deSCyndy Ishida // clang-format on
364c6043deSCyndy Ishida 
374c6043deSCyndy Ishida using namespace llvm;
384c6043deSCyndy Ishida using namespace llvm::json;
394c6043deSCyndy Ishida using namespace llvm::MachO;
404c6043deSCyndy Ishida using namespace clang::installapi;
414c6043deSCyndy Ishida 
424c6043deSCyndy Ishida namespace {
434c6043deSCyndy Ishida class Implementation {
444c6043deSCyndy Ishida private:
454c6043deSCyndy Ishida   Expected<StringRef> parseString(const Object *Obj, StringRef Key,
464c6043deSCyndy Ishida                                   StringRef Error);
474c6043deSCyndy Ishida   Expected<StringRef> parsePath(const Object *Obj);
484c6043deSCyndy Ishida   Expected<HeaderType> parseType(const Object *Obj);
494c6043deSCyndy Ishida   std::optional<clang::Language> parseLanguage(const Object *Obj);
504c6043deSCyndy Ishida   Error parseHeaders(Array &Headers);
514c6043deSCyndy Ishida 
524c6043deSCyndy Ishida public:
534c6043deSCyndy Ishida   std::unique_ptr<MemoryBuffer> InputBuffer;
54*278774e4SCyndy Ishida   clang::FileManager *FM;
554c6043deSCyndy Ishida   unsigned Version;
564c6043deSCyndy Ishida   HeaderSeq HeaderList;
574c6043deSCyndy Ishida 
584c6043deSCyndy Ishida   Error parse(StringRef Input);
594c6043deSCyndy Ishida };
604c6043deSCyndy Ishida 
614c6043deSCyndy Ishida Expected<StringRef>
parseString(const Object * Obj,StringRef Key,StringRef Error)624c6043deSCyndy Ishida Implementation::parseString(const Object *Obj, StringRef Key, StringRef Error) {
634c6043deSCyndy Ishida   auto Str = Obj->getString(Key);
644c6043deSCyndy Ishida   if (!Str)
654c6043deSCyndy Ishida     return make_error<StringError>(Error, inconvertibleErrorCode());
664c6043deSCyndy Ishida   return *Str;
674c6043deSCyndy Ishida }
684c6043deSCyndy Ishida 
parseType(const Object * Obj)694c6043deSCyndy Ishida Expected<HeaderType> Implementation::parseType(const Object *Obj) {
704c6043deSCyndy Ishida   auto TypeStr =
714c6043deSCyndy Ishida       parseString(Obj, "type", "required field 'type' not specified");
724c6043deSCyndy Ishida   if (!TypeStr)
734c6043deSCyndy Ishida     return TypeStr.takeError();
744c6043deSCyndy Ishida 
754c6043deSCyndy Ishida   if (*TypeStr == "public")
764c6043deSCyndy Ishida     return HeaderType::Public;
774c6043deSCyndy Ishida   else if (*TypeStr == "private")
784c6043deSCyndy Ishida     return HeaderType::Private;
794c6043deSCyndy Ishida   else if (*TypeStr == "project" && Version >= 2)
804c6043deSCyndy Ishida     return HeaderType::Project;
814c6043deSCyndy Ishida 
824c6043deSCyndy Ishida   return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
834c6043deSCyndy Ishida                                   "unsupported header type");
844c6043deSCyndy Ishida }
854c6043deSCyndy Ishida 
parsePath(const Object * Obj)864c6043deSCyndy Ishida Expected<StringRef> Implementation::parsePath(const Object *Obj) {
874c6043deSCyndy Ishida   auto Path = parseString(Obj, "path", "required field 'path' not specified");
884c6043deSCyndy Ishida   if (!Path)
894c6043deSCyndy Ishida     return Path.takeError();
904c6043deSCyndy Ishida 
914c6043deSCyndy Ishida   return *Path;
924c6043deSCyndy Ishida }
934c6043deSCyndy Ishida 
944c6043deSCyndy Ishida std::optional<clang::Language>
parseLanguage(const Object * Obj)954c6043deSCyndy Ishida Implementation::parseLanguage(const Object *Obj) {
964c6043deSCyndy Ishida   auto Language = Obj->getString("language");
974c6043deSCyndy Ishida   if (!Language)
984c6043deSCyndy Ishida     return std::nullopt;
994c6043deSCyndy Ishida 
1004c6043deSCyndy Ishida   return StringSwitch<clang::Language>(*Language)
1014c6043deSCyndy Ishida       .Case("c", clang::Language::C)
1024c6043deSCyndy Ishida       .Case("c++", clang::Language::CXX)
1034c6043deSCyndy Ishida       .Case("objective-c", clang::Language::ObjC)
1044c6043deSCyndy Ishida       .Case("objective-c++", clang::Language::ObjCXX)
1054c6043deSCyndy Ishida       .Default(clang::Language::Unknown);
1064c6043deSCyndy Ishida }
1074c6043deSCyndy Ishida 
parseHeaders(Array & Headers)1084c6043deSCyndy Ishida Error Implementation::parseHeaders(Array &Headers) {
1094c6043deSCyndy Ishida   for (const auto &H : Headers) {
1104c6043deSCyndy Ishida     auto *Obj = H.getAsObject();
1114c6043deSCyndy Ishida     if (!Obj)
1124c6043deSCyndy Ishida       return make_error<StringError>("expect a JSON object",
1134c6043deSCyndy Ishida                                      inconvertibleErrorCode());
1144c6043deSCyndy Ishida     auto Type = parseType(Obj);
1154c6043deSCyndy Ishida     if (!Type)
1164c6043deSCyndy Ishida       return Type.takeError();
1174c6043deSCyndy Ishida     auto Path = parsePath(Obj);
1184c6043deSCyndy Ishida     if (!Path)
1194c6043deSCyndy Ishida       return Path.takeError();
1204c6043deSCyndy Ishida     auto Language = parseLanguage(Obj);
1214c6043deSCyndy Ishida 
1224c6043deSCyndy Ishida     StringRef PathStr = *Path;
1234c6043deSCyndy Ishida     if (*Type == HeaderType::Project) {
1244c6043deSCyndy Ishida       HeaderList.emplace_back(
1254c6043deSCyndy Ishida           HeaderFile{PathStr, *Type, /*IncludeName=*/"", Language});
1264c6043deSCyndy Ishida       continue;
1274c6043deSCyndy Ishida     }
128*278774e4SCyndy Ishida 
129*278774e4SCyndy Ishida     if (FM)
130*278774e4SCyndy Ishida       if (!FM->getOptionalFileRef(PathStr))
131*278774e4SCyndy Ishida         return createFileError(
132*278774e4SCyndy Ishida             PathStr, make_error_code(std::errc::no_such_file_or_directory));
133*278774e4SCyndy Ishida 
1344c6043deSCyndy Ishida     auto IncludeName = createIncludeHeaderName(PathStr);
1354c6043deSCyndy Ishida     HeaderList.emplace_back(PathStr, *Type,
1364c6043deSCyndy Ishida                             IncludeName.has_value() ? IncludeName.value() : "",
1374c6043deSCyndy Ishida                             Language);
1384c6043deSCyndy Ishida   }
1394c6043deSCyndy Ishida 
1404c6043deSCyndy Ishida   return Error::success();
1414c6043deSCyndy Ishida }
1424c6043deSCyndy Ishida 
parse(StringRef Input)1434c6043deSCyndy Ishida Error Implementation::parse(StringRef Input) {
1444c6043deSCyndy Ishida   auto Val = json::parse(Input);
1454c6043deSCyndy Ishida   if (!Val)
1464c6043deSCyndy Ishida     return Val.takeError();
1474c6043deSCyndy Ishida 
1484c6043deSCyndy Ishida   auto *Root = Val->getAsObject();
1494c6043deSCyndy Ishida   if (!Root)
1504c6043deSCyndy Ishida     return make_error<StringError>("not a JSON object",
1514c6043deSCyndy Ishida                                    inconvertibleErrorCode());
1524c6043deSCyndy Ishida 
1534c6043deSCyndy Ishida   auto VersionStr = Root->getString("version");
1544c6043deSCyndy Ishida   if (!VersionStr)
1554c6043deSCyndy Ishida     return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
1564c6043deSCyndy Ishida                                     "required field 'version' not specified");
1574c6043deSCyndy Ishida   if (VersionStr->getAsInteger(10, Version))
1584c6043deSCyndy Ishida     return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
1594c6043deSCyndy Ishida                                     "invalid version number");
1604c6043deSCyndy Ishida 
1614c6043deSCyndy Ishida   if (Version < 1 || Version > 3)
1624c6043deSCyndy Ishida     return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
1634c6043deSCyndy Ishida                                     "unsupported version");
1644c6043deSCyndy Ishida 
1654c6043deSCyndy Ishida   // Not specifying any header files should be atypical, but valid.
1664c6043deSCyndy Ishida   auto Headers = Root->getArray("headers");
1674c6043deSCyndy Ishida   if (!Headers)
1684c6043deSCyndy Ishida     return Error::success();
1694c6043deSCyndy Ishida 
1704c6043deSCyndy Ishida   Error Err = parseHeaders(*Headers);
1714c6043deSCyndy Ishida   if (Err)
1724c6043deSCyndy Ishida     return Err;
1734c6043deSCyndy Ishida 
1744c6043deSCyndy Ishida   return Error::success();
1754c6043deSCyndy Ishida }
1764c6043deSCyndy Ishida } // namespace
1774c6043deSCyndy Ishida 
1784c6043deSCyndy Ishida llvm::Error
loadHeaders(std::unique_ptr<MemoryBuffer> InputBuffer,HeaderSeq & Destination,clang::FileManager * FM)1794c6043deSCyndy Ishida FileListReader::loadHeaders(std::unique_ptr<MemoryBuffer> InputBuffer,
180*278774e4SCyndy Ishida                             HeaderSeq &Destination, clang::FileManager *FM) {
1814c6043deSCyndy Ishida   Implementation Impl;
1824c6043deSCyndy Ishida   Impl.InputBuffer = std::move(InputBuffer);
183*278774e4SCyndy Ishida   Impl.FM = FM;
1844c6043deSCyndy Ishida 
1854c6043deSCyndy Ishida   if (llvm::Error Err = Impl.parse(Impl.InputBuffer->getBuffer()))
1864c6043deSCyndy Ishida     return Err;
1874c6043deSCyndy Ishida 
1884c6043deSCyndy Ishida   Destination.reserve(Destination.size() + Impl.HeaderList.size());
1894c6043deSCyndy Ishida   llvm::move(Impl.HeaderList, std::back_inserter(Destination));
1904c6043deSCyndy Ishida 
1914c6043deSCyndy Ishida   return Error::success();
1924c6043deSCyndy Ishida }
193