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