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/*
20InstallAPI 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
37using namespace llvm;
38using namespace llvm::json;
39using namespace llvm::MachO;
40using namespace clang::installapi;
41
42namespace {
43class Implementation {
44private:
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
52public:
53 std::unique_ptr<MemoryBuffer> InputBuffer;
54 clang::FileManager *FM;
55 unsigned Version;
56 HeaderSeq HeaderList;
57
58 Error parse(StringRef Input);
59};
60
61Expected<StringRef>
62Implementation::parseString(const Object *Obj, StringRef Key, StringRef Error) {
63 auto Str = Obj->getString(K: Key);
64 if (!Str)
65 return make_error<StringError>(Args&: Error, Args: inconvertibleErrorCode());
66 return *Str;
67}
68
69Expected<HeaderType> Implementation::parseType(const Object *Obj) {
70 auto TypeStr =
71 parseString(Obj, Key: "type", Error: "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>(Args: TextAPIErrorCode::InvalidInputFormat,
83 Args: "unsupported header type");
84}
85
86Expected<StringRef> Implementation::parsePath(const Object *Obj) {
87 auto Path = parseString(Obj, Key: "path", Error: "required field 'path' not specified");
88 if (!Path)
89 return Path.takeError();
90
91 return *Path;
92}
93
94std::optional<clang::Language>
95Implementation::parseLanguage(const Object *Obj) {
96 auto Language = Obj->getString(K: "language");
97 if (!Language)
98 return std::nullopt;
99
100 return StringSwitch<clang::Language>(*Language)
101 .Case(S: "c", Value: clang::Language::C)
102 .Case(S: "c++", Value: clang::Language::CXX)
103 .Case(S: "objective-c", Value: clang::Language::ObjC)
104 .Case(S: "objective-c++", Value: clang::Language::ObjCXX)
105 .Default(Value: clang::Language::Unknown);
106}
107
108Error Implementation::parseHeaders(Array &Headers) {
109 for (const auto &H : Headers) {
110 auto *Obj = H.getAsObject();
111 if (!Obj)
112 return make_error<StringError>(Args: "expect a JSON object",
113 Args: 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 args: HeaderFile{PathStr, *Type, /*IncludeName=*/"", Language});
126 continue;
127 }
128
129 if (FM)
130 if (!FM->getOptionalFileRef(Filename: PathStr))
131 return createFileError(
132 F: PathStr, EC: make_error_code(e: std::errc::no_such_file_or_directory));
133
134 auto IncludeName = createIncludeHeaderName(FullPath: PathStr);
135 HeaderList.emplace_back(args&: PathStr, args&: *Type,
136 args: IncludeName.has_value() ? IncludeName.value() : "",
137 args&: Language);
138 }
139
140 return Error::success();
141}
142
143Error Implementation::parse(StringRef Input) {
144 auto Val = json::parse(JSON: Input);
145 if (!Val)
146 return Val.takeError();
147
148 auto *Root = Val->getAsObject();
149 if (!Root)
150 return make_error<StringError>(Args: "not a JSON object",
151 Args: inconvertibleErrorCode());
152
153 auto VersionStr = Root->getString(K: "version");
154 if (!VersionStr)
155 return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat,
156 Args: "required field 'version' not specified");
157 if (VersionStr->getAsInteger(Radix: 10, Result&: Version))
158 return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat,
159 Args: "invalid version number");
160
161 if (Version < 1 || Version > 3)
162 return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat,
163 Args: "unsupported version");
164
165 // Not specifying any header files should be atypical, but valid.
166 auto Headers = Root->getArray(K: "headers");
167 if (!Headers)
168 return Error::success();
169
170 Error Err = parseHeaders(Headers&: *Headers);
171 if (Err)
172 return Err;
173
174 return Error::success();
175}
176} // namespace
177
178llvm::Error
179FileListReader::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(Input: Impl.InputBuffer->getBuffer()))
186 return Err;
187
188 Destination.reserve(n: Destination.size() + Impl.HeaderList.size());
189 llvm::move(Range&: Impl.HeaderList, Out: std::back_inserter(x&: Destination));
190
191 return Error::success();
192}
193