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>
parseString(const Object * Obj,StringRef Key,StringRef Error)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
parseType(const Object * Obj)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
parsePath(const Object * Obj)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>
parseLanguage(const Object * Obj)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
parseHeaders(Array & Headers)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
parse(StringRef Input)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
loadHeaders(std::unique_ptr<MemoryBuffer> InputBuffer,HeaderSeq & Destination,clang::FileManager * FM)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