/src/CMake/Source/cmPackageInfoReader.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmPackageInfoReader.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <initializer_list> |
7 | | #include <limits> |
8 | | #include <unordered_map> |
9 | | #include <utility> |
10 | | |
11 | | #include <cmext/algorithm> |
12 | | #include <cmext/string_view> |
13 | | |
14 | | #include <cm3p/json/value.h> |
15 | | #include <cm3p/json/version.h> |
16 | | |
17 | | #include "cmsys/RegularExpression.hxx" |
18 | | |
19 | | #include "cmCxxModuleMetadata.h" |
20 | | #include "cmExecutionStatus.h" |
21 | | #include "cmFileSet.h" |
22 | | #include "cmFileSetMetadata.h" |
23 | | #include "cmJSONState.h" |
24 | | #include "cmList.h" |
25 | | #include "cmListFileCache.h" |
26 | | #include "cmMakefile.h" |
27 | | #include "cmMessageType.h" |
28 | | #include "cmStringAlgorithms.h" |
29 | | #include "cmSystemTools.h" |
30 | | #include "cmTarget.h" |
31 | | #include "cmTargetTypes.h" |
32 | | #include "cmValue.h" |
33 | | |
34 | | // When adding or changing CPS schema versions, component types, languages, or |
35 | | // branch-selecting attributes, review Tests/Fuzzing/cmPackageInfoReader.dict. |
36 | | // Add tokens including keys that help mutations reach the new paths; keep the |
37 | | // dictionary focused on useful parser inputs, not an exhaustive list of CPS |
38 | | // keywords. |
39 | | |
40 | | namespace { |
41 | | |
42 | | // Map of CPS language names to CMake language name. Case insensitivity is |
43 | | // achieved by converting the CPS value to lower case, so keys in this map must |
44 | | // be lower case. |
45 | | std::unordered_map<std::string, std::string> Languages = { |
46 | | // clang-format off |
47 | | { "c", "C" }, |
48 | | { "c++", "CXX" }, |
49 | | { "cpp", "CXX" }, |
50 | | { "cxx", "CXX" }, |
51 | | { "objc", "OBJC" }, |
52 | | { "objc++", "OBJCXX" }, |
53 | | { "objcpp", "OBJCXX" }, |
54 | | { "objcxx", "OBJCXX" }, |
55 | | { "swift", "swift" }, |
56 | | { "hip", "HIP" }, |
57 | | { "cuda", "CUDA" }, |
58 | | { "ispc", "ISPC" }, |
59 | | { "c#", "CSharp" }, |
60 | | { "csharp", "CSharp" }, |
61 | | { "fortran", "Fortran" }, |
62 | | // clang-format on |
63 | | }; |
64 | | |
65 | | enum LanguageGlobOption |
66 | | { |
67 | | DisallowGlob, |
68 | | AllowGlob, |
69 | | }; |
70 | | |
71 | | cm::string_view MapLanguage(cm::string_view lang, |
72 | | LanguageGlobOption glob = AllowGlob) |
73 | 35.4k | { |
74 | 35.4k | if (glob == AllowGlob && lang == "*"_s) { |
75 | 22.6k | return "*"_s; |
76 | 22.6k | } |
77 | 12.7k | auto const li = Languages.find(cmSystemTools::LowerCase(lang)); |
78 | 12.7k | if (li != Languages.end()) { |
79 | 1.13k | return li->second; |
80 | 1.13k | } |
81 | 11.5k | return {}; |
82 | 12.7k | } |
83 | | |
84 | | std::string GetRealPath(std::string const& path) |
85 | 166 | { |
86 | 166 | return cmSystemTools::GetRealPath(path); |
87 | 166 | } |
88 | | |
89 | | std::string GetRealDir(std::string const& path) |
90 | 166 | { |
91 | 166 | return cmSystemTools::GetFilenamePath(cmSystemTools::GetRealPath(path)); |
92 | 166 | } |
93 | | |
94 | | Json::Value ReadJson(std::string const& fileName) |
95 | 18.2k | { |
96 | 18.2k | Json::Value data; |
97 | 18.2k | cmJSONState parseState(fileName, &data, cmJSONState::StrictMode::Relaxed); |
98 | 18.2k | if (!parseState.errors.empty()) { |
99 | | #if JSONCPP_VERSION_HEXA < 0x01070300 |
100 | | return Json::Value::null; |
101 | | #else |
102 | 3.52k | return Json::Value::nullSingleton(); |
103 | 3.52k | #endif |
104 | 3.52k | } |
105 | | |
106 | 14.7k | return data; |
107 | 18.2k | } |
108 | | |
109 | | std::string ToString(Json::Value const& value) |
110 | 109k | { |
111 | 109k | if (value.isString()) { |
112 | 79.6k | return value.asString(); |
113 | 79.6k | } |
114 | 29.5k | return {}; |
115 | 109k | } |
116 | | |
117 | | bool CheckSchemaVersion(Json::Value const& data) |
118 | 9.52k | { |
119 | 9.52k | std::string const& version = ToString(data["cps_version"]); |
120 | | |
121 | | // Check that a valid version is specified. |
122 | 9.52k | if (version.empty()) { |
123 | 126 | return false; |
124 | 126 | } |
125 | | |
126 | | // Check that we understand this version. |
127 | 9.39k | return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, |
128 | 9.39k | version, "0.13") && |
129 | 9.35k | cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, version, "0.16"); |
130 | | |
131 | | // TODO Eventually this probably needs to return the version tuple, and |
132 | | // should share code with cmPackageInfoReader::ParseVersion. |
133 | 9.52k | } |
134 | | |
135 | | bool ComparePathSuffix(std::string const& path, std::string const& suffix) |
136 | 8.82k | { |
137 | 8.82k | std::string::size_type const ps = path.size(); |
138 | 8.82k | std::string::size_type const ss = suffix.size(); |
139 | | |
140 | 8.82k | if (ss > ps) { |
141 | 206 | return false; |
142 | 206 | } |
143 | | |
144 | 8.61k | return cmSystemTools::ComparePath(path.substr(ps - ss), suffix); |
145 | 8.82k | } |
146 | | |
147 | | std::string DeterminePrefix(std::string const& filepath, |
148 | | Json::Value const& data) |
149 | 9.25k | { |
150 | | // First check if an absolute prefix was supplied. |
151 | 9.25k | std::string prefix = ToString(data["prefix"]); |
152 | 9.25k | if (!prefix.empty()) { |
153 | | // Ensure that the specified prefix is valid. |
154 | 2 | if (cmsys::SystemTools::FileIsFullPath(prefix) && |
155 | 0 | cmsys::SystemTools::FileIsDirectory(prefix)) { |
156 | 0 | cmSystemTools::ConvertToUnixSlashes(prefix); |
157 | 0 | return prefix; |
158 | 0 | } |
159 | | // The specified absolute prefix is not valid. |
160 | 2 | return {}; |
161 | 2 | } |
162 | | |
163 | | // Get and validate prefix-relative path. |
164 | 9.24k | std::string const& absPath = cmSystemTools::GetFilenamePath(filepath); |
165 | 9.24k | std::string relPath = ToString(data["cps_path"]); |
166 | 9.24k | cmSystemTools::ConvertToUnixSlashes(relPath); |
167 | 9.24k | if (relPath.empty() || !cmHasLiteralPrefix(relPath, "@prefix@")) { |
168 | | // The relative prefix is not valid. |
169 | 490 | return {}; |
170 | 490 | } |
171 | 8.75k | if (relPath.size() == 8) { |
172 | | // The relative path is exactly "@prefix@". |
173 | 68 | return absPath; |
174 | 68 | } |
175 | 8.69k | if (relPath[8] != '/') { |
176 | | // The relative prefix is not valid. |
177 | 36 | return {}; |
178 | 36 | } |
179 | 8.65k | relPath = relPath.substr(8); |
180 | | |
181 | | // Get directory portion of the absolute path. |
182 | 8.65k | if (ComparePathSuffix(absPath, relPath)) { |
183 | 8.48k | return absPath.substr(0, absPath.size() - relPath.size()); |
184 | 8.48k | } |
185 | | |
186 | 332 | for (auto* const f : { GetRealPath, GetRealDir }) { |
187 | 332 | std::string const& tmpPath = (*f)(absPath); |
188 | 332 | if (!cmSystemTools::ComparePath(tmpPath, absPath) && |
189 | 166 | ComparePathSuffix(tmpPath, relPath)) { |
190 | 0 | return tmpPath.substr(0, tmpPath.size() - relPath.size()); |
191 | 0 | } |
192 | 332 | } |
193 | | |
194 | 166 | return {}; |
195 | 166 | } |
196 | | |
197 | | // Extract key name from value iterator as string_view. |
198 | | cm::string_view IterKey(Json::Value::const_iterator iter) |
199 | 87.7k | { |
200 | 87.7k | char const* end; |
201 | 87.7k | char const* const start = iter.memberName(&end); |
202 | 87.7k | return { start, static_cast<std::string::size_type>(end - start) }; |
203 | 87.7k | } |
204 | | |
205 | | // Get list-of-strings value from object. |
206 | | std::vector<std::string> ReadList(Json::Value const& arr) |
207 | 467k | { |
208 | 467k | std::vector<std::string> result; |
209 | | |
210 | 467k | if (arr.isArray()) { |
211 | 181k | for (Json::Value const& val : arr) { |
212 | 181k | if (val.isString()) { |
213 | 180k | result.push_back(val.asString()); |
214 | 180k | } |
215 | 181k | } |
216 | 14.0k | } |
217 | | |
218 | 467k | return result; |
219 | 467k | } |
220 | | |
221 | | std::vector<std::string> ReadList(Json::Value const& data, char const* key) |
222 | 462k | { |
223 | 462k | return ReadList(data[key]); |
224 | 462k | } |
225 | | |
226 | | Json::Value GetExtensions(Json::Value const& data) |
227 | 8.60k | { |
228 | 8.60k | if (data.isObject()) { |
229 | 7.82k | Json::Value const& extensions = data["extensions"]; |
230 | 7.82k | if (extensions.isObject()) { |
231 | 2.17k | Json::Value const& cmake = extensions["cmake"]; |
232 | 2.17k | if (cmake.isObject()) { |
233 | 1.93k | return cmake; |
234 | 1.93k | } |
235 | 2.17k | } |
236 | 7.82k | } |
237 | 6.67k | return Json::Value{ Json::objectValue }; |
238 | 8.60k | } |
239 | | |
240 | | std::string NormalizeTargetName(std::string const& name, |
241 | | std::string const& context) |
242 | 56.8k | { |
243 | 56.8k | if (cmHasPrefix(name, ':')) { |
244 | 4.23k | return cmStrCat(context, ':', name); |
245 | 4.23k | } |
246 | | |
247 | 52.5k | std::string::size_type const n = name.find_first_of(':'); |
248 | 52.5k | if (n != std::string::npos) { |
249 | 4.45k | cm::string_view v{ name }; |
250 | 4.45k | return cmStrCat(v.substr(0, n), ':', v.substr(n)); |
251 | 4.45k | } |
252 | 48.1k | return name; |
253 | 52.5k | } |
254 | | |
255 | | void AppendProperty(cmMakefile* makefile, cmTarget* target, |
256 | | cm::string_view property, cm::string_view configuration, |
257 | | std::string const& value) |
258 | 142k | { |
259 | 142k | std::string const fullprop = cmStrCat("INTERFACE_", property); |
260 | 142k | if (!configuration.empty()) { |
261 | 98.2k | std::string const genexValue = |
262 | 98.2k | cmStrCat("$<$<CONFIG:", configuration, ">:", value, '>'); |
263 | 98.2k | target->AppendProperty(fullprop, genexValue, makefile->GetBacktrace()); |
264 | 98.2k | } else { |
265 | 44.0k | target->AppendProperty(fullprop, value, makefile->GetBacktrace()); |
266 | 44.0k | } |
267 | 142k | } |
268 | | |
269 | | void AppendImportProperty(cmMakefile* makefile, cmTarget* target, |
270 | | cm::string_view property, |
271 | | cm::string_view configuration, |
272 | | std::string const& value) |
273 | 7.40k | { |
274 | 7.40k | if (!configuration.empty()) { |
275 | 6.70k | std::string const fullprop = cmStrCat( |
276 | 6.70k | "IMPORTED_", property, '_', cmSystemTools::UpperCase(configuration)); |
277 | 6.70k | target->AppendProperty(fullprop, value, makefile->GetBacktrace()); |
278 | 6.70k | } else { |
279 | 706 | std::string const fullprop = cmStrCat("IMPORTED_", property); |
280 | 706 | target->AppendProperty(fullprop, value, makefile->GetBacktrace()); |
281 | 706 | } |
282 | 7.40k | } |
283 | | |
284 | | template <typename Transform> |
285 | | void AppendLanguageProperties(cmMakefile* makefile, cmTarget* target, |
286 | | cm::string_view property, |
287 | | cm::string_view configuration, |
288 | | Json::Value const& data, char const* key, |
289 | | Transform transform) |
290 | 95.7k | { |
291 | 95.7k | Json::Value const& value = data[key]; |
292 | 95.7k | if (value.isArray()) { |
293 | 53.9k | for (std::string v : ReadList(value)) { |
294 | 53.9k | AppendProperty(makefile, target, property, configuration, |
295 | 53.9k | transform(std::move(v))); |
296 | 53.9k | } |
297 | 95.2k | } else if (value.isObject()) { |
298 | 10 | for (auto vi = value.begin(), ve = value.end(); vi != ve; ++vi) { |
299 | 4 | cm::string_view const originalLang = IterKey(vi); |
300 | 4 | cm::string_view const lang = MapLanguage(originalLang); |
301 | 4 | if (lang.empty()) { |
302 | 4 | makefile->IssueMessage(MessageType::WARNING, |
303 | 4 | cmStrCat(R"(ignoring unknown language ")"_s, |
304 | 4 | originalLang, R"(" in )"_s, key, |
305 | 4 | " for "_s, target->GetName())); |
306 | 4 | continue; |
307 | 4 | } |
308 | | |
309 | 0 | if (lang == "*"_s) { |
310 | 0 | for (std::string v : ReadList(*vi)) { |
311 | 0 | AppendProperty(makefile, target, property, configuration, |
312 | 0 | transform(std::move(v))); |
313 | 0 | } |
314 | 0 | } else { |
315 | 0 | for (std::string v : ReadList(*vi)) { |
316 | 0 | v = cmStrCat("$<$<COMPILE_LANGUAGE:"_s, lang, ">:"_s, |
317 | 0 | transform(std::move(v)), '>'); |
318 | 0 | AppendProperty(makefile, target, property, configuration, v); |
319 | 0 | } |
320 | 0 | } |
321 | 0 | } |
322 | 6 | } |
323 | 95.7k | } cmPackageInfoReader.cxx:void (anonymous namespace)::AppendLanguageProperties<cmPackageInfoReader::SetTargetProperties(cmMakefile*, cmTarget*, Json::Value const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >) const::$_0>(cmMakefile*, cmTarget*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, Json::Value const&, char const*, cmPackageInfoReader::SetTargetProperties(cmMakefile*, cmTarget*, Json::Value const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >) const::$_0) Line | Count | Source | 290 | 47.8k | { | 291 | 47.8k | Json::Value const& value = data[key]; | 292 | 47.8k | if (value.isArray()) { | 293 | 2 | for (std::string v : ReadList(value)) { | 294 | 2 | AppendProperty(makefile, target, property, configuration, | 295 | 2 | transform(std::move(v))); | 296 | 2 | } | 297 | 47.8k | } else if (value.isObject()) { | 298 | 0 | for (auto vi = value.begin(), ve = value.end(); vi != ve; ++vi) { | 299 | 0 | cm::string_view const originalLang = IterKey(vi); | 300 | 0 | cm::string_view const lang = MapLanguage(originalLang); | 301 | 0 | if (lang.empty()) { | 302 | 0 | makefile->IssueMessage(MessageType::WARNING, | 303 | 0 | cmStrCat(R"(ignoring unknown language ")"_s, | 304 | 0 | originalLang, R"(" in )"_s, key, | 305 | 0 | " for "_s, target->GetName())); | 306 | 0 | continue; | 307 | 0 | } | 308 | | | 309 | 0 | if (lang == "*"_s) { | 310 | 0 | for (std::string v : ReadList(*vi)) { | 311 | 0 | AppendProperty(makefile, target, property, configuration, | 312 | 0 | transform(std::move(v))); | 313 | 0 | } | 314 | 0 | } else { | 315 | 0 | for (std::string v : ReadList(*vi)) { | 316 | 0 | v = cmStrCat("$<$<COMPILE_LANGUAGE:"_s, lang, ">:"_s, | 317 | 0 | transform(std::move(v)), '>'); | 318 | 0 | AppendProperty(makefile, target, property, configuration, v); | 319 | 0 | } | 320 | 0 | } | 321 | 0 | } | 322 | 0 | } | 323 | 47.8k | } |
cmPackageInfoReader.cxx:void (anonymous namespace)::AppendLanguageProperties<cmPackageInfoReader::SetTargetProperties(cmMakefile*, cmTarget*, Json::Value const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >) const::$_1>(cmMakefile*, cmTarget*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, Json::Value const&, char const*, cmPackageInfoReader::SetTargetProperties(cmMakefile*, cmTarget*, Json::Value const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >) const::$_1) Line | Count | Source | 290 | 47.8k | { | 291 | 47.8k | Json::Value const& value = data[key]; | 292 | 47.8k | if (value.isArray()) { | 293 | 53.9k | for (std::string v : ReadList(value)) { | 294 | 53.9k | AppendProperty(makefile, target, property, configuration, | 295 | 53.9k | transform(std::move(v))); | 296 | 53.9k | } | 297 | 47.3k | } else if (value.isObject()) { | 298 | 10 | for (auto vi = value.begin(), ve = value.end(); vi != ve; ++vi) { | 299 | 4 | cm::string_view const originalLang = IterKey(vi); | 300 | 4 | cm::string_view const lang = MapLanguage(originalLang); | 301 | 4 | if (lang.empty()) { | 302 | 4 | makefile->IssueMessage(MessageType::WARNING, | 303 | 4 | cmStrCat(R"(ignoring unknown language ")"_s, | 304 | 4 | originalLang, R"(" in )"_s, key, | 305 | 4 | " for "_s, target->GetName())); | 306 | 4 | continue; | 307 | 4 | } | 308 | | | 309 | 0 | if (lang == "*"_s) { | 310 | 0 | for (std::string v : ReadList(*vi)) { | 311 | 0 | AppendProperty(makefile, target, property, configuration, | 312 | 0 | transform(std::move(v))); | 313 | 0 | } | 314 | 0 | } else { | 315 | 0 | for (std::string v : ReadList(*vi)) { | 316 | 0 | v = cmStrCat("$<$<COMPILE_LANGUAGE:"_s, lang, ">:"_s, | 317 | 0 | transform(std::move(v)), '>'); | 318 | 0 | AppendProperty(makefile, target, property, configuration, v); | 319 | 0 | } | 320 | 0 | } | 321 | 0 | } | 322 | 6 | } | 323 | 47.8k | } |
|
324 | | |
325 | | void AddCompileFeature(cmMakefile* makefile, cmTarget* target, |
326 | | cm::string_view configuration, std::string const& value) |
327 | 7.83k | { |
328 | 7.83k | auto reLanguageLevel = []() -> cmsys::RegularExpression { |
329 | 7.83k | static cmsys::RegularExpression re{ "^[Cc]([+][+])?([0-9][0-9])$" }; |
330 | 7.83k | return re; |
331 | 7.83k | }(); |
332 | | |
333 | 7.83k | if (reLanguageLevel.find(value)) { |
334 | 8 | std::string::size_type const n = reLanguageLevel.end() - 2; |
335 | 8 | cm::string_view const featurePrefix = (n == 3 ? "cxx_std_"_s : "c_std_"_s); |
336 | 8 | if (configuration.empty()) { |
337 | 8 | AppendProperty(makefile, target, "COMPILE_FEATURES"_s, {}, |
338 | 8 | cmStrCat(featurePrefix, value.substr(n))); |
339 | 8 | } else { |
340 | 0 | std::string const& feature = |
341 | 0 | cmStrCat("$<$<CONFIG:"_s, configuration, ">:"_s, featurePrefix, |
342 | 0 | value.substr(n), '>'); |
343 | 0 | AppendProperty(makefile, target, "COMPILE_FEATURES"_s, {}, feature); |
344 | 0 | } |
345 | 7.83k | } else if (cmStrCaseEq(value, "gnu"_s)) { |
346 | | // Not implemented in CMake at this time |
347 | 7.83k | } else if (cmStrCaseEq(value, "threads"_s)) { |
348 | 0 | AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, |
349 | 0 | "Threads::Threads"); |
350 | 0 | } |
351 | 7.83k | } |
352 | | |
353 | | void AddLinkFeature(cmMakefile* makefile, cmTarget* target, |
354 | | cm::string_view configuration, std::string const& value) |
355 | 1.46k | { |
356 | 1.46k | if (cmStrCaseEq(value, "thread"_s)) { |
357 | 0 | AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, |
358 | 0 | "Threads::Threads"); |
359 | 0 | } |
360 | 1.46k | } |
361 | | |
362 | | std::string BuildDefinition(std::string const& name, Json::Value const& value) |
363 | 37.3k | { |
364 | 37.3k | if (!value.isNull() && value.isConvertibleTo(Json::stringValue)) { |
365 | 28.4k | return cmStrCat(name, '=', value.asString()); |
366 | 28.4k | } |
367 | 8.96k | return name; |
368 | 37.3k | } |
369 | | |
370 | | void AddDefinition(cmMakefile* makefile, cmTarget* target, |
371 | | cm::string_view configuration, |
372 | | std::string const& definition) |
373 | 37.3k | { |
374 | 37.3k | AppendProperty(makefile, target, "COMPILE_DEFINITIONS"_s, configuration, |
375 | 37.3k | definition); |
376 | 37.3k | } |
377 | | |
378 | | using DefinitionLanguageMap = std::map<cm::string_view, Json::Value>; |
379 | | using DefinitionsMap = std::map<std::string, DefinitionLanguageMap>; |
380 | | |
381 | | void AddDefinitions(cmMakefile* makefile, cmTarget* target, |
382 | | cm::string_view configuration, |
383 | | DefinitionsMap const& definitions) |
384 | 47.8k | { |
385 | 47.8k | for (auto const& di : definitions) { |
386 | 35.8k | auto const& g = di.second.find("*"_s); |
387 | 35.8k | if (g != di.second.end()) { |
388 | 24.5k | std::string const& def = BuildDefinition(di.first, g->second); |
389 | 24.5k | if (di.second.size() == 1) { |
390 | | // Only the non-language-specific definition exists. |
391 | 24.3k | AddDefinition(makefile, target, configuration, def); |
392 | 24.3k | continue; |
393 | 24.3k | } |
394 | | |
395 | | // Create a genex to apply this definition to all languages except |
396 | | // those that override it. |
397 | 192 | std::vector<cm::string_view> excludedLanguages; |
398 | 390 | for (auto const& li : di.second) { |
399 | 390 | if (li.first != "*"_s) { |
400 | 198 | excludedLanguages.emplace_back(li.first); |
401 | 198 | } |
402 | 390 | } |
403 | 192 | AddDefinition(makefile, target, configuration, |
404 | 192 | cmStrCat("$<$<NOT:$<COMPILE_LANGUAGE:"_s, |
405 | 192 | cmJoin(excludedLanguages, ","_s), ">>:"_s, def, |
406 | 192 | '>')); |
407 | 192 | } |
408 | | |
409 | | // Add language-specific definitions. |
410 | 13.0k | for (auto const& li : di.second) { |
411 | 13.0k | if (li.first != "*"_s) { |
412 | 12.8k | AddDefinition(makefile, target, configuration, |
413 | 12.8k | cmStrCat("$<$<COMPILE_LANGUAGE:"_s, li.first, ">:"_s, |
414 | 12.8k | BuildDefinition(di.first, li.second), '>')); |
415 | 12.8k | } |
416 | 13.0k | } |
417 | 11.5k | } |
418 | 47.8k | } |
419 | | |
420 | | cm::optional<cmPackageInfoReader::Pep440Version> ParseSimpleVersion( |
421 | | std::string const& version) |
422 | 342 | { |
423 | 342 | if (version.empty()) { |
424 | 2 | return cm::nullopt; |
425 | 2 | } |
426 | | |
427 | 340 | cmPackageInfoReader::Pep440Version result; |
428 | 340 | result.Simple = true; |
429 | | |
430 | 340 | cm::string_view remnant{ version }; |
431 | 810 | for (;;) { |
432 | | // Find the next part separator. |
433 | 810 | std::string::size_type const n = remnant.find_first_of(".+-"_s); |
434 | 810 | if (n == 0) { |
435 | | // The part is an empty string. |
436 | 34 | return cm::nullopt; |
437 | 34 | } |
438 | | |
439 | | // Extract the part as a number. |
440 | 776 | cm::string_view const part = remnant.substr(0, n); |
441 | 776 | std::string::size_type const l = part.size(); |
442 | 776 | std::string::size_type p; |
443 | 776 | unsigned long const value = std::stoul(std::string{ part }, &p); |
444 | 776 | if (p != l || value > std::numeric_limits<unsigned>::max()) { |
445 | | // The part was not a valid number or is too big. |
446 | 210 | return cm::nullopt; |
447 | 210 | } |
448 | 566 | result.ReleaseComponents.push_back(static_cast<unsigned>(value)); |
449 | | |
450 | | // Have we consumed the entire input? |
451 | 566 | if (n == std::string::npos) { |
452 | 60 | return { std::move(result) }; |
453 | 60 | } |
454 | | |
455 | | // Lop off the current part. |
456 | 506 | char const sep = remnant[n]; |
457 | 506 | remnant = remnant.substr(n + 1); |
458 | 506 | if (sep == '+' || sep == '-') { |
459 | | // If we hit the local label, we're done. |
460 | 30 | result.LocalLabel = remnant; |
461 | 30 | return { std::move(result) }; |
462 | 30 | } |
463 | | |
464 | | // We just consumed a '.'; check that there's more. |
465 | 476 | if (remnant.empty()) { |
466 | | // A trailing part separator is not allowed. |
467 | 6 | return cm::nullopt; |
468 | 6 | } |
469 | | |
470 | | // Continue with the remaining input. |
471 | 476 | } |
472 | | |
473 | | // Unreachable. |
474 | 340 | } |
475 | | |
476 | | } // namespace |
477 | | |
478 | | std::unique_ptr<cmPackageInfoReader> cmPackageInfoReader::Read( |
479 | | cmMakefile* makefile, std::string const& path, |
480 | | cmPackageInfoReader const* parent) |
481 | 18.2k | { |
482 | | // Read file and perform some basic validation: |
483 | | // - the input is valid JSON |
484 | | // - the input is a JSON object |
485 | | // - the input has a "cps_version" that we (in theory) know how to parse |
486 | 18.2k | Json::Value data = ReadJson(path); |
487 | 18.2k | if (!data.isObject() || (!parent && !CheckSchemaVersion(data))) { |
488 | 4.67k | return nullptr; |
489 | 4.67k | } |
490 | | |
491 | | // - the input has a "name" attribute that is a non-empty string |
492 | 13.5k | Json::Value const& name = data["name"]; |
493 | 13.5k | if (!name.isString() || name.empty()) { |
494 | 24 | return nullptr; |
495 | 24 | } |
496 | | |
497 | | // - the input has a "components" attribute that is a JSON object |
498 | 13.5k | if (!data["components"].isObject()) { |
499 | 20 | return nullptr; |
500 | 20 | } |
501 | | |
502 | 13.5k | std::string prefix = (parent ? parent->Prefix : DeterminePrefix(path, data)); |
503 | 13.5k | if (prefix.empty()) { |
504 | 694 | return nullptr; |
505 | 694 | } |
506 | | |
507 | | // Seems sane enough to hand back to the caller. |
508 | 12.8k | std::unique_ptr<cmPackageInfoReader> reader{ new cmPackageInfoReader }; |
509 | 12.8k | reader->Data = std::move(data); |
510 | 12.8k | reader->Prefix = std::move(prefix); |
511 | 12.8k | reader->Path = path; |
512 | | |
513 | | // Determine other information we need to know immediately, or (if this is |
514 | | // a supplemental reader) copy from the parent. |
515 | 12.8k | if (parent) { |
516 | 4.27k | reader->ComponentTargets = parent->ComponentTargets; |
517 | 4.27k | reader->DefaultConfigurations = parent->DefaultConfigurations; |
518 | 4.27k | reader->RequirementDomains = parent->RequirementDomains; |
519 | 8.55k | } else { |
520 | 8.55k | for (std::string const& config : |
521 | 41.3k | ReadList(reader->Data, "configurations")) { |
522 | 41.3k | reader->DefaultConfigurations.emplace_back( |
523 | 41.3k | cmSystemTools::UpperCase(config)); |
524 | 41.3k | } |
525 | 8.55k | } |
526 | | |
527 | | // Read package-level requirements. (Configuration supplements are not |
528 | | // allowed to declare package requirements.) |
529 | 12.8k | if (cmSystemTools::GetFilenameName(path).find('@') == std::string::npos) { |
530 | 12.8k | reader->ReadRequirements(); |
531 | 12.8k | } |
532 | | |
533 | | // Check for a default license. |
534 | 12.8k | Json::Value const& defaultLicense = reader->Data["default_license"]; |
535 | 12.8k | if (!defaultLicense.isNull()) { |
536 | 0 | if (defaultLicense.isString()) { |
537 | 0 | reader->DefaultLicense = defaultLicense.asString(); |
538 | 0 | } else { |
539 | 0 | makefile->IssueMessage( |
540 | 0 | MessageType::WARNING, |
541 | 0 | "Package attribute \"default_license\" is not a string."); |
542 | 0 | } |
543 | 12.8k | } else if (parent) { |
544 | 4.27k | reader->DefaultLicense = parent->DefaultLicense; |
545 | 8.55k | } else { |
546 | | // If there is no 'default_license', check for 'license'. Note that we |
547 | | // intentionally allow `default_license` on an appendix to override the |
548 | | // parent, but we do not consider `license` on an appendix. This is |
549 | | // consistent with not allowing LICENSE and APPENDIX to be used together. |
550 | 8.55k | Json::Value const& packageLicense = reader->Data["license"]; |
551 | 8.55k | if (!packageLicense.isNull()) { |
552 | 4 | if (packageLicense.isString()) { |
553 | 4 | reader->DefaultLicense = packageLicense.asString(); |
554 | 4 | } else { |
555 | 0 | makefile->IssueMessage( |
556 | 0 | MessageType::WARNING, |
557 | 0 | "Package attribute \"license\" is not a string."); |
558 | 0 | } |
559 | 4 | } |
560 | 8.55k | } |
561 | | |
562 | 12.8k | return reader; |
563 | 13.5k | } |
564 | | |
565 | | std::string cmPackageInfoReader::GetName() const |
566 | 17.1k | { |
567 | 17.1k | return ToString(this->Data["name"]); |
568 | 17.1k | } |
569 | | |
570 | | cm::optional<std::string> cmPackageInfoReader::GetVersion() const |
571 | 8.55k | { |
572 | 8.55k | Json::Value const& version = this->Data["version"]; |
573 | 8.55k | if (version.isString()) { |
574 | 124 | return version.asString(); |
575 | 124 | } |
576 | 8.43k | return cm::nullopt; |
577 | 8.55k | } |
578 | | |
579 | | cm::optional<std::string> cmPackageInfoReader::GetCompatVersion() const |
580 | 8.55k | { |
581 | 8.55k | Json::Value const& version = this->Data["compat_version"]; |
582 | 8.55k | if (version.isString()) { |
583 | 250 | return version.asString(); |
584 | 250 | } |
585 | 8.30k | return cm::nullopt; |
586 | 8.55k | } |
587 | | |
588 | | cm::optional<cmPackageInfoReader::Pep440Version> |
589 | | cmPackageInfoReader::ParseVersion( |
590 | | cm::optional<std::string> const& version) const |
591 | 17.1k | { |
592 | | // Check that we have a version. |
593 | 17.1k | if (!version) { |
594 | 16.7k | return cm::nullopt; |
595 | 16.7k | } |
596 | | |
597 | | // Check if we know how to parse the version. |
598 | 374 | Json::Value const& schema = this->Data["version_schema"]; |
599 | 374 | if (schema.isNull() || cmStrCaseEq(ToString(schema), "simple"_s)) { |
600 | 342 | return ParseSimpleVersion(*version); |
601 | 342 | } |
602 | | |
603 | 32 | return cm::nullopt; |
604 | 374 | } |
605 | | |
606 | | void cmPackageInfoReader::ReadRequirements() |
607 | 12.8k | { |
608 | 12.8k | auto const& requirementObjects = this->Data["requires"]; |
609 | 12.8k | if (!requirementObjects.isObject()) { |
610 | 12.6k | return; |
611 | 12.6k | } |
612 | | |
613 | 162 | for (auto ri = requirementObjects.begin(), re = requirementObjects.end(); |
614 | 5.05k | ri != re; ++ri) { |
615 | 4.89k | if (!ri->isObject() && !ri->isNull()) { |
616 | 126 | continue; |
617 | 126 | } |
618 | 4.76k | cmPackageRequirement r{ ri.name(), |
619 | 4.76k | ToString((*ri)["version"]), |
620 | 4.76k | ReadList(*ri, "components"), |
621 | 4.76k | ReadList(*ri, "hints"), |
622 | 4.76k | {} }; |
623 | 4.76k | for (std::string const& domain : |
624 | 4.76k | ReadList(GetExtensions(*ri), "domains@v1")) { |
625 | 0 | r.Domains.push_back(domain == "pkg-config" ? cm::PackageDomain::PkgConfig |
626 | 0 | : cm::PackageDomain::Unknown); |
627 | 0 | } |
628 | 4.76k | if (!r.Domains.empty()) { |
629 | 0 | this->RequirementDomains[r.Name] = r.Domains; |
630 | 4.76k | } else { |
631 | | // A local declaration without domains clears any inherited domains. |
632 | 4.76k | this->RequirementDomains.erase(r.Name); |
633 | 4.76k | } |
634 | 4.76k | this->Requirements.emplace_back(std::move(r)); |
635 | 4.76k | } |
636 | 162 | } |
637 | | |
638 | | std::string cmPackageInfoReader::ResolveTargetName( |
639 | | cmMakefile* makefile, std::string const& name, |
640 | | std::string const& context) const |
641 | 56.8k | { |
642 | | // A pkg-config package is exposed as a single component of the same name. |
643 | | // Only resolve it to a foreign target if this requirement opts in. |
644 | 56.8k | std::string::size_type const n = name.find(':'); |
645 | 56.8k | if (n != std::string::npos && n > 0 && |
646 | 4.45k | name.substr(0, n) == name.substr(n + 1)) { |
647 | 2 | std::string const package = name.substr(0, n); |
648 | 2 | auto const di = this->RequirementDomains.find(package); |
649 | 2 | if (di != this->RequirementDomains.end() && |
650 | 0 | cm::contains(di->second, cm::PackageDomain::PkgConfig)) { |
651 | 0 | if (cmTarget* foreignTarget = |
652 | 0 | makefile->FindForeignTarget("pkgcfg", package)) { |
653 | 0 | return foreignTarget->GetName(); |
654 | 0 | } |
655 | 0 | } |
656 | 2 | } |
657 | | |
658 | 56.8k | return NormalizeTargetName(name, context); |
659 | 56.8k | } |
660 | | |
661 | | std::vector<std::string> cmPackageInfoReader::GetComponentNames() const |
662 | 8.55k | { |
663 | 8.55k | std::vector<std::string> componentNames; |
664 | | |
665 | 8.55k | Json::Value const& components = this->Data["components"]; |
666 | 51.4k | for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) { |
667 | 42.8k | componentNames.emplace_back(ci.name()); |
668 | 42.8k | } |
669 | | |
670 | 8.55k | return componentNames; |
671 | 8.55k | } |
672 | | |
673 | | std::string cmPackageInfoReader::ResolvePath(std::string path) const |
674 | 81.3k | { |
675 | 81.3k | cmSystemTools::ConvertToUnixSlashes(path); |
676 | 81.3k | if (cmHasPrefix(path, "@prefix@"_s)) { |
677 | 23.9k | return cmStrCat(this->Prefix, path.substr(8)); |
678 | 23.9k | } |
679 | 57.4k | if (!cmSystemTools::FileIsFullPath(path)) { |
680 | 55.5k | return cmStrCat(cmSystemTools::GetFilenamePath(this->Path), '/', path); |
681 | 55.5k | } |
682 | 1.89k | return path; |
683 | 57.4k | } |
684 | | |
685 | | void cmPackageInfoReader::AddTargetConfiguration( |
686 | | cmTarget* target, cm::string_view configuration) const |
687 | 16.2k | { |
688 | 16.2k | static std::string const icProp = "IMPORTED_CONFIGURATIONS"; |
689 | | |
690 | 16.2k | std::string const& configUpper = cmSystemTools::UpperCase(configuration); |
691 | | |
692 | | // Get existing list of imported configurations. |
693 | 16.2k | cmList configs; |
694 | 16.2k | if (cmValue v = target->GetProperty(icProp)) { |
695 | 13.0k | configs.assign(cmSystemTools::UpperCase(*v)); |
696 | 13.0k | } else { |
697 | | // If the existing list is empty, just add the new one and return. |
698 | 3.25k | target->SetProperty(icProp, configUpper); |
699 | 3.25k | return; |
700 | 3.25k | } |
701 | | |
702 | 13.0k | if (cm::contains(configs, configUpper)) { |
703 | | // If the configuration is already listed, we don't need to do anything. |
704 | 16 | return; |
705 | 16 | } |
706 | | |
707 | | // Add the new configuration. |
708 | 13.0k | configs.append(configUpper); |
709 | | |
710 | | // Rebuild the configuration list by extracting any configuration in the |
711 | | // default configurations and reinserting it at the beginning of the list |
712 | | // according to the order of the default configurations. |
713 | 13.0k | std::vector<std::string> newConfigs; |
714 | 61.1k | for (std::string const& c : this->DefaultConfigurations) { |
715 | 61.1k | auto ci = std::find(configs.begin(), configs.end(), c); |
716 | 61.1k | if (ci != configs.end()) { |
717 | 0 | newConfigs.emplace_back(std::move(*ci)); |
718 | 0 | configs.erase(ci); |
719 | 0 | } |
720 | 61.1k | } |
721 | 111k | for (std::string& c : configs) { |
722 | 111k | newConfigs.emplace_back(std::move(c)); |
723 | 111k | } |
724 | | |
725 | 13.0k | target->SetProperty("IMPORTED_CONFIGURATIONS", cmJoin(newConfigs, ";"_s)); |
726 | 13.0k | } |
727 | | |
728 | | void cmPackageInfoReader::SetImportProperty(cmMakefile* makefile, |
729 | | cmTarget* target, |
730 | | cm::string_view property, |
731 | | cm::string_view configuration, |
732 | | Json::Value const& object, |
733 | | std::string const& attribute) const |
734 | 143k | { |
735 | 143k | Json::Value const& value = object[attribute]; |
736 | 143k | if (!value.isNull()) { |
737 | 22.5k | std::string fullprop; |
738 | 22.5k | if (configuration.empty()) { |
739 | 14.9k | fullprop = cmStrCat("IMPORTED_"_s, property); |
740 | 14.9k | } else { |
741 | 7.62k | fullprop = cmStrCat("IMPORTED_"_s, property, '_', |
742 | 7.62k | cmSystemTools::UpperCase(configuration)); |
743 | 7.62k | } |
744 | | |
745 | 22.5k | if (value.isString()) { |
746 | 22.4k | target->SetProperty(fullprop, this->ResolvePath(value.asString())); |
747 | 22.4k | } else { |
748 | 162 | makefile->IssueMessage(MessageType::WARNING, |
749 | 162 | cmStrCat("Failed to set property \""_s, property, |
750 | 162 | "\" on target \""_s, target->GetName(), |
751 | 162 | "\": attribute \"", attribute, |
752 | 162 | "\" is not a string."_s)); |
753 | 162 | } |
754 | 22.5k | } |
755 | 143k | } |
756 | | |
757 | | void cmPackageInfoReader::SetMetaProperty( |
758 | | cmMakefile* makefile, cmTarget* target, std::string const& property, |
759 | | Json::Value const& object, std::string const& attribute, |
760 | | std::string const& defaultValue) const |
761 | 31.6k | { |
762 | 31.6k | Json::Value const& value = object[attribute]; |
763 | 31.6k | if (!value.isNull()) { |
764 | 0 | if (value.isString()) { |
765 | 0 | target->SetProperty(property, value.asString()); |
766 | 0 | } else { |
767 | 0 | makefile->IssueMessage(MessageType::WARNING, |
768 | 0 | cmStrCat("Failed to set property \""_s, property, |
769 | 0 | "\" on target \""_s, target->GetName(), |
770 | 0 | "\": attribute \"", attribute, |
771 | 0 | "\" is not a string."_s)); |
772 | 0 | } |
773 | 31.6k | } else if (!defaultValue.empty()) { |
774 | 0 | target->SetProperty(property, defaultValue); |
775 | 0 | } |
776 | 31.6k | } |
777 | | |
778 | | void cmPackageInfoReader::SetTargetProperties( |
779 | | cmMakefile* makefile, cmTarget* target, Json::Value const& data, |
780 | | std::string const& package, cm::string_view configuration) const |
781 | 47.8k | { |
782 | | // Add configuration (if applicable). |
783 | 47.8k | if (!configuration.empty()) { |
784 | 16.2k | this->AddTargetConfiguration(target, configuration); |
785 | 16.2k | } |
786 | | |
787 | | // Add compile and link features. |
788 | 47.8k | for (std::string const& def : ReadList(data, "compile_features")) { |
789 | 7.83k | AddCompileFeature(makefile, target, configuration, def); |
790 | 7.83k | } |
791 | | |
792 | 47.8k | for (std::string const& def : ReadList(data, "link_features")) { |
793 | 1.46k | AddLinkFeature(makefile, target, configuration, def); |
794 | 1.46k | } |
795 | | |
796 | | // Add compile and link flags. |
797 | 47.8k | AppendLanguageProperties( |
798 | 47.8k | makefile, target, "COMPILE_OPTIONS"_s, configuration, data, |
799 | 47.8k | "compile_flags", [](std::string flag) -> std::string { return flag; }); |
800 | | |
801 | 47.8k | for (std::string const& flag : ReadList(data, "link_flags")) { |
802 | 0 | AppendProperty(makefile, target, "LINK_OPTIONS"_s, configuration, flag); |
803 | 0 | } |
804 | | |
805 | | // Add compile definitions. |
806 | 47.8k | Json::Value const& defs = data["definitions"]; |
807 | 47.8k | DefinitionsMap definitionsMap; |
808 | 75.3k | for (auto ldi = defs.begin(), lde = defs.end(); ldi != lde; ++ldi) { |
809 | 27.4k | cm::string_view const originalLang = IterKey(ldi); |
810 | 27.4k | cm::string_view const lang = MapLanguage(originalLang); |
811 | 27.4k | if (lang.empty()) { |
812 | 3.65k | makefile->IssueMessage( |
813 | 3.65k | MessageType::WARNING, |
814 | 3.65k | cmStrCat(R"(ignoring unknown language ")"_s, originalLang, |
815 | 3.65k | R"(" in definitions for )"_s, target->GetName())); |
816 | 3.65k | continue; |
817 | 3.65k | } |
818 | | |
819 | 61.2k | for (auto di = ldi->begin(), de = ldi->end(); di != de; ++di) { |
820 | 37.3k | definitionsMap[di.name()].emplace(lang, *di); |
821 | 37.3k | } |
822 | 23.8k | } |
823 | 47.8k | AddDefinitions(makefile, target, configuration, definitionsMap); |
824 | | |
825 | | // Add include directories. |
826 | 47.8k | AppendLanguageProperties(makefile, target, "INCLUDE_DIRECTORIES"_s, |
827 | 47.8k | configuration, data, "includes", |
828 | 53.9k | [this](std::string p) -> std::string { |
829 | 53.9k | return this->ResolvePath(std::move(p)); |
830 | 53.9k | }); |
831 | | |
832 | | // Add link name/location(s). |
833 | 47.8k | this->SetImportProperty(makefile, target, "LOCATION"_s, // br |
834 | 47.8k | configuration, data, "location"); |
835 | | |
836 | 47.8k | this->SetImportProperty(makefile, target, "IMPLIB"_s, // br |
837 | 47.8k | configuration, data, "link_location"); |
838 | | |
839 | 47.8k | this->SetImportProperty(makefile, target, "SONAME"_s, // br |
840 | 47.8k | configuration, data, "link_name"); |
841 | | |
842 | | // Add link languages. |
843 | 47.8k | for (std::string const& originalLang : ReadList(data, "link_languages")) { |
844 | 7.93k | cm::string_view const lang = MapLanguage(originalLang, DisallowGlob); |
845 | 7.93k | if (!lang.empty()) { |
846 | 0 | AppendProperty(makefile, target, "LINK_LANGUAGES"_s, configuration, |
847 | 0 | std::string{ lang }); |
848 | 0 | } |
849 | 7.93k | } |
850 | | |
851 | | // Add transitive dependencies. |
852 | 47.8k | for (std::string const& dep : ReadList(data, "requires")) { |
853 | 36.3k | AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, |
854 | 36.3k | this->ResolveTargetName(makefile, dep, package)); |
855 | 36.3k | } |
856 | | |
857 | 47.8k | for (std::string const& dep : ReadList(data, "compile_requires")) { |
858 | 13.0k | std::string const& lib = |
859 | 13.0k | cmStrCat("$<COMPILE_ONLY:"_s, |
860 | 13.0k | this->ResolveTargetName(makefile, dep, package), '>'); |
861 | 13.0k | AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib); |
862 | 13.0k | } |
863 | | |
864 | 47.8k | for (std::string const& dep : ReadList(data, "link_requires")) { |
865 | 48 | std::string const& lib = cmStrCat( |
866 | 48 | "$<LINK_ONLY:"_s, this->ResolveTargetName(makefile, dep, package), '>'); |
867 | 48 | AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib); |
868 | 48 | } |
869 | | |
870 | 47.8k | for (std::string const& dep : ReadList(data, "dyld_requires")) { |
871 | 7.40k | AppendImportProperty(makefile, target, "LINK_DEPENDENT_LIBRARIES"_s, |
872 | 7.40k | configuration, |
873 | 7.40k | this->ResolveTargetName(makefile, dep, package)); |
874 | 7.40k | } |
875 | | |
876 | 47.8k | for (std::string const& lib : ReadList(data, "link_libraries")) { |
877 | 1.54k | AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib); |
878 | 1.54k | } |
879 | | |
880 | | // TODO: Handle non-configuration modules |
881 | | // once IMPORTED_CXX_MODULES supports it |
882 | 47.8k | if (!configuration.empty()) { |
883 | 16.2k | this->ReadCxxModulesMetadata(makefile, target, configuration, data); |
884 | 16.2k | } |
885 | | |
886 | | // Add other information. |
887 | 47.8k | if (configuration.empty()) { |
888 | 31.6k | this->SetMetaProperty(makefile, target, "SPDX_LICENSE", data, "license", |
889 | 31.6k | this->DefaultLicense); |
890 | 31.6k | } |
891 | 47.8k | } |
892 | | |
893 | | void cmPackageInfoReader::ReadCxxModulesMetadata( |
894 | | cmMakefile* makefile, cmTarget* target, cm::string_view configuration, |
895 | | Json::Value const& object) const |
896 | 16.2k | { |
897 | 16.2k | #ifndef CMAKE_BOOTSTRAP |
898 | 16.2k | Json::Value const& path = object["cpp_module_metadata"]; |
899 | | |
900 | 16.2k | if (!path.isString()) { |
901 | 16.2k | return; |
902 | 16.2k | } |
903 | | |
904 | 0 | cmCxxModuleMetadata::ParseResult result = |
905 | 0 | cmCxxModuleMetadata::LoadFromFile(this->ResolvePath(path.asString())); |
906 | |
|
907 | 0 | if (!result) { |
908 | 0 | makefile->IssueMessage( |
909 | 0 | MessageType::WARNING, |
910 | 0 | cmStrCat("Error parsing module manifest:\n"_s, result.Error)); |
911 | 0 | return; |
912 | 0 | } |
913 | | |
914 | 0 | cmCxxModuleMetadata::PopulateTarget(*target, *result.Meta, configuration); |
915 | 0 | #endif |
916 | 0 | } |
917 | | |
918 | | cmTarget* cmPackageInfoReader::AddComponent( |
919 | | cmMakefile* makefile, cm::TargetType type, std::string const& name, |
920 | | Json::Value const& data, std::string const& package, |
921 | | cm::ImportedTargetScope scope) const |
922 | 31.5k | { |
923 | | // Create the imported target. |
924 | 31.5k | cmTarget* const target = makefile->AddImportedTarget(name, type, scope); |
925 | 31.5k | target->SetOrigin(cmTarget::Origin::Cps); |
926 | | |
927 | | // Set target properties. |
928 | 31.5k | this->SetTargetProperties(makefile, target, data, package, {}); |
929 | 31.5k | auto const& cfgData = data["configurations"]; |
930 | 31.5k | if (cfgData.isObject()) { |
931 | 20.7k | for (auto ci = cfgData.begin(), ce = cfgData.end(); ci != ce; ++ci) { |
932 | 17.4k | cm::string_view const cfgName = IterKey(ci); |
933 | 17.4k | Json::Value const& cfgObj = *ci; |
934 | 17.4k | if (cfgObj.isObject()) { |
935 | 16.2k | this->SetTargetProperties(makefile, target, cfgObj, package, cfgName); |
936 | 16.2k | } else if (!cfgObj.isNull()) { |
937 | 506 | makefile->IssueMessage(MessageType::WARNING, |
938 | 506 | cmStrCat("Component \""_s, name, |
939 | 506 | "\" configuration \""_s, cfgName, |
940 | 506 | "\" is not an object."_s)); |
941 | 506 | } |
942 | 17.4k | } |
943 | 28.3k | } else if (!cfgData.isNull()) { |
944 | 8 | makefile->IssueMessage( |
945 | 8 | MessageType::WARNING, |
946 | 8 | cmStrCat(R"(Component ")"_s, name, |
947 | 8 | R"(" attribute "configurations" is not an object.)"_s)); |
948 | 8 | } |
949 | | |
950 | | // Add target sources. |
951 | 31.5k | this->AddTargetSources(makefile, target, data["file_sets"]); |
952 | | |
953 | 31.5k | return target; |
954 | 31.5k | } |
955 | | |
956 | | void cmPackageInfoReader::AddTargetSources(cmMakefile* makefile, |
957 | | cmTarget* target, |
958 | | Json::Value const& data) const |
959 | 31.5k | { |
960 | 31.5k | if (data.isArray()) { |
961 | 4.98k | for (Json::Value const& fs : data) { |
962 | 4.98k | if (fs.isObject()) { |
963 | 4.98k | std::string const& type = ToString(fs["type"]); |
964 | 4.98k | std::string const& root = this->ResolvePath(ToString(fs["root"])); |
965 | 4.98k | std::vector<std::string> files = ReadList(fs["files"]); |
966 | | |
967 | 4.98k | if (files.empty() || root.empty() || type != "includes") { |
968 | 1.14k | continue; |
969 | 1.14k | } |
970 | | |
971 | 3.84k | Json::Value const& ext = GetExtensions(fs); |
972 | 3.84k | std::string const& name = [&] { |
973 | 3.84k | std::string const& extName = ToString(ext["name@v1"]); |
974 | 3.84k | if (!extName.empty()) { |
975 | 710 | return extName; |
976 | 710 | } |
977 | 3.13k | return std::string{ cm::FileSetMetadata::HEADERS }; |
978 | 3.84k | }(); |
979 | | |
980 | | // TODO: When we support more than one file set type, check that we |
981 | | // don't see the same 'name' on sets of different types. |
982 | 3.84k | auto fileSet = target->GetOrCreateFileSet( |
983 | 3.84k | name, std::string{ cm::FileSetMetadata::HEADERS }, |
984 | 3.84k | cm::FileSetMetadata::Visibility::Interface); |
985 | 3.84k | cmListFileBacktrace const& bt = makefile->GetBacktrace(); |
986 | | |
987 | 9.01k | for (std::string& file : files) { |
988 | 9.01k | file = cmStrCat(root, '/', std::move(file)); |
989 | 9.01k | } |
990 | 3.84k | fileSet.first->AddFileEntry( |
991 | 3.84k | BT<std::string>{ cmList{ files }.to_string(), bt }); |
992 | | |
993 | 3.84k | fileSet.first->AddDirectoryEntry(BT<std::string>{ root, bt }); |
994 | 3.84k | } |
995 | 4.98k | } |
996 | 576 | } |
997 | 31.5k | } |
998 | | |
999 | | bool cmPackageInfoReader::ImportTargets(cmMakefile* makefile, |
1000 | | cmExecutionStatus& status, |
1001 | | cm::ImportedTargetScope scope) |
1002 | 8.55k | { |
1003 | 8.55k | std::string const& package = this->GetName(); |
1004 | | |
1005 | | // Read components. |
1006 | 8.55k | Json::Value const& components = this->Data["components"]; |
1007 | | |
1008 | 51.4k | for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) { |
1009 | 42.8k | cm::string_view const name = IterKey(ci); |
1010 | | |
1011 | | // Get and validate full target name. |
1012 | 42.8k | std::string const& fullName = cmStrCat(package, "::"_s, name); |
1013 | 42.8k | { |
1014 | 42.8k | std::string msg; |
1015 | 42.8k | if (!makefile->EnforceUniqueName(fullName, msg)) { |
1016 | 0 | status.SetError(msg); |
1017 | 0 | return false; |
1018 | 0 | } |
1019 | 42.8k | } |
1020 | | |
1021 | 42.8k | Json::Value const& component = *ci; |
1022 | 42.8k | if (!component.isObject()) { |
1023 | 5.93k | makefile->IssueMessage( |
1024 | 5.93k | MessageType::WARNING, |
1025 | 5.93k | cmStrCat(R"(Component ")"_s, fullName, R"(" is not an object.)"_s)); |
1026 | 5.93k | continue; |
1027 | 5.93k | } |
1028 | | |
1029 | 36.9k | auto createTarget = [&](cm::TargetType typeEnum) { |
1030 | 31.5k | return this->AddComponent(makefile, typeEnum, fullName, component, |
1031 | 31.5k | package, scope); |
1032 | 31.5k | }; |
1033 | | |
1034 | 36.9k | cmTarget* target = nullptr; |
1035 | 36.9k | std::string const& type = |
1036 | 36.9k | cmSystemTools::LowerCase(ToString(component["type"])); |
1037 | 36.9k | if (type == "symbolic"_s) { |
1038 | 8 | target = createTarget(cm::TargetType::INTERFACE_LIBRARY); |
1039 | 8 | target->SetSymbolic(true); |
1040 | 36.9k | } else if (type == "executable"_s) { |
1041 | 84 | target = createTarget(cm::TargetType::EXECUTABLE); |
1042 | 36.8k | } else if (type == "dylib"_s) { |
1043 | 6.66k | target = createTarget(cm::TargetType::SHARED_LIBRARY); |
1044 | 30.1k | } else if (type == "module"_s) { |
1045 | 44 | target = createTarget(cm::TargetType::MODULE_LIBRARY); |
1046 | 30.1k | } else if (type == "archive"_s) { |
1047 | 15.6k | target = createTarget(cm::TargetType::STATIC_LIBRARY); |
1048 | 15.6k | } else if (type == "interface"_s) { |
1049 | 9.15k | target = createTarget(cm::TargetType::INTERFACE_LIBRARY); |
1050 | 9.15k | } else { |
1051 | 5.31k | makefile->IssueMessage(MessageType::WARNING, |
1052 | 5.31k | cmStrCat(R"(component ")"_s, fullName, |
1053 | 5.31k | R"(" has unknown type ")"_s, type, |
1054 | 5.31k | R"(" and was not imported)"_s)); |
1055 | 5.31k | } |
1056 | | |
1057 | 36.9k | if (target) { |
1058 | 31.5k | this->ComponentTargets.emplace(std::string{ name }, target); |
1059 | 31.5k | } |
1060 | 36.9k | } |
1061 | | |
1062 | | // Read default components. |
1063 | 8.55k | std::vector<std::string> const& defaultComponents = |
1064 | 8.55k | ReadList(this->Data, "default_components"); |
1065 | 8.55k | if (!defaultComponents.empty()) { |
1066 | 0 | std::string msg; |
1067 | 0 | if (!makefile->EnforceUniqueName(package, msg)) { |
1068 | 0 | status.SetError(msg); |
1069 | 0 | return false; |
1070 | 0 | } |
1071 | | |
1072 | 0 | cmTarget* const target = makefile->AddImportedTarget( |
1073 | 0 | package, cm::TargetType::INTERFACE_LIBRARY, scope); |
1074 | 0 | for (std::string const& name : defaultComponents) { |
1075 | 0 | std::string const& fullName = cmStrCat(package, "::"_s, name); |
1076 | 0 | AppendProperty(makefile, target, "LINK_LIBRARIES"_s, {}, fullName); |
1077 | 0 | } |
1078 | 0 | target->SetExportPassthrough(true); |
1079 | 0 | } |
1080 | | |
1081 | 8.55k | return true; |
1082 | 8.55k | } |
1083 | | |
1084 | | bool cmPackageInfoReader::ImportTargetConfigurations( |
1085 | | cmMakefile* makefile, cmExecutionStatus& status) const |
1086 | 8.55k | { |
1087 | 8.55k | std::string const& configuration = ToString(this->Data["configuration"]); |
1088 | | |
1089 | 8.55k | if (configuration.empty()) { |
1090 | 8.55k | makefile->IssueMessage(MessageType::WARNING, |
1091 | 8.55k | cmStrCat("supplemental file "_s, this->Path, |
1092 | 8.55k | " does not specify a configuration"_s)); |
1093 | 8.55k | return true; |
1094 | 8.55k | } |
1095 | | |
1096 | 0 | std::string const& package = this->GetName(); |
1097 | 0 | Json::Value const& components = this->Data["components"]; |
1098 | |
|
1099 | 0 | for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) { |
1100 | | // Get component name and look up target. |
1101 | 0 | cm::string_view const name = IterKey(ci); |
1102 | 0 | auto const& ti = this->ComponentTargets.find(std::string{ name }); |
1103 | 0 | if (ti == this->ComponentTargets.end()) { |
1104 | 0 | status.SetError(cmStrCat("component "_s, name, " was not found"_s)); |
1105 | 0 | return false; |
1106 | 0 | } |
1107 | | |
1108 | | // Read supplemental data for component. |
1109 | 0 | if (!ci->isObject()) { |
1110 | 0 | makefile->IssueMessage( |
1111 | 0 | MessageType::WARNING, |
1112 | 0 | cmStrCat("Component \""_s, name, "\" is not an object."_s)); |
1113 | 0 | continue; |
1114 | 0 | } |
1115 | 0 | this->SetTargetProperties(makefile, ti->second, *ci, package, |
1116 | 0 | configuration); |
1117 | 0 | } |
1118 | | |
1119 | 0 | return true; |
1120 | 0 | } |