xref: /src/contrib/llvm-project/clang/lib/InstallAPI/FileList.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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