/src/CMake/Source/cmCMakePkgConfigCommand.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 | | |
4 | | #include "cmCMakePkgConfigCommand.h" |
5 | | |
6 | | #include <cstdio> |
7 | | #include <memory> |
8 | | #include <string> |
9 | | #include <unordered_map> |
10 | | #include <utility> |
11 | | #include <vector> |
12 | | |
13 | | #include <cm/filesystem> |
14 | | #include <cm/optional> |
15 | | #include <cm/string_view> |
16 | | #include <cmext/string_view> |
17 | | |
18 | | #include "cmsys/FStream.hxx" |
19 | | |
20 | | #include "cmArgumentParser.h" |
21 | | #include "cmArgumentParserTypes.h" |
22 | | #include "cmExecutionStatus.h" |
23 | | #include "cmList.h" |
24 | | #include "cmMakefile.h" |
25 | | #include "cmMessageType.h" |
26 | | #include "cmPkgConfigParser.h" |
27 | | #include "cmPkgConfigResolver.h" |
28 | | #include "cmStateTypes.h" |
29 | | #include "cmStringAlgorithms.h" |
30 | | #include "cmSubcommandTable.h" |
31 | | #include "cmSystemTools.h" |
32 | | #include "cmTarget.h" |
33 | | #include "cmValue.h" |
34 | | #include <cmllpkgc/llpkgc.h> |
35 | | |
36 | | // IWYU wants this |
37 | | namespace { |
38 | | struct ExtractArguments; |
39 | | struct PopulateArguments; |
40 | | struct ImportArguments; |
41 | | } |
42 | | |
43 | | namespace { |
44 | | |
45 | | cm::optional<std::string> GetPkgConfigBin(cmMakefile& mf) |
46 | 0 | { |
47 | 0 | cm::optional<std::string> result; |
48 | |
|
49 | 0 | auto pkgcfg = mf.GetDefinition("CMAKE_PKG_CONFIG_BIN"); |
50 | 0 | if (pkgcfg.IsNOTFOUND()) { |
51 | 0 | return result; |
52 | 0 | } |
53 | | |
54 | 0 | if (pkgcfg) { |
55 | 0 | result = *pkgcfg; |
56 | 0 | return result; |
57 | 0 | } |
58 | | |
59 | 0 | std::string path = cmSystemTools::FindProgram("pkgconf"); |
60 | 0 | if (path.empty()) { |
61 | 0 | path = cmSystemTools::FindProgram("pkg-config"); |
62 | 0 | if (path.empty()) { |
63 | 0 | mf.AddCacheDefinition("CMAKE_PKG_CONFIG_BIN", "pkg-config-NOTFOUND", |
64 | 0 | "Location of pkg-config or pkgconf binary", |
65 | 0 | cmStateEnums::FILEPATH); |
66 | 0 | return result; |
67 | 0 | } |
68 | 0 | } |
69 | | |
70 | 0 | mf.AddCacheDefinition("CMAKE_PKG_CONFIG_BIN", path, |
71 | 0 | "Location of pkg-config or pkgconf binary", |
72 | 0 | cmStateEnums::FILEPATH); |
73 | |
|
74 | 0 | result = std::move(path); |
75 | 0 | return result; |
76 | 0 | } |
77 | | |
78 | | std::vector<std::string> GetLocations(cmMakefile& mf, char const* cachevar, |
79 | | char const* envvar, char const* desc, |
80 | | char const* pcvar, bool need_pkgconf, |
81 | | std::vector<std::string> default_locs) |
82 | 0 | { |
83 | 0 | auto def = mf.GetDefinition(cachevar); |
84 | 0 | if (def) { |
85 | 0 | return cmList(def); |
86 | 0 | } |
87 | | |
88 | 0 | std::string paths; |
89 | 0 | if (cmSystemTools::GetEnv(envvar, paths)) { |
90 | 0 | cmPkgConfigResolver::ReplaceSep(paths); |
91 | 0 | mf.AddCacheDefinition(cachevar, paths, desc, cmStateEnums::STRING); |
92 | 0 | return cmList(paths); |
93 | 0 | } |
94 | | |
95 | 0 | auto pkgcfg = GetPkgConfigBin(mf); |
96 | 0 | if (!pkgcfg || (need_pkgconf && (pkgcfg->find("pkgconf") == pkgcfg->npos))) { |
97 | 0 | mf.AddCacheDefinition(cachevar, cmList::to_string(default_locs), desc, |
98 | 0 | cmStateEnums::STRING); |
99 | 0 | return default_locs; |
100 | 0 | } |
101 | | |
102 | 0 | std::string out; |
103 | 0 | cmSystemTools::RunSingleCommand({ *pkgcfg, pcvar, "pkg-config" }, &out, |
104 | 0 | nullptr, nullptr, nullptr, |
105 | 0 | cmSystemTools::OUTPUT_NONE); |
106 | |
|
107 | 0 | cmPkgConfigResolver::ReplaceSep(out); |
108 | 0 | out = cmTrimWhitespace(out); |
109 | 0 | mf.AddCacheDefinition(cachevar, out, desc, cmStateEnums::STRING); |
110 | 0 | return cmList(out); |
111 | 0 | } |
112 | | |
113 | | std::vector<std::string> GetPcLibDirs(cmMakefile& mf) |
114 | 0 | { |
115 | 0 | std::vector<std::string> default_locs = { |
116 | 0 | #ifndef _WIN32 |
117 | 0 | "/usr/lib/pkgconfig", "/usr/share/pkgconfig" |
118 | 0 | #endif |
119 | 0 | }; |
120 | 0 | return GetLocations(mf, "CMAKE_PKG_CONFIG_PC_LIB_DIRS", "PKG_CONFIG_LIBDIR", |
121 | 0 | "Default search locations for package files", |
122 | 0 | "--variable=pc_path", false, std::move(default_locs)); |
123 | 0 | } |
124 | | |
125 | | std::vector<std::string> GetSysLibDirs(cmMakefile& mf) |
126 | 0 | { |
127 | 0 | std::vector<std::string> default_locs = { |
128 | 0 | #ifndef _WIN32 |
129 | 0 | "/lib", "/usr/lib" |
130 | 0 | #endif |
131 | 0 | }; |
132 | 0 | return GetLocations( |
133 | 0 | mf, "CMAKE_PKG_CONFIG_SYS_LIB_DIRS", "PKG_CONFIG_SYSTEM_LIBRARY_PATH", |
134 | 0 | "System library directories filtered by flag mangling", |
135 | 0 | "--variable=pc_system_libdirs", true, std::move(default_locs)); |
136 | 0 | } |
137 | | |
138 | | std::vector<std::string> GetSysCflags(cmMakefile& mf) |
139 | 0 | { |
140 | 0 | std::vector<std::string> default_locs = { |
141 | 0 | #ifndef _WIN32 |
142 | 0 | "/usr/include" |
143 | 0 | #endif |
144 | 0 | }; |
145 | 0 | return GetLocations( |
146 | 0 | mf, "CMAKE_PKG_CONFIG_SYS_INCLUDE_DIRS", "PKG_CONFIG_SYSTEM_INCLUDE_PATH", |
147 | 0 | "System include directories filtered by flag mangling", |
148 | 0 | "--variable=pc_system_includedirs", true, std::move(default_locs)); |
149 | 0 | } |
150 | | |
151 | | std::vector<std::string> GetPkgConfSysLibs(cmMakefile& mf) |
152 | 0 | { |
153 | 0 | auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS"); |
154 | 0 | if (def) { |
155 | 0 | return cmList(def); |
156 | 0 | } |
157 | | |
158 | 0 | std::string paths; |
159 | 0 | if (!cmSystemTools::GetEnv("LIBRARY_PATH", paths)) { |
160 | 0 | return {}; |
161 | 0 | } |
162 | | |
163 | 0 | cmPkgConfigResolver::ReplaceSep(paths); |
164 | 0 | mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS", paths, |
165 | 0 | "Additional system library directories filtered by " |
166 | 0 | "flag mangling in PKGCONF mode", |
167 | 0 | cmStateEnums::STRING); |
168 | 0 | return cmList(paths); |
169 | 0 | } |
170 | | |
171 | | std::vector<std::string> GetPkgConfSysCflags(cmMakefile& mf) |
172 | 0 | { |
173 | 0 | auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PKGCONF_INCLUDES"); |
174 | 0 | if (def) { |
175 | 0 | return cmList(def); |
176 | 0 | } |
177 | | |
178 | 0 | std::string paths; |
179 | 0 | auto get_and_append = [&](char const* var) { |
180 | 0 | if (paths.empty()) { |
181 | 0 | cmSystemTools::GetEnv(var, paths); |
182 | 0 | } else { |
183 | 0 | std::string tmp; |
184 | 0 | cmSystemTools::GetEnv(var, tmp); |
185 | 0 | if (!tmp.empty()) { |
186 | 0 | paths += ";" + tmp; |
187 | 0 | } |
188 | 0 | } |
189 | 0 | }; |
190 | |
|
191 | 0 | get_and_append("CPATH"); |
192 | 0 | get_and_append("C_INCLUDE_PATH"); |
193 | 0 | get_and_append("CPLUS_INCLUDE_PATH"); |
194 | 0 | get_and_append("OBJC_INCLUDE_PATH"); |
195 | |
|
196 | | #ifdef _WIN32 |
197 | | get_and_append("INCLUDE"); |
198 | | #endif |
199 | |
|
200 | 0 | cmPkgConfigResolver::ReplaceSep(paths); |
201 | 0 | mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PKGCONF_INCLUDES", paths, |
202 | 0 | "Additional system include directories filtered by " |
203 | 0 | "flag mangling in PKGCONF mode", |
204 | 0 | cmStateEnums::STRING); |
205 | 0 | return cmList(paths); |
206 | 0 | } |
207 | | |
208 | | std::vector<std::string> GetPcPath(cmMakefile& mf) |
209 | 0 | { |
210 | 0 | auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PC_PATH"); |
211 | 0 | if (def) { |
212 | 0 | return cmList(def); |
213 | 0 | } |
214 | | |
215 | 0 | std::string pcpath; |
216 | 0 | if (cmSystemTools::GetEnv("PKG_CONFIG_PATH", pcpath)) { |
217 | 0 | auto result = cmSystemTools::SplitString(pcpath, cmPkgConfigResolver::Sep); |
218 | 0 | mf.AddCacheDefinition( |
219 | 0 | "CMAKE_PKG_CONFIG_PC_PATH", cmList::to_string(result), |
220 | 0 | "Additional search locations for package files", cmStateEnums::STRING); |
221 | 0 | return result; |
222 | 0 | } |
223 | | |
224 | 0 | mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PC_PATH", "", |
225 | 0 | "Additional search locations for package files", |
226 | 0 | cmStateEnums::STRING); |
227 | 0 | return {}; |
228 | 0 | } |
229 | | |
230 | | cm::optional<std::string> GetPath(cmMakefile& mf, char const* cachevar, |
231 | | char const* envvar, char const* desc) |
232 | 0 | { |
233 | 0 | cm::optional<std::string> result; |
234 | |
|
235 | 0 | auto def = mf.GetDefinition(cachevar); |
236 | 0 | if (def) { |
237 | 0 | result = *def; |
238 | 0 | return result; |
239 | 0 | } |
240 | | |
241 | 0 | std::string path; |
242 | 0 | if (cmSystemTools::GetEnv(envvar, path)) { |
243 | 0 | mf.AddCacheDefinition(cachevar, path, desc, cmStateEnums::FILEPATH); |
244 | 0 | result = std::move(path); |
245 | 0 | return result; |
246 | 0 | } |
247 | | |
248 | 0 | return result; |
249 | 0 | } |
250 | | |
251 | | cm::optional<std::string> GetSysrootDir(cmMakefile& mf) |
252 | 0 | { |
253 | 0 | return GetPath(mf, "CMAKE_PKG_CONFIG_SYSROOT_DIR", "PKG_CONFIG_SYSROOT_DIR", |
254 | 0 | "System root used for re-rooting package includes and " |
255 | 0 | "library directories"); |
256 | 0 | } |
257 | | |
258 | | cm::optional<std::string> GetTopBuildDir(cmMakefile& mf) |
259 | 0 | { |
260 | 0 | return GetPath(mf, "CMAKE_PKG_CONFIG_TOP_BUILD_DIR", |
261 | 0 | "PKG_CONFIG_TOP_BUILD_DIR", |
262 | 0 | "Package file top_build_dir variable default value"); |
263 | 0 | } |
264 | | |
265 | | bool GetBool(cmMakefile& mf, char const* cachevar, char const* envvar, |
266 | | char const* desc) |
267 | 0 | { |
268 | 0 | auto def = mf.GetDefinition(cachevar); |
269 | 0 | if (def) { |
270 | 0 | return def.IsOn(); |
271 | 0 | } |
272 | | |
273 | 0 | if (cmSystemTools::HasEnv(envvar)) { |
274 | 0 | mf.AddCacheDefinition(cachevar, "ON", desc, cmStateEnums::BOOL); |
275 | 0 | return true; |
276 | 0 | } |
277 | | |
278 | 0 | return false; |
279 | 0 | } |
280 | | |
281 | | bool GetDisableUninstalled(cmMakefile& mf) |
282 | 0 | { |
283 | 0 | return GetBool(mf, "CMAKE_PKG_CONFIG_DISABLE_UNINSTALLED", |
284 | 0 | "PKG_CONFIG_DISABLE_UNINSTALLED", |
285 | 0 | "Disable search for `-uninstalled` (build tree) packages"); |
286 | 0 | } |
287 | | |
288 | | bool GetAllowSysLibs(cmMakefile& mf) |
289 | 0 | { |
290 | 0 | return GetBool(mf, "CMAKE_PKG_CONFIG_ALLOW_SYS_LIBS", |
291 | 0 | "PKG_CONFIG_ALLOW_SYSTEM_LIBS", |
292 | 0 | "Allow system library directories during flag mangling"); |
293 | 0 | } |
294 | | |
295 | | bool GetAllowSysInclude(cmMakefile& mf) |
296 | 0 | { |
297 | 0 | return GetBool(mf, "CMAKE_PKG_CONFIG_ALLOW_SYS_INCLUDES", |
298 | 0 | "PKG_CONFIG_ALLOW_SYSTEM_CFLAGS", |
299 | 0 | "Allow system include paths during flag manglging"); |
300 | 0 | } |
301 | | |
302 | | struct CommonArguments : ArgumentParser::ParseResult |
303 | | { |
304 | | bool Required = false; |
305 | | bool Exact = false; |
306 | | bool Quiet = false; |
307 | | |
308 | | enum StrictnessType |
309 | | { |
310 | | STRICTNESS_STRICT, |
311 | | STRICTNESS_PERMISSIVE, |
312 | | STRICTNESS_BEST_EFFORT, |
313 | | }; |
314 | | |
315 | | StrictnessType Strictness = STRICTNESS_PERMISSIVE; |
316 | | std::string StrictnessError; |
317 | | |
318 | | ArgumentParser::Continue SetStrictness(cm::string_view strictness) |
319 | 0 | { |
320 | 0 | if (strictness == "STRICT"_s) { |
321 | 0 | Strictness = STRICTNESS_STRICT; |
322 | 0 | } else if (strictness == "PERMISSIVE"_s) { |
323 | 0 | Strictness = STRICTNESS_PERMISSIVE; |
324 | 0 | } else if (strictness == "BEST_EFFORT"_s) { |
325 | 0 | Strictness = STRICTNESS_BEST_EFFORT; |
326 | 0 | } else { |
327 | 0 | StrictnessError = |
328 | 0 | cmStrCat("Invalid 'STRICTNESS' '", strictness, |
329 | 0 | "'; must be one of 'STRICT', 'PERMISSIVE', or 'BEST_EFFORT'"); |
330 | 0 | } |
331 | 0 | return ArgumentParser::Continue::Yes; |
332 | 0 | } |
333 | | |
334 | | enum EnvModeType |
335 | | { |
336 | | ENVMODE_FDO, |
337 | | ENVMODE_PKGCONF, |
338 | | ENVMODE_IGNORE, |
339 | | }; |
340 | | |
341 | | EnvModeType EnvMode = ENVMODE_PKGCONF; |
342 | | std::string EnvModeError; |
343 | | |
344 | | ArgumentParser::Continue SetEnvMode(cm::string_view envMode) |
345 | 0 | { |
346 | 0 | if (envMode == "FDO"_s) { |
347 | 0 | EnvMode = ENVMODE_FDO; |
348 | 0 | } else if (envMode == "PKGCONF"_s) { |
349 | 0 | EnvMode = ENVMODE_PKGCONF; |
350 | 0 | } else if (envMode == "IGNORE"_s) { |
351 | 0 | EnvMode = ENVMODE_IGNORE; |
352 | 0 | } else { |
353 | 0 | EnvModeError = |
354 | 0 | cmStrCat("Invalid 'ENV_MODE' '", envMode, |
355 | 0 | "'; must be one of 'FDO', 'PKGCONF', or 'IGNORE'"); |
356 | 0 | } |
357 | 0 | return ArgumentParser::Continue::Yes; |
358 | 0 | } |
359 | | |
360 | | cm::optional<std::string> Package; |
361 | | cm::optional<std::string> Version; |
362 | | cm::optional<std::string> SysrootDir; |
363 | | cm::optional<std::string> TopBuildDir; |
364 | | |
365 | | cm::optional<bool> DisableUninstalled; |
366 | | |
367 | | cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> PcPath; |
368 | | cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> PcLibdir; |
369 | | |
370 | | bool CheckArgs(cmExecutionStatus& status) const |
371 | 0 | { |
372 | |
|
373 | 0 | if (!Package) { |
374 | 0 | status.SetError("A package name or absolute path must be specified"); |
375 | 0 | return false; |
376 | 0 | } |
377 | | |
378 | 0 | if (!StrictnessError.empty()) { |
379 | 0 | status.SetError(StrictnessError); |
380 | 0 | return false; |
381 | 0 | } |
382 | | |
383 | 0 | if (!EnvModeError.empty()) { |
384 | 0 | status.SetError(EnvModeError); |
385 | 0 | return false; |
386 | 0 | } |
387 | | |
388 | 0 | return true; |
389 | 0 | } |
390 | | }; |
391 | | |
392 | | #define BIND_COMMON(argtype) \ |
393 | | (cmArgumentParser<argtype>{}) \ |
394 | | .Bind(1, &argtype::Package) \ |
395 | | .Bind(2, &argtype::Version) \ |
396 | | .Bind("REQUIRED"_s, &argtype::Required) \ |
397 | | .Bind("EXACT"_s, &argtype::Exact) \ |
398 | | .Bind("QUIET"_s, &argtype::Quiet) \ |
399 | | .Bind("STRICTNESS"_s, &argtype::SetStrictness) \ |
400 | | .Bind("ENV_MODE"_s, &argtype::SetEnvMode) \ |
401 | | .Bind("PC_SYSROOT_DIR"_s, &argtype::SysrootDir) \ |
402 | | .Bind("TOP_BUILD_DIR"_s, &argtype::TopBuildDir) \ |
403 | | .Bind("DISABLE_UNINSTALLED"_s, &argtype::DisableUninstalled) \ |
404 | | .Bind("PC_LIBDIR"_s, &argtype::PcLibdir) \ |
405 | | .Bind("PC_PATH"_s, &argtype::PcPath) |
406 | | |
407 | | void CollectEnv(cmMakefile& mf, cmPkgConfigEnv& env, |
408 | | CommonArguments::EnvModeType mode) |
409 | 0 | { |
410 | 0 | if (mode == CommonArguments::EnvModeType::ENVMODE_IGNORE) { |
411 | 0 | return; |
412 | 0 | } |
413 | | |
414 | 0 | if (!env.Path) { |
415 | 0 | env.Path = GetPcPath(mf); |
416 | 0 | } |
417 | |
|
418 | 0 | if (!env.LibDirs) { |
419 | 0 | env.LibDirs = GetPcLibDirs(mf); |
420 | 0 | } |
421 | |
|
422 | 0 | if (!env.DisableUninstalled) { |
423 | 0 | env.DisableUninstalled = GetDisableUninstalled(mf); |
424 | 0 | } |
425 | |
|
426 | 0 | if (!env.SysrootDir) { |
427 | 0 | env.SysrootDir = GetSysrootDir(mf); |
428 | 0 | } |
429 | |
|
430 | 0 | if (!env.TopBuildDir) { |
431 | 0 | env.TopBuildDir = GetTopBuildDir(mf); |
432 | 0 | } |
433 | |
|
434 | 0 | env.AllowSysCflags = GetAllowSysInclude(mf); |
435 | 0 | env.SysCflags = GetSysCflags(mf); |
436 | |
|
437 | 0 | env.AllowSysLibs = GetAllowSysLibs(mf); |
438 | 0 | env.SysLibs = GetSysLibDirs(mf); |
439 | |
|
440 | 0 | if (mode == CommonArguments::EnvModeType::ENVMODE_FDO) { |
441 | 0 | return; |
442 | 0 | } |
443 | | |
444 | 0 | *env.SysCflags += GetPkgConfSysCflags(mf); |
445 | 0 | *env.SysLibs += GetPkgConfSysLibs(mf); |
446 | 0 | } |
447 | | |
448 | | struct ImportEnv |
449 | | { |
450 | | bool required; |
451 | | bool quiet; |
452 | | bool exact; |
453 | | bool err; |
454 | | CommonArguments::StrictnessType strictness; |
455 | | cmExecutionStatus& status; |
456 | | }; |
457 | | |
458 | | void warn_or_error(std::string const& err, ImportEnv& imEnv) |
459 | 0 | { |
460 | 0 | if (imEnv.required) { |
461 | 0 | imEnv.status.SetError(err); |
462 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
463 | 0 | } else if (!imEnv.quiet) { |
464 | 0 | imEnv.status.GetMakefile().IssueMessage(MessageType::WARNING, err); |
465 | 0 | } |
466 | 0 | imEnv.err = true; |
467 | 0 | } |
468 | | |
469 | | cm::optional<cmPkgConfigResult> ReadPackage(std::string const& package, |
470 | | ImportEnv& imEnv, |
471 | | cmPkgConfigEnv& pcEnv) |
472 | 0 | { |
473 | 0 | cm::optional<cmPkgConfigResult> result; |
474 | 0 | cm::filesystem::path path{ package }; |
475 | |
|
476 | 0 | if (path.extension() == ".pc") { |
477 | 0 | if (!cmSystemTools::FileExists(path.string())) { |
478 | 0 | return result; |
479 | 0 | } |
480 | 0 | } else { |
481 | |
|
482 | 0 | if (pcEnv.DisableUninstalled && !*pcEnv.DisableUninstalled) { |
483 | 0 | auto uninstalled = path; |
484 | 0 | uninstalled.concat("-uninstalled.pc"); |
485 | 0 | uninstalled = |
486 | 0 | cmSystemTools::FindFile(uninstalled.string(), pcEnv.search, true); |
487 | 0 | if (uninstalled.empty()) { |
488 | 0 | path = cmSystemTools::FindFile(path.concat(".pc").string(), |
489 | 0 | pcEnv.search, true); |
490 | 0 | if (path.empty()) { |
491 | 0 | return result; |
492 | 0 | } |
493 | 0 | } else { |
494 | 0 | path = uninstalled; |
495 | 0 | } |
496 | 0 | } else { |
497 | 0 | path = cmSystemTools::FindFile(path.concat(".pc").string(), pcEnv.search, |
498 | 0 | true); |
499 | 0 | if (path.empty()) { |
500 | 0 | return result; |
501 | 0 | } |
502 | 0 | } |
503 | 0 | } |
504 | | |
505 | 0 | auto len = cmSystemTools::FileLength(path.string()); |
506 | | |
507 | | // Windows requires this weird string -> c_str dance |
508 | 0 | cmsys::ifstream ifs(path.string().c_str(), std::ios::binary); |
509 | |
|
510 | 0 | if (!ifs) { |
511 | 0 | warn_or_error(cmStrCat("Could not open file '", path.string(), '\''), |
512 | 0 | imEnv); |
513 | 0 | return result; |
514 | 0 | } |
515 | | |
516 | 0 | std::unique_ptr<char[]> buf(new char[len]); |
517 | 0 | ifs.read(buf.get(), len); |
518 | | |
519 | | // Shouldn't have hit eof on previous read, should hit eof now |
520 | 0 | if (ifs.fail() || ifs.eof() || ifs.get() != EOF) { |
521 | 0 | warn_or_error(cmStrCat("Error while reading file '", path.string(), '\''), |
522 | 0 | imEnv); |
523 | 0 | return result; |
524 | 0 | } |
525 | | |
526 | 0 | using StrictnessType = CommonArguments::StrictnessType; |
527 | |
|
528 | 0 | cmPkgConfigParser parser; |
529 | 0 | auto err = parser.Finish(buf.get(), len); |
530 | |
|
531 | 0 | if (imEnv.strictness != StrictnessType::STRICTNESS_BEST_EFFORT && |
532 | 0 | err != PCE_OK) { |
533 | 0 | warn_or_error(cmStrCat("Parsing failed for file '", path.string(), '\''), |
534 | 0 | imEnv); |
535 | 0 | return result; |
536 | 0 | } |
537 | | |
538 | 0 | if (imEnv.strictness == StrictnessType::STRICTNESS_STRICT) { |
539 | 0 | result = cmPkgConfigResolver::ResolveStrict(parser.Data(), pcEnv); |
540 | 0 | } else if (imEnv.strictness == StrictnessType::STRICTNESS_PERMISSIVE) { |
541 | 0 | result = cmPkgConfigResolver::ResolvePermissive(parser.Data(), pcEnv); |
542 | 0 | } else { |
543 | 0 | result = cmPkgConfigResolver::ResolveBestEffort(parser.Data(), pcEnv); |
544 | 0 | } |
545 | |
|
546 | 0 | if (!result) { |
547 | 0 | warn_or_error( |
548 | 0 | cmStrCat("Resolution failed for file '", path.string(), '\''), imEnv); |
549 | 0 | } |
550 | |
|
551 | 0 | return result; |
552 | 0 | } |
553 | | |
554 | | cm::optional<cmPkgConfigResult> ImportPackage( |
555 | | std::string const& package, cm::optional<std::string> version, |
556 | | ImportEnv& imEnv, cmPkgConfigEnv& pcEnv) |
557 | 0 | { |
558 | 0 | auto result = ReadPackage(package, imEnv, pcEnv); |
559 | |
|
560 | 0 | if (!result) { |
561 | 0 | if (!imEnv.err) { |
562 | 0 | warn_or_error(cmStrCat("Could not find pkg-config: '", package, '\''), |
563 | 0 | imEnv); |
564 | 0 | } |
565 | 0 | return result; |
566 | 0 | } |
567 | | |
568 | 0 | if (imEnv.exact) { |
569 | 0 | std::string ver; |
570 | |
|
571 | 0 | if (version) { |
572 | 0 | ver = cmPkgConfigResolver::ParseVersion(*version).Version; |
573 | 0 | } |
574 | |
|
575 | 0 | if (ver != result->Version()) { |
576 | 0 | warn_or_error( |
577 | 0 | cmStrCat("Package '", package, "' version '", result->Version(), |
578 | 0 | "' does not meet exact version requirement '", ver, '\''), |
579 | 0 | imEnv); |
580 | 0 | return {}; |
581 | 0 | } |
582 | |
|
583 | 0 | } else if (version) { |
584 | 0 | auto rv = cmPkgConfigResolver::ParseVersion(*version); |
585 | 0 | if (!cmPkgConfigResolver::CheckVersion(rv, result->Version())) { |
586 | 0 | warn_or_error( |
587 | 0 | cmStrCat("Package '", package, "' version '", result->Version(), |
588 | 0 | "' does not meet version requirement '", *version, '\''), |
589 | 0 | imEnv); |
590 | 0 | return {}; |
591 | 0 | } |
592 | 0 | } |
593 | | |
594 | 0 | result->env = &pcEnv; |
595 | 0 | return result; |
596 | 0 | } |
597 | | |
598 | | struct pkgStackEntry |
599 | | { |
600 | | cmPkgConfigVersionReq ver; |
601 | | std::string parent; |
602 | | }; |
603 | | |
604 | | cm::optional<cmPkgConfigResult> ImportPackage( |
605 | | std::string const& package, std::vector<pkgStackEntry> const& reqs, |
606 | | ImportEnv& imEnv, cmPkgConfigEnv& pcEnv) |
607 | 0 | { |
608 | 0 | auto result = ReadPackage(package, imEnv, pcEnv); |
609 | |
|
610 | 0 | if (!result) { |
611 | 0 | if (!imEnv.err) { |
612 | 0 | std::string req_str = cmStrCat('\'', reqs.begin()->parent, '\''); |
613 | 0 | for (auto it = reqs.begin() + 1; it != reqs.end(); ++it) { |
614 | 0 | req_str = cmStrCat(req_str, ", '", it->parent, '\''); |
615 | 0 | } |
616 | 0 | warn_or_error(cmStrCat("Could not find pkg-config: '", package, |
617 | 0 | "' required by: ", req_str), |
618 | 0 | imEnv); |
619 | 0 | } |
620 | 0 | return result; |
621 | 0 | } |
622 | | |
623 | 0 | auto ver = result->Version(); |
624 | 0 | for (auto const& req : reqs) { |
625 | |
|
626 | 0 | if (!cmPkgConfigResolver::CheckVersion(req.ver, ver)) { |
627 | 0 | warn_or_error(cmStrCat("Package '", package, "' version '", ver, |
628 | 0 | "' does not meet version requirement '", |
629 | 0 | req.ver.string(), "' of '", req.parent, '\''), |
630 | 0 | imEnv); |
631 | 0 | return {}; |
632 | 0 | } |
633 | 0 | } |
634 | | |
635 | 0 | result->env = &pcEnv; |
636 | 0 | return result; |
637 | 0 | } |
638 | | |
639 | | cm::optional<std::pair<cmPkgConfigEnv, ImportEnv>> HandleCommon( |
640 | | CommonArguments& args, cmExecutionStatus& status) |
641 | 0 | { |
642 | |
|
643 | 0 | auto& mf = status.GetMakefile(); |
644 | |
|
645 | 0 | if (!args.CheckArgs(status)) { |
646 | 0 | return {}; |
647 | 0 | } |
648 | | |
649 | 0 | cmPkgConfigEnv pcEnv; |
650 | |
|
651 | 0 | if (args.PcLibdir) { |
652 | 0 | pcEnv.LibDirs = std::move(*args.PcLibdir); |
653 | 0 | } |
654 | |
|
655 | 0 | if (args.PcPath) { |
656 | 0 | pcEnv.Path = std::move(*args.PcPath); |
657 | 0 | } |
658 | |
|
659 | 0 | pcEnv.DisableUninstalled = args.DisableUninstalled; |
660 | |
|
661 | 0 | if (args.SysrootDir) { |
662 | 0 | pcEnv.SysrootDir = std::move(*args.SysrootDir); |
663 | 0 | } |
664 | |
|
665 | 0 | if (args.TopBuildDir) { |
666 | 0 | pcEnv.TopBuildDir = std::move(*args.TopBuildDir); |
667 | 0 | } |
668 | |
|
669 | 0 | CollectEnv(mf, pcEnv, args.EnvMode); |
670 | |
|
671 | 0 | if (pcEnv.Path) { |
672 | 0 | pcEnv.search = *pcEnv.Path; |
673 | 0 | if (pcEnv.LibDirs) { |
674 | 0 | pcEnv.search += *pcEnv.LibDirs; |
675 | 0 | } |
676 | 0 | } else if (pcEnv.LibDirs) { |
677 | 0 | pcEnv.search = *pcEnv.LibDirs; |
678 | 0 | } |
679 | |
|
680 | 0 | return std::pair<cmPkgConfigEnv, ImportEnv>{ |
681 | 0 | pcEnv, |
682 | 0 | { args.Required, args.Quiet, args.Exact, false, args.Strictness, status } |
683 | 0 | }; |
684 | 0 | } |
685 | | |
686 | | struct ExtractArguments : CommonArguments |
687 | | { |
688 | | cm::optional<bool> AllowSystemIncludes; |
689 | | cm::optional<bool> AllowSystemLibs; |
690 | | |
691 | | cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> |
692 | | SystemIncludeDirs; |
693 | | cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> |
694 | | SystemLibraryDirs; |
695 | | }; |
696 | | |
697 | | auto const ExtractParser = |
698 | | BIND_COMMON(ExtractArguments) |
699 | | .Bind("ALLOW_SYSTEM_INCLUDES"_s, &ExtractArguments::AllowSystemIncludes) |
700 | | .Bind("ALLOW_SYSTEM_LIBS"_s, &ExtractArguments::AllowSystemLibs) |
701 | | .Bind("SYSTEM_INCLUDE_DIRS"_s, &ExtractArguments::SystemIncludeDirs) |
702 | | .Bind("SYSTEM_LIBRARY_DIRS"_s, &ExtractArguments::SystemLibraryDirs); |
703 | | |
704 | | bool HandleExtractCommand(std::vector<std::string> const& args, |
705 | | cmExecutionStatus& status) |
706 | 0 | { |
707 | |
|
708 | 0 | std::vector<std::string> unparsed; |
709 | 0 | auto parsedArgs = ExtractParser.Parse(args, &unparsed); |
710 | 0 | auto maybeEnv = HandleCommon(parsedArgs, status); |
711 | |
|
712 | 0 | if (!maybeEnv) { |
713 | 0 | return !parsedArgs.Required; |
714 | 0 | } |
715 | 0 | auto& pcEnv = maybeEnv->first; |
716 | 0 | auto& imEnv = maybeEnv->second; |
717 | |
|
718 | 0 | auto maybePackage = |
719 | 0 | ImportPackage(*parsedArgs.Package, parsedArgs.Version, imEnv, pcEnv); |
720 | 0 | if (!maybePackage) { |
721 | 0 | return !parsedArgs.Required; |
722 | 0 | } |
723 | 0 | auto& package = *maybePackage; |
724 | |
|
725 | 0 | if (parsedArgs.AllowSystemIncludes) { |
726 | 0 | pcEnv.AllowSysCflags = *parsedArgs.AllowSystemIncludes; |
727 | 0 | } |
728 | |
|
729 | 0 | if (parsedArgs.AllowSystemLibs) { |
730 | 0 | pcEnv.AllowSysLibs = *parsedArgs.AllowSystemLibs; |
731 | 0 | } |
732 | |
|
733 | 0 | if (parsedArgs.SystemIncludeDirs) { |
734 | 0 | pcEnv.SysCflags = *parsedArgs.SystemIncludeDirs; |
735 | 0 | } |
736 | |
|
737 | 0 | if (parsedArgs.SystemLibraryDirs) { |
738 | 0 | pcEnv.SysLibs = *parsedArgs.SystemLibraryDirs; |
739 | 0 | } |
740 | |
|
741 | 0 | auto& mf = status.GetMakefile(); |
742 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_NAME", package.Name()); |
743 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_DESCRIPTION", package.Description()); |
744 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_VERSION", package.Version()); |
745 | |
|
746 | 0 | auto make_list = [&](char const* def, |
747 | 0 | std::vector<cmPkgConfigDependency> const& deps) { |
748 | 0 | std::vector<cm::string_view> vec; |
749 | 0 | vec.reserve(deps.size()); |
750 | |
|
751 | 0 | for (auto const& dep : deps) { |
752 | 0 | vec.emplace_back(dep.Name); |
753 | 0 | } |
754 | |
|
755 | 0 | mf.AddDefinition(def, cmList::to_string(vec)); |
756 | 0 | }; |
757 | |
|
758 | 0 | make_list("CMAKE_PKG_CONFIG_CONFLICTS", package.Conflicts()); |
759 | 0 | make_list("CMAKE_PKG_CONFIG_PROVIDES", package.Provides()); |
760 | 0 | make_list("CMAKE_PKG_CONFIG_REQUIRES", package.Requires()); |
761 | 0 | make_list("CMAKE_PKG_CONFIG_REQUIRES_PRIVATE", package.Requires(true)); |
762 | |
|
763 | 0 | auto cflags = package.Cflags(); |
764 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS", cflags.Flagline); |
765 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES", |
766 | 0 | cmList::to_string(cflags.Includes)); |
767 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS", |
768 | 0 | cmList::to_string(cflags.CompileOptions)); |
769 | |
|
770 | 0 | cflags = package.Cflags(true); |
771 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS_PRIVATE", cflags.Flagline); |
772 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES_PRIVATE", |
773 | 0 | cmList::to_string(cflags.Includes)); |
774 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS_PRIVATE", |
775 | 0 | cmList::to_string(cflags.CompileOptions)); |
776 | |
|
777 | 0 | auto libs = package.Libs(); |
778 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS", libs.Flagline); |
779 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS", |
780 | 0 | cmList::to_string(libs.LibDirs)); |
781 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_LIBNAMES", |
782 | 0 | cmList::to_string(libs.LibNames)); |
783 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS", |
784 | 0 | cmList::to_string(libs.LinkOptions)); |
785 | |
|
786 | 0 | libs = package.Libs(true); |
787 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS_PRIVATE", libs.Flagline); |
788 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS_PRIVATE", |
789 | 0 | cmList::to_string(libs.LibDirs)); |
790 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_LIBNAMES_PRIVATE", |
791 | 0 | cmList::to_string(libs.LibNames)); |
792 | 0 | mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS_PRIVATE", |
793 | 0 | cmList::to_string(libs.LinkOptions)); |
794 | |
|
795 | 0 | return true; |
796 | 0 | } |
797 | | |
798 | | using pkgStack = std::unordered_map<std::string, std::vector<pkgStackEntry>>; |
799 | | using pkgProviders = std::unordered_map<std::string, std::string>; |
800 | | |
801 | | cmTarget* CreateCMakeTarget(std::string const& name, std::string const& prefix, |
802 | | cmPkgConfigResult& pkg, pkgProviders& providers, |
803 | | cmMakefile& mf) |
804 | 0 | { |
805 | 0 | auto* tgt = mf.AddForeignTarget("pkgcfg", cmStrCat(prefix, name)); |
806 | |
|
807 | 0 | tgt->AppendProperty("VERSION", pkg.Version()); |
808 | |
|
809 | 0 | auto libs = pkg.Libs(); |
810 | 0 | for (auto const& flag : libs.LibNames) { |
811 | 0 | tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", flag.substr(2)); |
812 | 0 | } |
813 | 0 | for (auto const& flag : libs.LibDirs) { |
814 | 0 | tgt->AppendProperty("INTERFACE_LINK_DIRECTORIES", flag.substr(2)); |
815 | 0 | } |
816 | 0 | tgt->AppendProperty("INTERFACE_LINK_OPTIONS", |
817 | 0 | cmList::to_string(libs.LinkOptions)); |
818 | |
|
819 | 0 | auto cflags = pkg.Cflags(); |
820 | 0 | for (auto const& flag : cflags.Includes) { |
821 | 0 | tgt->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES", flag.substr(2)); |
822 | 0 | } |
823 | 0 | tgt->AppendProperty("INTERFACE_COMPILE_OPTIONS", |
824 | 0 | cmList::to_string(cflags.CompileOptions)); |
825 | |
|
826 | 0 | for (auto& dep : pkg.Requires()) { |
827 | 0 | auto it = providers.find(dep.Name); |
828 | 0 | if (it != providers.end()) { |
829 | 0 | tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", it->second); |
830 | 0 | continue; |
831 | 0 | } |
832 | | |
833 | 0 | tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", |
834 | 0 | cmStrCat("@foreign_pkgcfg::", prefix, dep.Name)); |
835 | 0 | } |
836 | 0 | return tgt; |
837 | 0 | } |
838 | | |
839 | | bool CheckPackageDependencies( |
840 | | std::string const& name, std::string const& prefix, cmPkgConfigResult& pkg, |
841 | | pkgStack& inStack, |
842 | | std::unordered_map<std::string, cmPkgConfigResult>& outStack, |
843 | | pkgProviders& providers, ImportEnv& imEnv) |
844 | 0 | { |
845 | 0 | for (auto& dep : pkg.Requires()) { |
846 | 0 | auto prov_it = providers.find(dep.Name); |
847 | 0 | if (prov_it != providers.end()) { |
848 | 0 | continue; |
849 | 0 | } |
850 | | |
851 | 0 | auto* tgt = imEnv.status.GetMakefile().FindTargetToUse( |
852 | 0 | cmStrCat("@foreign_pkgcfg::", prefix, dep.Name), |
853 | 0 | cmStateEnums::TargetDomain::FOREIGN); |
854 | 0 | if (tgt) { |
855 | 0 | auto ver = tgt->GetProperty("VERSION"); |
856 | 0 | if (!cmPkgConfigResolver::CheckVersion(dep.VerReq, *ver)) { |
857 | 0 | warn_or_error(cmStrCat("Package '", dep.Name, "' version '", *ver, |
858 | 0 | "' does not meet version requirement '", |
859 | 0 | dep.VerReq.string(), "' of '", name, '\''), |
860 | 0 | imEnv); |
861 | 0 | return false; |
862 | 0 | } |
863 | 0 | continue; |
864 | 0 | } |
865 | | |
866 | 0 | auto it = outStack.find(dep.Name); |
867 | 0 | if (it != outStack.end()) { |
868 | 0 | auto ver = it->second.Version(); |
869 | 0 | if (!cmPkgConfigResolver::CheckVersion(dep.VerReq, ver)) { |
870 | 0 | warn_or_error(cmStrCat("Package '", dep.Name, "' version '", ver, |
871 | 0 | "' does not meet version requirement '", |
872 | 0 | dep.VerReq.string(), "' of '", name, '\''), |
873 | 0 | imEnv); |
874 | 0 | return false; |
875 | 0 | } |
876 | 0 | continue; |
877 | 0 | } |
878 | | |
879 | 0 | inStack[dep.Name].emplace_back( |
880 | 0 | pkgStackEntry{ std::move(dep.VerReq), name }); |
881 | 0 | } |
882 | | |
883 | 0 | return true; |
884 | 0 | } |
885 | | |
886 | | struct PopulateArguments : CommonArguments |
887 | | { |
888 | | cm::optional<std::string> Prefix; |
889 | | cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Providers; |
890 | | }; |
891 | | |
892 | | #define BIND_POPULATE(argtype) \ |
893 | | BIND_COMMON(argtype) \ |
894 | | .Bind("PREFIX"_s, &argtype::Prefix) \ |
895 | | .Bind("BIND_PC_REQUIRES"_s, &argtype::Providers) |
896 | | |
897 | | auto const PopulateParser = BIND_POPULATE(PopulateArguments); |
898 | | |
899 | | std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args, |
900 | | cmExecutionStatus& status) |
901 | 0 | { |
902 | |
|
903 | 0 | std::string prefix = args.Prefix ? cmStrCat(*args.Prefix, "_"_s) : ""; |
904 | |
|
905 | 0 | auto& mf = status.GetMakefile(); |
906 | 0 | auto maybeEnv = HandleCommon(args, status); |
907 | |
|
908 | 0 | if (!maybeEnv) { |
909 | 0 | return { !args.Required, false }; |
910 | 0 | } |
911 | 0 | auto& pcEnv = maybeEnv->first; |
912 | 0 | auto& imEnv = maybeEnv->second; |
913 | |
|
914 | 0 | pcEnv.AllowSysCflags = true; |
915 | 0 | pcEnv.AllowSysLibs = true; |
916 | |
|
917 | 0 | pkgProviders providers; |
918 | 0 | if (args.Providers) { |
919 | 0 | for (auto const& provider_str : *args.Providers) { |
920 | 0 | auto assignment = provider_str.find('='); |
921 | 0 | if (assignment != std::string::npos) { |
922 | 0 | providers.emplace(provider_str.substr(0, assignment), |
923 | 0 | provider_str.substr(assignment + 1)); |
924 | 0 | } else { |
925 | 0 | imEnv.status.SetError(cmStrCat( |
926 | 0 | "No '=' found in BIND_PC_REQUIRES argument '", provider_str, '\'')); |
927 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
928 | 0 | return { false, false }; |
929 | 0 | } |
930 | 0 | } |
931 | 0 | } |
932 | | |
933 | 0 | pkgStack inStack; |
934 | 0 | std::unordered_map<std::string, cmPkgConfigResult> outStack; |
935 | |
|
936 | 0 | auto maybePackage = ImportPackage(*args.Package, args.Version, imEnv, pcEnv); |
937 | 0 | if (!maybePackage) { |
938 | 0 | return { !args.Required, false }; |
939 | 0 | } |
940 | 0 | imEnv.exact = false; |
941 | |
|
942 | 0 | if (!CheckPackageDependencies(*args.Package, prefix, *maybePackage, inStack, |
943 | 0 | outStack, providers, imEnv)) { |
944 | 0 | return { !args.Required, false }; |
945 | 0 | } |
946 | 0 | outStack[*args.Package] = std::move(*maybePackage); |
947 | |
|
948 | 0 | while (!inStack.empty()) { |
949 | 0 | auto name = inStack.begin()->first; |
950 | 0 | auto reqs = inStack.begin()->second; |
951 | 0 | maybePackage = ImportPackage(name, reqs, imEnv, pcEnv); |
952 | 0 | if (!maybePackage) { |
953 | 0 | return { !args.Required, false }; |
954 | 0 | } |
955 | 0 | if (!CheckPackageDependencies(name, prefix, *maybePackage, inStack, |
956 | 0 | outStack, providers, imEnv)) { |
957 | 0 | return { !args.Required, false }; |
958 | 0 | } |
959 | 0 | inStack.erase(name); |
960 | 0 | outStack[std::move(name)] = std::move(*maybePackage); |
961 | 0 | } |
962 | | |
963 | 0 | for (auto& entry : outStack) { |
964 | 0 | CreateCMakeTarget(entry.first, prefix, entry.second, providers, mf); |
965 | 0 | } |
966 | |
|
967 | 0 | return { true, true }; |
968 | 0 | } |
969 | | |
970 | | bool HandlePopulateCommand(std::vector<std::string> const& args, |
971 | | cmExecutionStatus& status) |
972 | 0 | { |
973 | 0 | std::vector<std::string> unparsed; |
974 | 0 | auto parsedArgs = PopulateParser.Parse(args, &unparsed); |
975 | |
|
976 | 0 | std::string prefix = |
977 | 0 | parsedArgs.Prefix ? cmStrCat(*parsedArgs.Prefix, "_"_s) : ""; |
978 | |
|
979 | 0 | auto foreign_name = |
980 | 0 | cmStrCat("@foreign_pkgcfg::", prefix, *parsedArgs.Package); |
981 | 0 | auto found_var = cmStrCat("PKGCONFIG_", *parsedArgs.Package, "_FOUND"); |
982 | |
|
983 | 0 | auto& mf = status.GetMakefile(); |
984 | |
|
985 | 0 | if (mf.FindTargetToUse(foreign_name, cmStateEnums::TargetDomain::FOREIGN)) { |
986 | 0 | mf.AddDefinition(found_var, "TRUE"); |
987 | 0 | return true; |
988 | 0 | } |
989 | | |
990 | 0 | auto result = PopulatePCTarget(parsedArgs, status); |
991 | 0 | mf.AddDefinition(found_var, result.second ? "TRUE" : "FALSE"); |
992 | 0 | return result.first; |
993 | 0 | } |
994 | | |
995 | | struct ImportArguments : PopulateArguments |
996 | | { |
997 | | cm::optional<std::string> Name; |
998 | | }; |
999 | | |
1000 | | auto const ImportParser = |
1001 | | BIND_POPULATE(ImportArguments).Bind("NAME"_s, &ImportArguments::Name); |
1002 | | |
1003 | | bool HandleImportCommand(std::vector<std::string> const& args, |
1004 | | cmExecutionStatus& status) |
1005 | 0 | { |
1006 | 0 | std::vector<std::string> unparsed; |
1007 | 0 | auto parsedArgs = ImportParser.Parse(args, &unparsed); |
1008 | |
|
1009 | 0 | std::string prefix = |
1010 | 0 | parsedArgs.Prefix ? cmStrCat(*parsedArgs.Prefix, "_"_s) : ""; |
1011 | |
|
1012 | 0 | auto foreign_name = |
1013 | 0 | cmStrCat("@foreign_pkgcfg::", prefix, *parsedArgs.Package); |
1014 | 0 | auto local_name = |
1015 | 0 | cmStrCat("PkgConfig::", parsedArgs.Name.value_or(*parsedArgs.Package)); |
1016 | 0 | auto found_var = cmStrCat("PKGCONFIG_", *parsedArgs.Package, "_FOUND"); |
1017 | |
|
1018 | 0 | auto& mf = status.GetMakefile(); |
1019 | |
|
1020 | 0 | if (mf.FindTargetToUse(local_name)) { |
1021 | 0 | mf.AddDefinition(found_var, "TRUE"); |
1022 | 0 | return true; |
1023 | 0 | } |
1024 | | |
1025 | 0 | if (!mf.FindTargetToUse(foreign_name, cmStateEnums::TargetDomain::FOREIGN)) { |
1026 | 0 | auto result = PopulatePCTarget(parsedArgs, status); |
1027 | 0 | if (!result.second) { |
1028 | 0 | mf.AddDefinition(found_var, "FALSE"); |
1029 | 0 | return result.first; |
1030 | 0 | } |
1031 | 0 | } |
1032 | | |
1033 | 0 | mf.AddDefinition(found_var, "TRUE"); |
1034 | 0 | auto* tgt = mf.AddImportedTarget( |
1035 | 0 | local_name, cmStateEnums::TargetType::INTERFACE_LIBRARY, false); |
1036 | 0 | tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", foreign_name); |
1037 | 0 | return true; |
1038 | 0 | } |
1039 | | |
1040 | | } // namespace |
1041 | | |
1042 | | bool cmCMakePkgConfigCommand(std::vector<std::string> const& args, |
1043 | | cmExecutionStatus& status) |
1044 | 0 | { |
1045 | 0 | if (args.size() < 2) { |
1046 | 0 | status.SetError("must be called with at least two arguments."); |
1047 | 0 | return false; |
1048 | 0 | } |
1049 | | |
1050 | 0 | static cmSubcommandTable const subcommand{ |
1051 | 0 | { "EXTRACT"_s, HandleExtractCommand }, |
1052 | 0 | { "POPULATE"_s, HandlePopulateCommand }, |
1053 | 0 | { "IMPORT"_s, HandleImportCommand }, |
1054 | 0 | }; |
1055 | |
|
1056 | 0 | return subcommand(args[0], args, status); |
1057 | 0 | } |