cmCMakePathCommand.cxx 26 KB

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