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