cmCMakePathCommand.cxx 27 KB

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