cmPkgConfigResolver.cxx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  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 "cmPkgConfigResolver.h"
  4. #include <algorithm>
  5. #include <cctype>
  6. #include <cstring>
  7. #include <iterator>
  8. #include <string>
  9. #include <unordered_map>
  10. #include <utility>
  11. #include <vector>
  12. #include <cm/optional>
  13. #include <cm/string_view>
  14. #include "cmPkgConfigParser.h"
  15. #include "cmStringAlgorithms.h"
  16. namespace {
  17. void TrimBack(std::string& str)
  18. {
  19. if (!str.empty()) {
  20. auto it = str.end() - 1;
  21. for (; std::isspace(*it); --it) {
  22. if (it == str.begin()) {
  23. str.clear();
  24. return;
  25. }
  26. }
  27. str.erase(++it, str.end());
  28. }
  29. }
  30. std::string AppendAndTrim(std::string& str, cm::string_view sv)
  31. {
  32. auto size = str.length();
  33. str += sv;
  34. if (str.empty()) {
  35. return {};
  36. }
  37. auto begin = str.begin() + size;
  38. auto cur = str.end() - 1;
  39. while (cur != begin && std::isspace(*cur)) {
  40. --cur;
  41. }
  42. if (std::isspace(*cur)) {
  43. return {};
  44. }
  45. return { &*begin, static_cast<std::size_t>(cur - begin) + 1 };
  46. }
  47. } // namespace
  48. std::string cmPkgConfigVersionReq::string() const
  49. {
  50. switch (Operation) {
  51. case ANY:
  52. return "";
  53. case LT:
  54. return cmStrCat('<', Version);
  55. case LT_EQ:
  56. return cmStrCat("<=", Version);
  57. case EQ:
  58. return cmStrCat('=', Version);
  59. case NEQ:
  60. return cmStrCat("!=", Version);
  61. case GT_EQ:
  62. return cmStrCat(">=", Version);
  63. case GT:
  64. return cmStrCat('>', Version);
  65. }
  66. return "";
  67. }
  68. std::string cmPkgConfigResult::StrOrDefault(std::string const& key,
  69. cm::string_view def)
  70. {
  71. auto it = Keywords.find(key);
  72. return it == Keywords.end() ? std::string{ def } : it->second;
  73. };
  74. std::string cmPkgConfigResult::Name()
  75. {
  76. return StrOrDefault("Name");
  77. }
  78. std::string cmPkgConfigResult::Description()
  79. {
  80. return StrOrDefault("Description");
  81. }
  82. std::string cmPkgConfigResult::Version()
  83. {
  84. return StrOrDefault("Version");
  85. }
  86. std::vector<cmPkgConfigDependency> cmPkgConfigResult::Conflicts()
  87. {
  88. auto it = Keywords.find("Conflicts");
  89. if (it == Keywords.end()) {
  90. return {};
  91. }
  92. return cmPkgConfigResolver::ParseDependencies(it->second);
  93. }
  94. std::vector<cmPkgConfigDependency> cmPkgConfigResult::Provides()
  95. {
  96. auto it = Keywords.find("Provides");
  97. if (it == Keywords.end()) {
  98. return {};
  99. }
  100. return cmPkgConfigResolver::ParseDependencies(it->second);
  101. }
  102. std::vector<cmPkgConfigDependency> cmPkgConfigResult::Requires(bool priv)
  103. {
  104. auto it = Keywords.find(priv ? "Requires.private" : "Requires");
  105. if (it == Keywords.end()) {
  106. return {};
  107. }
  108. return cmPkgConfigResolver::ParseDependencies(it->second);
  109. }
  110. cmPkgConfigCflagsResult cmPkgConfigResult::Cflags(bool priv)
  111. {
  112. std::string cflags;
  113. auto it = Keywords.find(priv ? "Cflags.private" : "Cflags");
  114. if (it != Keywords.end()) {
  115. cflags += it->second;
  116. }
  117. it = Keywords.find(priv ? "CFlags.private" : "CFlags");
  118. if (it != Keywords.end()) {
  119. if (!cflags.empty()) {
  120. cflags += " ";
  121. }
  122. cflags += it->second;
  123. }
  124. auto tokens = cmPkgConfigResolver::TokenizeFlags(cflags);
  125. if (env->AllowSysCflags) {
  126. if (env->SysrootDir) {
  127. return cmPkgConfigResolver::MangleCflags(tokens, *env->SysrootDir);
  128. }
  129. return cmPkgConfigResolver::MangleCflags(tokens);
  130. }
  131. if (env->SysCflags) {
  132. if (env->SysrootDir) {
  133. return cmPkgConfigResolver::MangleCflags(tokens, *env->SysrootDir,
  134. *env->SysCflags);
  135. }
  136. return cmPkgConfigResolver::MangleCflags(tokens, *env->SysCflags);
  137. }
  138. if (env->SysrootDir) {
  139. return cmPkgConfigResolver::MangleCflags(
  140. tokens, *env->SysrootDir, std::vector<std::string>{ "/usr/include" });
  141. }
  142. return cmPkgConfigResolver::MangleCflags(
  143. tokens, std::vector<std::string>{ "/usr/include" });
  144. }
  145. cmPkgConfigLibsResult cmPkgConfigResult::Libs(bool priv)
  146. {
  147. auto it = Keywords.find(priv ? "Libs.private" : "Libs");
  148. if (it == Keywords.end()) {
  149. return cmPkgConfigLibsResult();
  150. }
  151. auto tokens = cmPkgConfigResolver::TokenizeFlags(it->second);
  152. if (env->AllowSysLibs) {
  153. if (env->SysrootDir) {
  154. return cmPkgConfigResolver::MangleLibs(tokens, *env->SysrootDir);
  155. }
  156. return cmPkgConfigResolver::MangleLibs(tokens);
  157. }
  158. if (env->SysLibs) {
  159. if (env->SysrootDir) {
  160. return cmPkgConfigResolver::MangleLibs(tokens, *env->SysrootDir,
  161. *env->SysLibs);
  162. }
  163. return cmPkgConfigResolver::MangleLibs(tokens, *env->SysLibs);
  164. }
  165. if (env->SysrootDir) {
  166. return cmPkgConfigResolver::MangleLibs(
  167. tokens, *env->SysrootDir, std::vector<std::string>{ "/usr/lib" });
  168. }
  169. return cmPkgConfigResolver::MangleLibs(
  170. tokens, std::vector<std::string>{ "/usr/lib" });
  171. }
  172. void cmPkgConfigResolver::ReplaceSep(std::string& list)
  173. {
  174. #ifndef _WIN32
  175. std::replace(list.begin(), list.end(), ':', ';');
  176. #else
  177. static_cast<void>(list); // Unused parameter
  178. #endif
  179. }
  180. cm::optional<cmPkgConfigResult> cmPkgConfigResolver::ResolveStrict(
  181. std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env)
  182. {
  183. cm::optional<cmPkgConfigResult> result;
  184. cmPkgConfigResult config;
  185. auto& keys = config.Keywords;
  186. if (env.SysrootDir) {
  187. config.Variables["pc_sysrootdir"] = *env.SysrootDir;
  188. } else {
  189. config.Variables["pc_sysrootdir"] = "/";
  190. }
  191. if (env.TopBuildDir) {
  192. config.Variables["pc_top_builddir"] = *env.TopBuildDir;
  193. }
  194. config.env = &env;
  195. for (auto const& entry : entries) {
  196. std::string key(entry.Key);
  197. if (entry.IsVariable) {
  198. if (config.Variables.find(key) != config.Variables.end()) {
  199. return result;
  200. }
  201. auto var = HandleVariableStrict(entry, config.Variables);
  202. if (!var) {
  203. return result;
  204. }
  205. config.Variables[key] = *var;
  206. } else {
  207. if (key == "Cflags" && keys.find("CFlags") != keys.end()) {
  208. return result;
  209. }
  210. if (key == "CFlags" && keys.find("Cflags") != keys.end()) {
  211. return result;
  212. }
  213. if (key == "Cflags.private" &&
  214. keys.find("CFlags.private") != keys.end()) {
  215. return result;
  216. }
  217. if (key == "CFlags.private" &&
  218. keys.find("Cflags.private") != keys.end()) {
  219. return result;
  220. }
  221. if (keys.find(key) != keys.end()) {
  222. return result;
  223. }
  224. keys[key] = HandleKeyword(entry, config.Variables);
  225. }
  226. }
  227. if (keys.find("Name") == keys.end() ||
  228. keys.find("Description") == keys.end() ||
  229. keys.find("Version") == keys.end()) {
  230. return result;
  231. }
  232. result = std::move(config);
  233. return result;
  234. }
  235. cm::optional<cmPkgConfigResult> cmPkgConfigResolver::ResolvePermissive(
  236. std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env)
  237. {
  238. cm::optional<cmPkgConfigResult> result;
  239. cmPkgConfigResult config = ResolveBestEffort(entries, std::move(env));
  240. auto const& keys = config.Keywords;
  241. if (keys.find("Name") == keys.end() ||
  242. keys.find("Description") == keys.end() ||
  243. keys.find("Version") == keys.end()) {
  244. return result;
  245. }
  246. result = std::move(config);
  247. return result;
  248. }
  249. cmPkgConfigResult cmPkgConfigResolver::ResolveBestEffort(
  250. std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env)
  251. {
  252. cmPkgConfigResult result;
  253. if (env.SysrootDir) {
  254. result.Variables["pc_sysrootdir"] = *env.SysrootDir;
  255. } else {
  256. result.Variables["pc_sysrootdir"] = "/";
  257. }
  258. if (env.TopBuildDir) {
  259. result.Variables["pc_top_builddir"] = *env.TopBuildDir;
  260. }
  261. result.env = &env;
  262. for (auto const& entry : entries) {
  263. std::string key(entry.Key);
  264. if (entry.IsVariable) {
  265. result.Variables[key] =
  266. HandleVariablePermissive(entry, result.Variables);
  267. } else {
  268. result.Keywords[key] += HandleKeyword(entry, result.Variables);
  269. }
  270. }
  271. return result;
  272. }
  273. std::string cmPkgConfigResolver::HandleVariablePermissive(
  274. cmPkgConfigEntry const& entry,
  275. std::unordered_map<std::string, std::string> const& variables)
  276. {
  277. std::string result;
  278. for (auto const& segment : entry.Val) {
  279. if (!segment.IsVariable) {
  280. result += segment.Data;
  281. } else if (entry.Key != segment.Data) {
  282. auto it = variables.find(std::string{ segment.Data });
  283. if (it != variables.end()) {
  284. result += it->second;
  285. }
  286. }
  287. }
  288. TrimBack(result);
  289. return result;
  290. }
  291. cm::optional<std::string> cmPkgConfigResolver::HandleVariableStrict(
  292. cmPkgConfigEntry const& entry,
  293. std::unordered_map<std::string, std::string> const& variables)
  294. {
  295. cm::optional<std::string> result;
  296. std::string value;
  297. for (auto const& segment : entry.Val) {
  298. if (!segment.IsVariable) {
  299. value += segment.Data;
  300. } else if (entry.Key == segment.Data) {
  301. return result;
  302. } else {
  303. auto it = variables.find(std::string{ segment.Data });
  304. if (it != variables.end()) {
  305. value += it->second;
  306. } else {
  307. return result;
  308. }
  309. }
  310. }
  311. TrimBack(value);
  312. result = std::move(value);
  313. return result;
  314. }
  315. std::string cmPkgConfigResolver::HandleKeyword(
  316. cmPkgConfigEntry const& entry,
  317. std::unordered_map<std::string, std::string> const& variables)
  318. {
  319. std::string result;
  320. for (auto const& segment : entry.Val) {
  321. if (!segment.IsVariable) {
  322. result += segment.Data;
  323. } else {
  324. auto it = variables.find(std::string{ segment.Data });
  325. if (it != variables.end()) {
  326. result += it->second;
  327. }
  328. }
  329. }
  330. TrimBack(result);
  331. return result;
  332. }
  333. std::vector<cm::string_view> cmPkgConfigResolver::TokenizeFlags(
  334. std::string const& flagline)
  335. {
  336. std::vector<cm::string_view> result;
  337. auto it = flagline.begin();
  338. while (it != flagline.end() && std::isspace(*it)) {
  339. ++it;
  340. }
  341. while (it != flagline.end()) {
  342. char const* start = &(*it);
  343. std::size_t len = 0;
  344. for (; it != flagline.end() && !std::isspace(*it); ++it) {
  345. ++len;
  346. }
  347. for (; it != flagline.end() && std::isspace(*it); ++it) {
  348. ++len;
  349. }
  350. result.emplace_back(start, len);
  351. }
  352. return result;
  353. }
  354. cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags(
  355. std::vector<cm::string_view> const& flags)
  356. {
  357. cmPkgConfigCflagsResult result;
  358. for (auto flag : flags) {
  359. if (flag.rfind("-I", 0) == 0) {
  360. result.Includes.emplace_back(AppendAndTrim(result.Flagline, flag));
  361. } else {
  362. result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
  363. }
  364. }
  365. return result;
  366. }
  367. cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags(
  368. std::vector<cm::string_view> const& flags, std::string const& sysroot)
  369. {
  370. cmPkgConfigCflagsResult result;
  371. for (auto flag : flags) {
  372. if (flag.rfind("-I", 0) == 0) {
  373. std::string reroot = Reroot(flag, "-I", sysroot);
  374. result.Includes.emplace_back(AppendAndTrim(result.Flagline, reroot));
  375. } else {
  376. result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
  377. }
  378. }
  379. return result;
  380. }
  381. cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags(
  382. std::vector<cm::string_view> const& flags,
  383. std::vector<std::string> const& syspaths)
  384. {
  385. cmPkgConfigCflagsResult result;
  386. for (auto flag : flags) {
  387. if (flag.rfind("-I", 0) == 0) {
  388. cm::string_view noprefix{ flag.data() + 2, flag.size() - 2 };
  389. if (std::all_of(syspaths.begin(), syspaths.end(),
  390. [&](std::string const& path) {
  391. return noprefix.rfind(path, 0) == noprefix.npos;
  392. })) {
  393. result.Includes.emplace_back(AppendAndTrim(result.Flagline, flag));
  394. }
  395. } else {
  396. result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
  397. }
  398. }
  399. return result;
  400. }
  401. cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags(
  402. std::vector<cm::string_view> const& flags, std::string const& sysroot,
  403. std::vector<std::string> const& syspaths)
  404. {
  405. cmPkgConfigCflagsResult result;
  406. for (auto flag : flags) {
  407. if (flag.rfind("-I", 0) == 0) {
  408. std::string reroot = Reroot(flag, "-I", sysroot);
  409. cm::string_view noprefix{ reroot.data() + 2, reroot.size() - 2 };
  410. if (std::all_of(syspaths.begin(), syspaths.end(),
  411. [&](std::string const& path) {
  412. return noprefix.rfind(path, 0) == noprefix.npos;
  413. })) {
  414. result.Includes.emplace_back(AppendAndTrim(result.Flagline, reroot));
  415. }
  416. } else {
  417. result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
  418. }
  419. }
  420. return result;
  421. }
  422. cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs(
  423. std::vector<cm::string_view> const& flags)
  424. {
  425. cmPkgConfigLibsResult result;
  426. for (auto flag : flags) {
  427. if (flag.rfind("-L", 0) == 0) {
  428. result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, flag));
  429. } else if (flag.rfind("-l", 0) == 0) {
  430. result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag));
  431. } else {
  432. result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
  433. }
  434. }
  435. return result;
  436. }
  437. cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs(
  438. std::vector<cm::string_view> const& flags, std::string const& sysroot)
  439. {
  440. cmPkgConfigLibsResult result;
  441. for (auto flag : flags) {
  442. if (flag.rfind("-L", 0) == 0) {
  443. std::string reroot = Reroot(flag, "-L", sysroot);
  444. result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, reroot));
  445. } else if (flag.rfind("-l", 0) == 0) {
  446. result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag));
  447. } else {
  448. result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
  449. }
  450. }
  451. return result;
  452. }
  453. cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs(
  454. std::vector<cm::string_view> const& flags,
  455. std::vector<std::string> const& syspaths)
  456. {
  457. cmPkgConfigLibsResult result;
  458. for (auto flag : flags) {
  459. if (flag.rfind("-L", 0) == 0) {
  460. cm::string_view noprefix{ flag.data() + 2, flag.size() - 2 };
  461. if (std::all_of(syspaths.begin(), syspaths.end(),
  462. [&](std::string const& path) {
  463. return noprefix.rfind(path, 0) == noprefix.npos;
  464. })) {
  465. result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, flag));
  466. }
  467. } else if (flag.rfind("-l", 0) == 0) {
  468. result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag));
  469. } else {
  470. result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
  471. }
  472. }
  473. return result;
  474. }
  475. cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs(
  476. std::vector<cm::string_view> const& flags, std::string const& sysroot,
  477. std::vector<std::string> const& syspaths)
  478. {
  479. cmPkgConfigLibsResult result;
  480. for (auto flag : flags) {
  481. if (flag.rfind("-L", 0) == 0) {
  482. std::string reroot = Reroot(flag, "-L", sysroot);
  483. cm::string_view noprefix{ reroot.data() + 2, reroot.size() - 2 };
  484. if (std::all_of(syspaths.begin(), syspaths.end(),
  485. [&](std::string const& path) {
  486. return noprefix.rfind(path, 0) == noprefix.npos;
  487. })) {
  488. result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, reroot));
  489. }
  490. } else if (flag.rfind("-l", 0) == 0) {
  491. result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag));
  492. } else {
  493. result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
  494. }
  495. }
  496. return result;
  497. }
  498. std::string cmPkgConfigResolver::Reroot(cm::string_view flag,
  499. cm::string_view prefix,
  500. std::string const& sysroot)
  501. {
  502. std::string result = std::string{ prefix };
  503. result += sysroot;
  504. result += cm::string_view{ flag.data() + prefix.length(),
  505. flag.size() - prefix.length() };
  506. return result;
  507. }
  508. cmPkgConfigVersionReq cmPkgConfigResolver::ParseVersion(
  509. std::string::const_iterator& cur, std::string::const_iterator end)
  510. {
  511. cmPkgConfigVersionReq result;
  512. if (*cur == '=') {
  513. result.Operation = result.EQ;
  514. ++cur;
  515. } else if (*cur == '>') {
  516. ++cur;
  517. if (cur == end) {
  518. result.Operation = result.GT;
  519. return result;
  520. }
  521. if (*cur == '=') {
  522. result.Operation = result.GT_EQ;
  523. ++cur;
  524. } else {
  525. result.Operation = result.GT;
  526. }
  527. } else if (*cur == '<') {
  528. ++cur;
  529. if (cur == end) {
  530. result.Operation = result.LT;
  531. return result;
  532. }
  533. if (*cur == '=') {
  534. result.Operation = result.LT_EQ;
  535. ++cur;
  536. } else {
  537. result.Operation = result.LT;
  538. }
  539. } else if (*cur == '!') {
  540. ++cur;
  541. if (cur == end) {
  542. result.Operation = result.ANY;
  543. return result;
  544. }
  545. if (*cur == '=') {
  546. result.Operation = result.NEQ;
  547. ++cur;
  548. } else {
  549. result.Operation = result.ANY;
  550. }
  551. }
  552. for (;; ++cur) {
  553. if (cur == end) {
  554. return result;
  555. }
  556. if (!std::isspace(*cur)) {
  557. break;
  558. }
  559. }
  560. for (; cur != end && !std::isspace(*cur) && *cur != ','; ++cur) {
  561. result.Version += *cur;
  562. }
  563. return result;
  564. }
  565. std::vector<cmPkgConfigDependency> cmPkgConfigResolver::ParseDependencies(
  566. std::string const& deps)
  567. {
  568. std::vector<cmPkgConfigDependency> result;
  569. auto cur = deps.begin();
  570. auto end = deps.end();
  571. while (cur != end) {
  572. while ((std::isspace(*cur) || *cur == ',')) {
  573. if (++cur == end) {
  574. return result;
  575. }
  576. }
  577. result.emplace_back();
  578. auto& dep = result.back();
  579. while (!std::isspace(*cur) && *cur != ',') {
  580. dep.Name += *cur;
  581. if (++cur == end) {
  582. return result;
  583. }
  584. }
  585. auto in_operator = [&]() -> bool {
  586. for (;; ++cur) {
  587. if (cur == end) {
  588. return false;
  589. }
  590. if (*cur == '>' || *cur == '=' || *cur == '<' || *cur == '!') {
  591. return true;
  592. }
  593. if (!std::isspace(*cur)) {
  594. return false;
  595. }
  596. }
  597. };
  598. if (!in_operator()) {
  599. continue;
  600. }
  601. dep.VerReq = ParseVersion(cur, end);
  602. }
  603. return result;
  604. }
  605. bool cmPkgConfigResolver::CheckVersion(cmPkgConfigVersionReq const& desired,
  606. std::string const& provided)
  607. {
  608. if (desired.Operation == cmPkgConfigVersionReq::ANY) {
  609. return true;
  610. }
  611. // https://blog.jasonantman.com/2014/07/how-yum-and-rpm-compare-versions/
  612. auto check_with_op = [&](int comp) -> bool {
  613. switch (desired.Operation) {
  614. case cmPkgConfigVersionReq::EQ:
  615. return comp == 0;
  616. case cmPkgConfigVersionReq::NEQ:
  617. return comp != 0;
  618. case cmPkgConfigVersionReq::GT:
  619. return comp < 0;
  620. case cmPkgConfigVersionReq::GT_EQ:
  621. return comp <= 0;
  622. case cmPkgConfigVersionReq::LT:
  623. return comp > 0;
  624. case cmPkgConfigVersionReq::LT_EQ:
  625. return comp >= 0;
  626. default:
  627. return true;
  628. }
  629. };
  630. if (desired.Version == provided) {
  631. return check_with_op(0);
  632. }
  633. auto a_cur = desired.Version.begin();
  634. auto a_end = desired.Version.end();
  635. auto b_cur = provided.begin();
  636. auto b_end = provided.end();
  637. while (a_cur != a_end && b_cur != b_end) {
  638. while (a_cur != a_end && !std::isalnum(*a_cur) && *a_cur != '~') {
  639. ++a_cur;
  640. }
  641. while (b_cur != b_end && !std::isalnum(*b_cur) && *b_cur != '~') {
  642. ++b_cur;
  643. }
  644. if (a_cur == a_end || b_cur == b_end) {
  645. break;
  646. }
  647. if (*a_cur == '~' || *b_cur == '~') {
  648. if (*a_cur != '~') {
  649. return check_with_op(1);
  650. }
  651. if (*b_cur != '~') {
  652. return check_with_op(-1);
  653. }
  654. ++a_cur;
  655. ++b_cur;
  656. continue;
  657. }
  658. auto a_seg = a_cur;
  659. auto b_seg = b_cur;
  660. bool is_num;
  661. if (std::isdigit(*a_cur)) {
  662. is_num = true;
  663. while (a_cur != a_end && std::isdigit(*a_cur)) {
  664. ++a_cur;
  665. }
  666. while (b_cur != b_end && std::isdigit(*b_cur)) {
  667. ++b_cur;
  668. }
  669. } else {
  670. is_num = false;
  671. while (a_cur != a_end && std::isalpha(*a_cur)) {
  672. ++a_cur;
  673. }
  674. while (b_cur != b_end && std::isalpha(*b_cur)) {
  675. ++b_cur;
  676. }
  677. }
  678. auto a_len = std::distance(a_seg, a_cur);
  679. auto b_len = std::distance(b_seg, b_cur);
  680. if (!b_len) {
  681. return check_with_op(is_num ? 1 : -1);
  682. }
  683. if (is_num) {
  684. while (a_seg != a_cur && *a_seg == '0') {
  685. ++a_seg;
  686. }
  687. while (b_seg != b_cur && *b_seg == '0') {
  688. ++b_seg;
  689. }
  690. a_len = std::distance(a_seg, a_cur);
  691. b_len = std::distance(b_seg, b_cur);
  692. if (a_len != b_len) {
  693. return check_with_op(a_len > b_len ? 1 : -1);
  694. }
  695. auto cmp = std::memcmp(&*a_seg, &*b_seg, a_len);
  696. if (cmp) {
  697. return check_with_op(cmp);
  698. }
  699. } else {
  700. auto cmp = std::memcmp(&*a_seg, &*b_seg, std::min(a_len, b_len));
  701. if (cmp) {
  702. return check_with_op(cmp);
  703. }
  704. if (a_len != b_len) {
  705. return check_with_op(a_len > b_len ? 1 : -1);
  706. }
  707. }
  708. }
  709. if (a_cur == a_end) {
  710. if (b_cur == b_end) {
  711. return check_with_op(0);
  712. }
  713. return check_with_op(-1);
  714. }
  715. return check_with_op(1);
  716. }
  717. cmPkgConfigVersionReq cmPkgConfigResolver::ParseVersion(
  718. std::string const& version)
  719. {
  720. cmPkgConfigVersionReq result;
  721. auto cur = version.begin();
  722. auto end = version.end();
  723. if (cur == end) {
  724. result.Operation = cmPkgConfigVersionReq::EQ;
  725. return result;
  726. }
  727. result = ParseVersion(cur, end);
  728. cur = version.begin();
  729. if (*cur != '=' && *cur != '!' && *cur != '<' && *cur != '>') {
  730. result.Operation = cmPkgConfigVersionReq::EQ;
  731. }
  732. return result;
  733. }