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