cmCMakePathCommand.cxx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCMakePathCommand.h"
  4. #include <algorithm>
  5. #include <functional>
  6. #include <iomanip>
  7. #include <map>
  8. #include <sstream>
  9. #include <string>
  10. #include <utility>
  11. #include <vector>
  12. #include <cm/string_view>
  13. #include <cmext/string_view>
  14. #include "cmArgumentParser.h"
  15. #include "cmCMakePath.h"
  16. #include "cmExecutionStatus.h"
  17. #include "cmMakefile.h"
  18. #include "cmProperty.h"
  19. #include "cmRange.h"
  20. #include "cmStringAlgorithms.h"
  21. #include "cmSubcommandTable.h"
  22. #include "cmSystemTools.h"
  23. namespace {
  24. // Helper classes for argument parsing
  25. template <typename Result>
  26. class CMakePathArgumentParser : public cmArgumentParser<Result>
  27. {
  28. public:
  29. CMakePathArgumentParser()
  30. : cmArgumentParser<Result>()
  31. {
  32. }
  33. template <typename T>
  34. CMakePathArgumentParser& Bind(cm::static_string_view name, T Result::*member)
  35. {
  36. this->cmArgumentParser<Result>::Bind(name, member);
  37. return *this;
  38. }
  39. template <int Advance = 2>
  40. Result Parse(std::vector<std::string> const& args,
  41. std::vector<std::string>* keywordsMissingValue = nullptr,
  42. std::vector<std::string>* parsedKeywords = nullptr) const
  43. {
  44. this->Inputs.clear();
  45. return this->cmArgumentParser<Result>::Parse(
  46. cmMakeRange(args).advance(Advance), &this->Inputs, keywordsMissingValue,
  47. parsedKeywords);
  48. }
  49. const std::vector<std::string>& GetInputs() const { return this->Inputs; }
  50. protected:
  51. mutable std::vector<std::string> Inputs;
  52. };
  53. // OUTPUT_VARIABLE is expected
  54. template <typename Result>
  55. class ArgumentParserWithOutputVariable : public CMakePathArgumentParser<Result>
  56. {
  57. public:
  58. ArgumentParserWithOutputVariable()
  59. : CMakePathArgumentParser<Result>()
  60. {
  61. this->Bind("OUTPUT_VARIABLE"_s, &Result::Output);
  62. }
  63. template <typename T>
  64. ArgumentParserWithOutputVariable& Bind(cm::static_string_view name,
  65. T Result::*member)
  66. {
  67. this->cmArgumentParser<Result>::Bind(name, member);
  68. return *this;
  69. }
  70. template <int Advance = 2>
  71. Result Parse(std::vector<std::string> const& args) const
  72. {
  73. this->KeywordsMissingValue.clear();
  74. this->ParsedKeywords.clear();
  75. return this->CMakePathArgumentParser<Result>::template Parse<Advance>(
  76. args, &this->KeywordsMissingValue, &this->ParsedKeywords);
  77. }
  78. const std::vector<std::string>& GetKeywordsMissingValue() const
  79. {
  80. return this->KeywordsMissingValue;
  81. }
  82. const std::vector<std::string>& GetParsedKeywords() const
  83. {
  84. return this->ParsedKeywords;
  85. }
  86. bool checkOutputVariable(const Result& arguments,
  87. cmExecutionStatus& status) const
  88. {
  89. if (std::find(this->GetKeywordsMissingValue().begin(),
  90. this->GetKeywordsMissingValue().end(),
  91. "OUTPUT_VARIABLE"_s) !=
  92. this->GetKeywordsMissingValue().end()) {
  93. status.SetError("OUTPUT_VARIABLE requires an argument.");
  94. return false;
  95. }
  96. if (std::find(this->GetParsedKeywords().begin(),
  97. this->GetParsedKeywords().end(),
  98. "OUTPUT_VARIABLE"_s) != this->GetParsedKeywords().end() &&
  99. arguments.Output.empty()) {
  100. status.SetError("Invalid name for output variable.");
  101. return false;
  102. }
  103. return true;
  104. }
  105. private:
  106. mutable std::vector<std::string> KeywordsMissingValue;
  107. mutable std::vector<std::string> ParsedKeywords;
  108. };
  109. struct OutputVariable
  110. {
  111. std::string Output;
  112. };
  113. // Usable when OUTPUT_VARIABLE is the only option
  114. class OutputVariableParser
  115. : public ArgumentParserWithOutputVariable<OutputVariable>
  116. {
  117. };
  118. struct NormalizeOption
  119. {
  120. bool Normalize = false;
  121. };
  122. // Usable when NORMALIZE is the only option
  123. class NormalizeParser : public CMakePathArgumentParser<NormalizeOption>
  124. {
  125. public:
  126. NormalizeParser() { this->Bind("NORMALIZE"_s, &NormalizeOption::Normalize); }
  127. };
  128. // retrieve value of input path from specified variable
  129. bool getInputPath(const std::string& arg, cmExecutionStatus& status,
  130. std::string& path)
  131. {
  132. cmProp def = status.GetMakefile().GetDefinition(arg);
  133. if (!def) {
  134. status.SetError("undefined variable for input path.");
  135. return false;
  136. }
  137. path = *def;
  138. return true;
  139. }
  140. bool HandleGetCommand(std::vector<std::string> const& args,
  141. cmExecutionStatus& status)
  142. {
  143. static std::map<cm::string_view,
  144. std::function<cmCMakePath(const cmCMakePath&, bool)>> const
  145. actions{ { "ROOT_NAME"_s,
  146. [](const cmCMakePath& path, bool) -> cmCMakePath {
  147. return path.GetRootName();
  148. } },
  149. { "ROOT_DIRECTORY"_s,
  150. [](const cmCMakePath& path, bool) -> cmCMakePath {
  151. return path.GetRootDirectory();
  152. } },
  153. { "ROOT_PATH"_s,
  154. [](const cmCMakePath& path, bool) -> cmCMakePath {
  155. return path.GetRootPath();
  156. } },
  157. { "FILENAME"_s,
  158. [](const cmCMakePath& path, bool) -> cmCMakePath {
  159. return path.GetFileName();
  160. } },
  161. { "EXTENSION"_s,
  162. [](const cmCMakePath& path, bool last_only) -> cmCMakePath {
  163. if (last_only) {
  164. return path.GetExtension();
  165. }
  166. return path.GetWideExtension();
  167. } },
  168. { "STEM"_s,
  169. [](const cmCMakePath& path, bool last_only) -> cmCMakePath {
  170. if (last_only) {
  171. return path.GetStem();
  172. }
  173. return path.GetNarrowStem();
  174. } },
  175. { "RELATIVE_PART"_s,
  176. [](const cmCMakePath& path, bool) -> cmCMakePath {
  177. return path.GetRelativePath();
  178. } },
  179. { "PARENT_PATH"_s,
  180. [](const cmCMakePath& path, bool) -> cmCMakePath {
  181. return path.GetParentPath();
  182. } } };
  183. if (args.size() < 4) {
  184. status.SetError("GET must be called with at least three arguments.");
  185. return false;
  186. }
  187. const auto& action = args[2];
  188. if (actions.find(action) == actions.end()) {
  189. status.SetError(
  190. cmStrCat("GET called with an unknown action: ", action, "."));
  191. return false;
  192. }
  193. struct Arguments
  194. {
  195. bool LastOnly = false;
  196. };
  197. CMakePathArgumentParser<Arguments> parser;
  198. if ((action == "EXTENSION"_s || action == "STEM"_s)) {
  199. parser.Bind("LAST_ONLY"_s, &Arguments::LastOnly);
  200. }
  201. Arguments const arguments = parser.Parse<3>(args);
  202. if (parser.GetInputs().size() != 1) {
  203. status.SetError("GET called with unexpected arguments.");
  204. return false;
  205. }
  206. if (parser.GetInputs().front().empty()) {
  207. status.SetError("Invalid name for output variable.");
  208. return false;
  209. }
  210. std::string path;
  211. if (!getInputPath(args[1], status, path)) {
  212. return false;
  213. }
  214. auto result = actions.at(action)(path, arguments.LastOnly);
  215. status.GetMakefile().AddDefinition(parser.GetInputs().front(),
  216. result.String());
  217. return true;
  218. }
  219. bool HandleSetCommand(std::vector<std::string> const& args,
  220. cmExecutionStatus& status)
  221. {
  222. if (args.size() < 3 || args.size() > 4) {
  223. status.SetError("SET must be called with two or three arguments.");
  224. return false;
  225. }
  226. if (args[1].empty()) {
  227. status.SetError("Invalid name for path variable.");
  228. return false;
  229. }
  230. static NormalizeParser const parser;
  231. const auto arguments = parser.Parse(args);
  232. if (parser.GetInputs().size() != 1) {
  233. status.SetError("SET called with unexpected arguments.");
  234. return false;
  235. }
  236. auto path =
  237. cmCMakePath(parser.GetInputs().front(), cmCMakePath::native_format);
  238. if (arguments.Normalize) {
  239. path = path.Normal();
  240. }
  241. status.GetMakefile().AddDefinition(args[1], path.GenericString());
  242. return true;
  243. }
  244. bool HandleAppendCommand(std::vector<std::string> const& args,
  245. cmExecutionStatus& status)
  246. {
  247. if (args[1].empty()) {
  248. status.SetError("Invalid name for path variable.");
  249. return false;
  250. }
  251. static OutputVariableParser const parser{};
  252. const auto arguments = parser.Parse(args);
  253. if (!parser.checkOutputVariable(arguments, status)) {
  254. return false;
  255. }
  256. cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1]));
  257. for (const auto& input : parser.GetInputs()) {
  258. path /= input;
  259. }
  260. status.GetMakefile().AddDefinition(
  261. arguments.Output.empty() ? args[1] : arguments.Output, path.String());
  262. return true;
  263. }
  264. bool HandleAppendStringCommand(std::vector<std::string> const& args,
  265. cmExecutionStatus& status)
  266. {
  267. static OutputVariableParser const parser{};
  268. const auto arguments = parser.Parse(args);
  269. if (!parser.checkOutputVariable(arguments, status)) {
  270. return false;
  271. }
  272. std::string inputPath;
  273. if (!getInputPath(args[1], status, inputPath)) {
  274. return false;
  275. }
  276. cmCMakePath path(inputPath);
  277. for (const auto& input : parser.GetInputs()) {
  278. path += input;
  279. }
  280. status.GetMakefile().AddDefinition(
  281. arguments.Output.empty() ? args[1] : arguments.Output, path.String());
  282. return true;
  283. }
  284. bool HandleRemoveFilenameCommand(std::vector<std::string> const& args,
  285. cmExecutionStatus& status)
  286. {
  287. static OutputVariableParser const parser{};
  288. const auto arguments = parser.Parse(args);
  289. if (!parser.checkOutputVariable(arguments, status)) {
  290. return false;
  291. }
  292. if (!parser.GetInputs().empty()) {
  293. status.SetError("REMOVE_FILENAME called with unexpected arguments.");
  294. return false;
  295. }
  296. std::string inputPath;
  297. if (!getInputPath(args[1], status, inputPath)) {
  298. return false;
  299. }
  300. cmCMakePath path(inputPath);
  301. path.RemoveFileName();
  302. status.GetMakefile().AddDefinition(
  303. arguments.Output.empty() ? args[1] : arguments.Output, path.String());
  304. return true;
  305. }
  306. bool HandleReplaceFilenameCommand(std::vector<std::string> const& args,
  307. cmExecutionStatus& status)
  308. {
  309. static OutputVariableParser const parser{};
  310. const auto arguments = parser.Parse(args);
  311. if (!parser.checkOutputVariable(arguments, status)) {
  312. return false;
  313. }
  314. if (parser.GetInputs().size() > 1) {
  315. status.SetError("REPLACE_FILENAME called with unexpected arguments.");
  316. return false;
  317. }
  318. std::string inputPath;
  319. if (!getInputPath(args[1], status, inputPath)) {
  320. return false;
  321. }
  322. cmCMakePath path(inputPath);
  323. path.ReplaceFileName(
  324. parser.GetInputs().empty() ? "" : parser.GetInputs().front());
  325. status.GetMakefile().AddDefinition(
  326. arguments.Output.empty() ? args[1] : arguments.Output, path.String());
  327. return true;
  328. }
  329. bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
  330. cmExecutionStatus& status)
  331. {
  332. struct Arguments
  333. {
  334. std::string Output;
  335. bool LastOnly = false;
  336. };
  337. static auto const parser =
  338. ArgumentParserWithOutputVariable<Arguments>{}.Bind("LAST_ONLY"_s,
  339. &Arguments::LastOnly);
  340. Arguments const arguments = parser.Parse(args);
  341. if (!parser.checkOutputVariable(arguments, status)) {
  342. return false;
  343. }
  344. if (!parser.GetInputs().empty()) {
  345. status.SetError("REMOVE_EXTENSION called with unexpected arguments.");
  346. return false;
  347. }
  348. std::string inputPath;
  349. if (!getInputPath(args[1], status, inputPath)) {
  350. return false;
  351. }
  352. cmCMakePath path(inputPath);
  353. if (arguments.LastOnly) {
  354. path.RemoveExtension();
  355. } else {
  356. path.RemoveWideExtension();
  357. }
  358. status.GetMakefile().AddDefinition(
  359. arguments.Output.empty() ? args[1] : arguments.Output, path.String());
  360. return true;
  361. }
  362. bool HandleReplaceExtensionCommand(std::vector<std::string> const& args,
  363. cmExecutionStatus& status)
  364. {
  365. struct Arguments
  366. {
  367. std::string Output;
  368. bool LastOnly = false;
  369. };
  370. static auto const parser =
  371. ArgumentParserWithOutputVariable<Arguments>{}.Bind("LAST_ONLY"_s,
  372. &Arguments::LastOnly);
  373. Arguments const arguments = parser.Parse(args);
  374. if (!parser.checkOutputVariable(arguments, status)) {
  375. return false;
  376. }
  377. if (parser.GetInputs().size() > 1) {
  378. status.SetError("REPLACE_EXTENSION called with unexpected arguments.");
  379. return false;
  380. }
  381. std::string inputPath;
  382. if (!getInputPath(args[1], status, inputPath)) {
  383. return false;
  384. }
  385. cmCMakePath path(inputPath);
  386. cmCMakePath extension(
  387. parser.GetInputs().empty() ? "" : parser.GetInputs().front());
  388. if (arguments.LastOnly) {
  389. path.ReplaceExtension(extension);
  390. } else {
  391. path.ReplaceWideExtension(extension);
  392. }
  393. status.GetMakefile().AddDefinition(
  394. arguments.Output.empty() ? args[1] : arguments.Output, path.String());
  395. return true;
  396. }
  397. bool HandleNormalPathCommand(std::vector<std::string> const& args,
  398. cmExecutionStatus& status)
  399. {
  400. static OutputVariableParser const parser{};
  401. const auto arguments = parser.Parse(args);
  402. if (!parser.checkOutputVariable(arguments, status)) {
  403. return false;
  404. }
  405. if (!parser.GetInputs().empty()) {
  406. status.SetError("NORMAL_PATH called with unexpected arguments.");
  407. return false;
  408. }
  409. std::string inputPath;
  410. if (!getInputPath(args[1], status, inputPath)) {
  411. return false;
  412. }
  413. auto path = cmCMakePath(inputPath).Normal();
  414. status.GetMakefile().AddDefinition(
  415. arguments.Output.empty() ? args[1] : arguments.Output, path.String());
  416. return true;
  417. }
  418. bool HandleTransformPathCommand(
  419. std::vector<std::string> const& args, cmExecutionStatus& status,
  420. const std::function<cmCMakePath(const cmCMakePath&,
  421. const std::string& base)>& transform,
  422. bool normalizeOption = false)
  423. {
  424. struct Arguments
  425. {
  426. std::string Output;
  427. std::string BaseDirectory;
  428. bool Normalize = false;
  429. };
  430. auto parser = ArgumentParserWithOutputVariable<Arguments>{}.Bind(
  431. "BASE_DIRECTORY"_s, &Arguments::BaseDirectory);
  432. if (normalizeOption) {
  433. parser.Bind("NORMALIZE"_s, &Arguments::Normalize);
  434. }
  435. Arguments arguments = parser.Parse(args);
  436. if (!parser.checkOutputVariable(arguments, status)) {
  437. return false;
  438. }
  439. if (!parser.GetInputs().empty()) {
  440. status.SetError(cmStrCat(args[0], " called with unexpected arguments."));
  441. return false;
  442. }
  443. if (std::find(parser.GetKeywordsMissingValue().begin(),
  444. parser.GetKeywordsMissingValue().end(), "BASE_DIRECTORY"_s) !=
  445. parser.GetKeywordsMissingValue().end()) {
  446. status.SetError("BASE_DIRECTORY requires an argument.");
  447. return false;
  448. }
  449. if (std::find(parser.GetParsedKeywords().begin(),
  450. parser.GetParsedKeywords().end(),
  451. "BASE_DIRECTORY"_s) == parser.GetParsedKeywords().end()) {
  452. arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
  453. }
  454. std::string inputPath;
  455. if (!getInputPath(args[1], status, inputPath)) {
  456. return false;
  457. }
  458. auto path = transform(cmCMakePath(inputPath), arguments.BaseDirectory);
  459. if (arguments.Normalize) {
  460. path = path.Normal();
  461. }
  462. status.GetMakefile().AddDefinition(
  463. arguments.Output.empty() ? args[1] : arguments.Output, path.String());
  464. return true;
  465. }
  466. bool HandleRelativePathCommand(std::vector<std::string> const& args,
  467. cmExecutionStatus& status)
  468. {
  469. return HandleTransformPathCommand(
  470. args, status,
  471. [](const cmCMakePath& path, const std::string& base) -> cmCMakePath {
  472. return path.Relative(base);
  473. });
  474. }
  475. bool HandleAbsolutePathCommand(std::vector<std::string> const& args,
  476. cmExecutionStatus& status)
  477. {
  478. return HandleTransformPathCommand(
  479. args, status,
  480. [](const cmCMakePath& path, const std::string& base) -> cmCMakePath {
  481. return path.Absolute(base);
  482. },
  483. true);
  484. }
  485. bool HandleNativePathCommand(std::vector<std::string> const& args,
  486. cmExecutionStatus& status)
  487. {
  488. if (args.size() < 3 || args.size() > 4) {
  489. status.SetError("NATIVE_PATH must be called with two or three arguments.");
  490. return false;
  491. }
  492. static NormalizeParser const parser;
  493. const auto arguments = parser.Parse(args);
  494. if (parser.GetInputs().size() != 1) {
  495. status.SetError("NATIVE_PATH called with unexpected arguments.");
  496. return false;
  497. }
  498. if (parser.GetInputs().front().empty()) {
  499. status.SetError("Invalid name for output variable.");
  500. return false;
  501. }
  502. std::string inputPath;
  503. if (!getInputPath(args[1], status, inputPath)) {
  504. return false;
  505. }
  506. cmCMakePath path(inputPath);
  507. if (arguments.Normalize) {
  508. path = path.Normal();
  509. }
  510. status.GetMakefile().AddDefinition(parser.GetInputs().front(),
  511. path.NativeString());
  512. return true;
  513. }
  514. bool HandleConvertCommand(std::vector<std::string> const& args,
  515. cmExecutionStatus& status)
  516. {
  517. #if defined(_WIN32) && !defined(__CYGWIN__)
  518. const auto pathSep = ";"_s;
  519. #else
  520. const auto pathSep = ":"_s;
  521. #endif
  522. const auto cmakePath = "TO_CMAKE_PATH_LIST"_s;
  523. const auto nativePath = "TO_NATIVE_PATH_LIST"_s;
  524. if (args.size() < 4 || args.size() > 5) {
  525. status.SetError("CONVERT must be called with three or four arguments.");
  526. return false;
  527. }
  528. const auto& action = args[2];
  529. if (action != cmakePath && action != nativePath) {
  530. status.SetError(
  531. cmStrCat("CONVERT called with an unknown action: ", action, "."));
  532. return false;
  533. }
  534. if (args[3].empty()) {
  535. status.SetError("Invalid name for output variable.");
  536. return false;
  537. }
  538. static NormalizeParser const parser;
  539. const auto arguments = parser.Parse<4>(args);
  540. if (!parser.GetInputs().empty()) {
  541. status.SetError("CONVERT called with unexpected arguments.");
  542. return false;
  543. }
  544. std::vector<std::string> paths;
  545. if (action == cmakePath) {
  546. paths = cmSystemTools::SplitString(args[1], pathSep.front());
  547. } else {
  548. cmExpandList(args[1], paths);
  549. }
  550. for (auto& path : paths) {
  551. auto p = cmCMakePath(path,
  552. action == cmakePath ? cmCMakePath::native_format
  553. : cmCMakePath::generic_format);
  554. if (arguments.Normalize) {
  555. p = p.Normal();
  556. }
  557. if (action == cmakePath) {
  558. path = p.GenericString();
  559. } else {
  560. path = p.NativeString();
  561. }
  562. }
  563. auto value = cmJoin(paths, action == cmakePath ? ";"_s : pathSep);
  564. status.GetMakefile().AddDefinition(args[3], value);
  565. return true;
  566. }
  567. bool HandleCompareCommand(std::vector<std::string> const& args,
  568. cmExecutionStatus& status)
  569. {
  570. if (args.size() != 5) {
  571. status.SetError("COMPARE must be called with four arguments.");
  572. return false;
  573. }
  574. static std::map<cm::string_view,
  575. std::function<bool(const cmCMakePath&,
  576. const cmCMakePath&)>> const operators{
  577. { "EQUAL"_s,
  578. [](const cmCMakePath& path1, const cmCMakePath& path2) -> bool {
  579. return path1 == path2;
  580. } },
  581. { "NOT_EQUAL"_s,
  582. [](const cmCMakePath& path1, const cmCMakePath& path2) -> bool {
  583. return path1 != path2;
  584. } }
  585. };
  586. const auto op = operators.find(args[2]);
  587. if (op == operators.end()) {
  588. status.SetError(cmStrCat(
  589. "COMPARE called with an unknown comparison operator: ", args[2], "."));
  590. return false;
  591. }
  592. if (args[4].empty()) {
  593. status.SetError("Invalid name for output variable.");
  594. return false;
  595. }
  596. cmCMakePath path1(args[1]);
  597. cmCMakePath path2(args[3]);
  598. auto result = op->second(path1, path2);
  599. status.GetMakefile().AddDefinitionBool(args[4], result);
  600. return true;
  601. }
  602. bool HandleHasItemCommand(
  603. std::vector<std::string> const& args, cmExecutionStatus& status,
  604. const std::function<bool(const cmCMakePath&)>& has_item)
  605. {
  606. if (args.size() != 3) {
  607. status.SetError(
  608. cmStrCat(args.front(), " must be called with two arguments."));
  609. return false;
  610. }
  611. std::string inputPath;
  612. if (!getInputPath(args[1], status, inputPath)) {
  613. return false;
  614. }
  615. if (args[2].empty()) {
  616. status.SetError("Invalid name for output variable.");
  617. return false;
  618. }
  619. cmCMakePath path(inputPath);
  620. auto result = has_item(path);
  621. status.GetMakefile().AddDefinitionBool(args[2], result);
  622. return true;
  623. }
  624. bool HandleHasRootNameCommand(std::vector<std::string> const& args,
  625. cmExecutionStatus& status)
  626. {
  627. return HandleHasItemCommand(
  628. args, status,
  629. [](const cmCMakePath& path) -> bool { return path.HasRootName(); });
  630. }
  631. bool HandleHasRootDirectoryCommand(std::vector<std::string> const& args,
  632. cmExecutionStatus& status)
  633. {
  634. return HandleHasItemCommand(
  635. args, status,
  636. [](const cmCMakePath& path) -> bool { return path.HasRootDirectory(); });
  637. }
  638. bool HandleHasRootPathCommand(std::vector<std::string> const& args,
  639. cmExecutionStatus& status)
  640. {
  641. return HandleHasItemCommand(
  642. args, status,
  643. [](const cmCMakePath& path) -> bool { return path.HasRootPath(); });
  644. }
  645. bool HandleHasFilenameCommand(std::vector<std::string> const& args,
  646. cmExecutionStatus& status)
  647. {
  648. return HandleHasItemCommand(
  649. args, status,
  650. [](const cmCMakePath& path) -> bool { return path.HasFileName(); });
  651. }
  652. bool HandleHasExtensionCommand(std::vector<std::string> const& args,
  653. cmExecutionStatus& status)
  654. {
  655. return HandleHasItemCommand(
  656. args, status,
  657. [](const cmCMakePath& path) -> bool { return path.HasExtension(); });
  658. }
  659. bool HandleHasStemCommand(std::vector<std::string> const& args,
  660. cmExecutionStatus& status)
  661. {
  662. return HandleHasItemCommand(
  663. args, status,
  664. [](const cmCMakePath& path) -> bool { return path.HasStem(); });
  665. }
  666. bool HandleHasRelativePartCommand(std::vector<std::string> const& args,
  667. cmExecutionStatus& status)
  668. {
  669. return HandleHasItemCommand(
  670. args, status,
  671. [](const cmCMakePath& path) -> bool { return path.HasRelativePath(); });
  672. }
  673. bool HandleHasParentPathCommand(std::vector<std::string> const& args,
  674. cmExecutionStatus& status)
  675. {
  676. return HandleHasItemCommand(
  677. args, status,
  678. [](const cmCMakePath& path) -> bool { return path.HasParentPath(); });
  679. }
  680. bool HandleIsAbsoluteCommand(std::vector<std::string> const& args,
  681. cmExecutionStatus& status)
  682. {
  683. if (args.size() != 3) {
  684. status.SetError("IS_ABSOLUTE must be called with two arguments.");
  685. return false;
  686. }
  687. std::string inputPath;
  688. if (!getInputPath(args[1], status, inputPath)) {
  689. return false;
  690. }
  691. if (args[2].empty()) {
  692. status.SetError("Invalid name for output variable.");
  693. return false;
  694. }
  695. bool isAbsolute = cmCMakePath(inputPath).IsAbsolute();
  696. status.GetMakefile().AddDefinitionBool(args[2], isAbsolute);
  697. return true;
  698. }
  699. bool HandleIsRelativeCommand(std::vector<std::string> const& args,
  700. cmExecutionStatus& status)
  701. {
  702. if (args.size() != 3) {
  703. status.SetError("IS_RELATIVE must be called with two arguments.");
  704. return false;
  705. }
  706. std::string inputPath;
  707. if (!getInputPath(args[1], status, inputPath)) {
  708. return false;
  709. }
  710. if (args[2].empty()) {
  711. status.SetError("Invalid name for output variable.");
  712. return false;
  713. }
  714. bool isRelative = cmCMakePath(inputPath).IsRelative();
  715. status.GetMakefile().AddDefinitionBool(args[2], isRelative);
  716. return true;
  717. }
  718. bool HandleIsPrefixCommand(std::vector<std::string> const& args,
  719. cmExecutionStatus& status)
  720. {
  721. if (args.size() < 4 || args.size() > 5) {
  722. status.SetError("IS_PREFIX must be called with three or four arguments.");
  723. return false;
  724. }
  725. static NormalizeParser const parser;
  726. const auto arguments = parser.Parse(args);
  727. if (parser.GetInputs().size() != 2) {
  728. status.SetError("IS_PREFIX called with unexpected arguments.");
  729. return false;
  730. }
  731. std::string inputPath;
  732. if (!getInputPath(args[1], status, inputPath)) {
  733. return false;
  734. }
  735. const auto& input = parser.GetInputs().front();
  736. const auto& output = parser.GetInputs().back();
  737. if (output.empty()) {
  738. status.SetError("Invalid name for output variable.");
  739. return false;
  740. }
  741. bool isPrefix;
  742. if (arguments.Normalize) {
  743. isPrefix =
  744. cmCMakePath(inputPath).Normal().IsPrefix(cmCMakePath(input).Normal());
  745. } else {
  746. isPrefix = cmCMakePath(inputPath).IsPrefix(input);
  747. }
  748. status.GetMakefile().AddDefinitionBool(output, isPrefix);
  749. return true;
  750. }
  751. bool HandleHashCommand(std::vector<std::string> const& args,
  752. cmExecutionStatus& status)
  753. {
  754. if (args.size() != 3) {
  755. status.SetError("HASH must be called with two arguments.");
  756. return false;
  757. }
  758. std::string inputPath;
  759. if (!getInputPath(args[1], status, inputPath)) {
  760. return false;
  761. }
  762. const auto& output = args[2];
  763. if (output.empty()) {
  764. status.SetError("Invalid name for output variable.");
  765. return false;
  766. }
  767. auto hash = hash_value(cmCMakePath(inputPath).Normal());
  768. std::ostringstream out;
  769. out << std::setbase(16) << hash;
  770. status.GetMakefile().AddDefinition(output, out.str());
  771. return true;
  772. }
  773. } // anonymous namespace
  774. bool cmCMakePathCommand(std::vector<std::string> const& args,
  775. cmExecutionStatus& status)
  776. {
  777. if (args.size() < 2) {
  778. status.SetError("must be called with at least two arguments.");
  779. return false;
  780. }
  781. static cmSubcommandTable const subcommand{
  782. { "GET"_s, HandleGetCommand },
  783. { "SET"_s, HandleSetCommand },
  784. { "APPEND"_s, HandleAppendCommand },
  785. { "APPEND_STRING"_s, HandleAppendStringCommand },
  786. { "REMOVE_FILENAME"_s, HandleRemoveFilenameCommand },
  787. { "REPLACE_FILENAME"_s, HandleReplaceFilenameCommand },
  788. { "REMOVE_EXTENSION"_s, HandleRemoveExtensionCommand },
  789. { "REPLACE_EXTENSION"_s, HandleReplaceExtensionCommand },
  790. { "NORMAL_PATH"_s, HandleNormalPathCommand },
  791. { "RELATIVE_PATH"_s, HandleRelativePathCommand },
  792. { "ABSOLUTE_PATH"_s, HandleAbsolutePathCommand },
  793. { "NATIVE_PATH"_s, HandleNativePathCommand },
  794. { "CONVERT"_s, HandleConvertCommand },
  795. { "COMPARE"_s, HandleCompareCommand },
  796. { "HAS_ROOT_NAME"_s, HandleHasRootNameCommand },
  797. { "HAS_ROOT_DIRECTORY"_s, HandleHasRootDirectoryCommand },
  798. { "HAS_ROOT_PATH"_s, HandleHasRootPathCommand },
  799. { "HAS_FILENAME"_s, HandleHasFilenameCommand },
  800. { "HAS_EXTENSION"_s, HandleHasExtensionCommand },
  801. { "HAS_STEM"_s, HandleHasStemCommand },
  802. { "HAS_RELATIVE_PART"_s, HandleHasRelativePartCommand },
  803. { "HAS_PARENT_PATH"_s, HandleHasParentPathCommand },
  804. { "IS_ABSOLUTE"_s, HandleIsAbsoluteCommand },
  805. { "IS_RELATIVE"_s, HandleIsRelativeCommand },
  806. { "IS_PREFIX"_s, HandleIsPrefixCommand },
  807. { "HASH"_s, HandleHashCommand }
  808. };
  809. return subcommand(args[0], args, status);
  810. }