1 //===- FileList.cpp ---------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
20 InstallAPI JSON Input Format specification.
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.
31 "version" : "3" # Required: Version 3 supports language mode
32 & project header input.
38 using namespace llvm::json
;
39 using namespace llvm::MachO
;
40 using namespace clang::installapi
;
43 class Implementation
{
45 Expected
<StringRef
> parseString(const Object
*Obj
, StringRef Key
,
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
);
53 std::unique_ptr
<MemoryBuffer
> InputBuffer
;
54 clang::FileManager
*FM
;
58 Error
parse(StringRef Input
);
62 Implementation::parseString(const Object
*Obj
, StringRef Key
, StringRef Error
) {
63 auto Str
= Obj
->getString(Key
);
65 return make_error
<StringError
>(Error
, inconvertibleErrorCode());
69 Expected
<HeaderType
> Implementation::parseType(const Object
*Obj
) {
71 parseString(Obj
, "type", "required field 'type' not specified");
73 return TypeStr
.takeError();
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
;
82 return make_error
<TextAPIError
>(TextAPIErrorCode::InvalidInputFormat
,
83 "unsupported header type");
86 Expected
<StringRef
> Implementation::parsePath(const Object
*Obj
) {
87 auto Path
= parseString(Obj
, "path", "required field 'path' not specified");
89 return Path
.takeError();
94 std::optional
<clang::Language
>
95 Implementation::parseLanguage(const Object
*Obj
) {
96 auto Language
= Obj
->getString("language");
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
);
108 Error
Implementation::parseHeaders(Array
&Headers
) {
109 for (const auto &H
: Headers
) {
110 auto *Obj
= H
.getAsObject();
112 return make_error
<StringError
>("expect a JSON object",
113 inconvertibleErrorCode());
114 auto Type
= parseType(Obj
);
116 return Type
.takeError();
117 auto Path
= parsePath(Obj
);
119 return Path
.takeError();
120 auto Language
= parseLanguage(Obj
);
122 StringRef PathStr
= *Path
;
123 if (*Type
== HeaderType::Project
) {
124 HeaderList
.emplace_back(
125 HeaderFile
{PathStr
, *Type
, /*IncludeName=*/"", Language
});
130 if (!FM
->getOptionalFileRef(PathStr
))
131 return createFileError(
132 PathStr
, make_error_code(std::errc::no_such_file_or_directory
));
134 auto IncludeName
= createIncludeHeaderName(PathStr
);
135 HeaderList
.emplace_back(PathStr
, *Type
,
136 IncludeName
.has_value() ? IncludeName
.value() : "",
140 return Error::success();
143 Error
Implementation::parse(StringRef Input
) {
144 auto Val
= json::parse(Input
);
146 return Val
.takeError();
148 auto *Root
= Val
->getAsObject();
150 return make_error
<StringError
>("not a JSON object",
151 inconvertibleErrorCode());
153 auto VersionStr
= Root
->getString("version");
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");
161 if (Version
< 1 || Version
> 3)
162 return make_error
<TextAPIError
>(TextAPIErrorCode::InvalidInputFormat
,
163 "unsupported version");
165 // Not specifying any header files should be atypical, but valid.
166 auto Headers
= Root
->getArray("headers");
168 return Error::success();
170 Error Err
= parseHeaders(*Headers
);
174 return Error::success();
179 FileListReader::loadHeaders(std::unique_ptr
<MemoryBuffer
> InputBuffer
,
180 HeaderSeq
&Destination
, clang::FileManager
*FM
) {
182 Impl
.InputBuffer
= std::move(InputBuffer
);
185 if (llvm::Error Err
= Impl
.parse(Impl
.InputBuffer
->getBuffer()))
188 Destination
.reserve(Destination
.size() + Impl
.HeaderList
.size());
189 llvm::move(Impl
.HeaderList
, std::back_inserter(Destination
));
191 return Error::success();