You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1039 lines
32 KiB

Simplify CMake per-source license notices Per-source copyright/license notice headers that spell out copyright holder names and years are hard to maintain and often out-of-date or plain wrong. Precise contributor information is already maintained automatically by the version control tool. Ultimately it is the receiver of a file who is responsible for determining its licensing status, and per-source notices are merely a convenience. Therefore it is simpler and more accurate for each source to have a generic notice of the license name and references to more detailed information on copyright holders and full license terms. Our `Copyright.txt` file now contains a list of Contributors whose names appeared source-level copyright notices. It also references version control history for more precise information. Therefore we no longer need to spell out the list of Contributors in each source file notice. Replace CMake per-source copyright/license notice headers with a short description of the license and links to `Copyright.txt` and online information available from "https://cmake.org/licensing". The online URL also handles cases of modules being copied out of our source into other projects, so we can drop our notices about replacing links with full license text. Run the `Utilities/Scripts/filter-notices.bash` script to perform the majority of the replacements mechanically. Manually fix up shebang lines and trailing newlines in a few files. Manually update the notices in a few files that the script does not handle.
9 years ago
  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 "cmConfigure.h" // IWYU pragma: keep
  4. #include <algorithm>
  5. #include <cassert>
  6. #include <cctype>
  7. #include <climits>
  8. #include <cstdio>
  9. #include <cstring>
  10. #include <iostream>
  11. #include <sstream>
  12. #include <string>
  13. #include <utility>
  14. #include <vector>
  15. #include <cm/memory>
  16. #include <cmext/algorithm>
  17. #include <cm3p/uv.h>
  18. #include "cmBuildOptions.h"
  19. #include "cmCommandLineArgument.h"
  20. #include "cmConsoleBuf.h"
  21. #include "cmDocumentationEntry.h" // IWYU pragma: keep
  22. #include "cmGlobalGenerator.h"
  23. #include "cmMakefile.h"
  24. #include "cmMessageMetadata.h"
  25. #include "cmState.h"
  26. #include "cmStateTypes.h"
  27. #include "cmStringAlgorithms.h"
  28. #include "cmSystemTools.h"
  29. #include "cmValue.h"
  30. #include "cmake.h"
  31. #include "cmcmd.h"
  32. #ifndef CMAKE_BOOTSTRAP
  33. # include "cmDocumentation.h"
  34. # include "cmDynamicLoader.h"
  35. #endif
  36. #include "cmsys/Encoding.hxx"
  37. #include "cmsys/Terminal.h"
  38. namespace {
  39. #ifndef CMAKE_BOOTSTRAP
  40. const char* cmDocumentationName[][2] = {
  41. { nullptr, " cmake - Cross-Platform Makefile Generator." },
  42. { nullptr, nullptr }
  43. };
  44. const char* cmDocumentationUsage[][2] = {
  45. { nullptr,
  46. " cmake [options] <path-to-source>\n"
  47. " cmake [options] <path-to-existing-build>\n"
  48. " cmake [options] -S <path-to-source> -B <path-to-build>" },
  49. { nullptr,
  50. "Specify a source directory to (re-)generate a build system for "
  51. "it in the current working directory. Specify an existing build "
  52. "directory to re-generate its build system." },
  53. { nullptr, nullptr }
  54. };
  55. const char* cmDocumentationUsageNote[][2] = {
  56. { nullptr, "Run 'cmake --help' for more information." },
  57. { nullptr, nullptr }
  58. };
  59. const char* cmDocumentationOptions[][2] = {
  60. CMAKE_STANDARD_OPTIONS_TABLE,
  61. { "--preset <preset>,--preset=<preset>", "Specify a configure preset." },
  62. { "--list-presets", "List available presets." },
  63. { "-E", "CMake command mode." },
  64. { "-L[A][H]", "List non-advanced cached variables." },
  65. { "--build <dir>", "Build a CMake-generated project binary tree." },
  66. { "--install <dir>", "Install a CMake-generated project binary tree." },
  67. { "--open <dir>", "Open generated project in the associated application." },
  68. { "-N", "View mode only." },
  69. { "-P <file>", "Process script mode." },
  70. { "--find-package", "Legacy pkg-config like mode. Do not use." },
  71. { "--graphviz=[file]",
  72. "Generate graphviz of dependencies, see "
  73. "CMakeGraphVizOptions.cmake for more." },
  74. { "--system-information [file]", "Dump information about this system." },
  75. { "--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>",
  76. "Set the verbosity of messages from CMake files. "
  77. "--loglevel is also accepted for backward compatibility reasons." },
  78. { "--log-context", "Prepend log messages with context, if given" },
  79. { "--debug-trycompile",
  80. "Do not delete the try_compile build tree. Only "
  81. "useful on one try_compile at a time." },
  82. { "--debug-output", "Put cmake in a debug mode." },
  83. { "--debug-find", "Put cmake find in a debug mode." },
  84. { "--debug-find-pkg=<pkg-name>[,...]",
  85. "Limit cmake debug-find to the comma-separated list of packages" },
  86. { "--debug-find-var=<var-name>[,...]",
  87. "Limit cmake debug-find to the comma-separated list of result variables" },
  88. { "--trace", "Put cmake in trace mode." },
  89. { "--trace-expand", "Put cmake in trace mode with variable expansion." },
  90. { "--trace-format=<human|json-v1>", "Set the output format of the trace." },
  91. { "--trace-source=<file>",
  92. "Trace only this CMake file/module. Multiple options allowed." },
  93. { "--trace-redirect=<file>",
  94. "Redirect trace output to a file instead of stderr." },
  95. { "--warn-uninitialized", "Warn about uninitialized values." },
  96. { "--no-warn-unused-cli", "Don't warn about command line options." },
  97. { "--check-system-vars",
  98. "Find problems with variable usage in system "
  99. "files." },
  100. # if !defined(CMAKE_BOOTSTRAP)
  101. { "--profiling-format=<fmt>",
  102. "Output data for profiling CMake scripts. Supported formats: "
  103. "google-trace" },
  104. { "--profiling-output=<file>",
  105. "Select an output path for the profiling data enabled through "
  106. "--profiling-format." },
  107. # endif
  108. { nullptr, nullptr }
  109. };
  110. #endif
  111. int do_command(int ac, char const* const* av,
  112. std::unique_ptr<cmConsoleBuf> consoleBuf)
  113. {
  114. std::vector<std::string> args;
  115. args.reserve(ac - 1);
  116. args.emplace_back(av[0]);
  117. cm::append(args, av + 2, av + ac);
  118. return cmcmd::ExecuteCMakeCommand(args, std::move(consoleBuf));
  119. }
  120. cmMakefile* cmakemainGetMakefile(cmake* cm)
  121. {
  122. if (cm && cm->GetDebugOutput()) {
  123. cmGlobalGenerator* gg = cm->GetGlobalGenerator();
  124. if (gg) {
  125. return gg->GetCurrentMakefile();
  126. }
  127. }
  128. return nullptr;
  129. }
  130. std::string cmakemainGetStack(cmake* cm)
  131. {
  132. std::string msg;
  133. cmMakefile* mf = cmakemainGetMakefile(cm);
  134. if (mf) {
  135. msg = mf->FormatListFileStack();
  136. if (!msg.empty()) {
  137. msg = "\n Called from: " + msg;
  138. }
  139. }
  140. return msg;
  141. }
  142. void cmakemainMessageCallback(const std::string& m,
  143. const cmMessageMetadata& md, cmake* cm)
  144. {
  145. #if defined(_WIN32)
  146. // FIXME: On Windows we replace cerr's streambuf with a custom
  147. // implementation that converts our internal UTF-8 encoding to the
  148. // console's encoding. It also does *not* replace LF with CRLF.
  149. // Since stderr does not convert encoding and does convert LF, we
  150. // cannot use it to print messages. Another implementation will
  151. // be needed to print colored messages on Windows.
  152. static_cast<void>(md);
  153. std::cerr << m << cmakemainGetStack(cm) << '\n' << std::flush;
  154. #else
  155. cmsysTerminal_cfprintf(md.desiredColor, stderr, "%s", m.c_str());
  156. fflush(stderr); // stderr is buffered in some cases.
  157. std::cerr << cmakemainGetStack(cm) << '\n' << std::flush;
  158. #endif
  159. }
  160. void cmakemainProgressCallback(const std::string& m, float prog, cmake* cm)
  161. {
  162. cmMakefile* mf = cmakemainGetMakefile(cm);
  163. std::string dir;
  164. if (mf && cmHasLiteralPrefix(m, "Configuring") && (prog < 0)) {
  165. dir = cmStrCat(' ', mf->GetCurrentSourceDirectory());
  166. } else if (mf && cmHasLiteralPrefix(m, "Generating")) {
  167. dir = cmStrCat(' ', mf->GetCurrentBinaryDirectory());
  168. }
  169. if ((prog < 0) || (!dir.empty())) {
  170. std::cout << "-- " << m << dir << cmakemainGetStack(cm) << std::endl;
  171. }
  172. }
  173. int do_cmake(int ac, char const* const* av)
  174. {
  175. if (cmSystemTools::GetCurrentWorkingDirectory().empty()) {
  176. std::cerr << "Current working directory cannot be established."
  177. << std::endl;
  178. return 1;
  179. }
  180. #ifndef CMAKE_BOOTSTRAP
  181. cmDocumentation doc;
  182. doc.addCMakeStandardDocSections();
  183. if (doc.CheckOptions(ac, av)) {
  184. // Construct and print requested documentation.
  185. cmake hcm(cmake::RoleInternal, cmState::Unknown);
  186. hcm.SetHomeDirectory("");
  187. hcm.SetHomeOutputDirectory("");
  188. hcm.AddCMakePaths();
  189. // the command line args are processed here so that you can do
  190. // -DCMAKE_MODULE_PATH=/some/path and have this value accessible here
  191. std::vector<std::string> args(av, av + ac);
  192. hcm.SetCacheArgs(args);
  193. auto generators = hcm.GetGeneratorsDocumentation();
  194. doc.SetName("cmake");
  195. doc.SetSection("Name", cmDocumentationName);
  196. doc.SetSection("Usage", cmDocumentationUsage);
  197. if (ac == 1) {
  198. doc.AppendSection("Usage", cmDocumentationUsageNote);
  199. }
  200. doc.AppendSection("Generators", generators);
  201. doc.PrependSection("Options", cmDocumentationOptions);
  202. return doc.PrintRequestedDocumentation(std::cout) ? 0 : 1;
  203. }
  204. #else
  205. if (ac == 1) {
  206. std::cout
  207. << "Bootstrap CMake should not be used outside CMake build process."
  208. << std::endl;
  209. return 0;
  210. }
  211. #endif
  212. bool wizard_mode = false;
  213. bool sysinfo = false;
  214. bool list_cached = false;
  215. bool list_all_cached = false;
  216. bool list_help = false;
  217. bool view_only = false;
  218. cmake::WorkingMode workingMode = cmake::NORMAL_MODE;
  219. std::vector<std::string> parsedArgs;
  220. using CommandArgument =
  221. cmCommandLineArgument<bool(std::string const& value)>;
  222. std::vector<CommandArgument> arguments = {
  223. CommandArgument{
  224. "-i", CommandArgument::Values::Zero,
  225. [&wizard_mode](std::string const&) -> bool {
  226. /* clang-format off */
  227. std::cerr <<
  228. "The \"cmake -i\" wizard mode is no longer supported.\n"
  229. "Use the -D option to set cache values on the command line.\n"
  230. "Use cmake-gui or ccmake for an interactive dialog.\n";
  231. /* clang-format on */
  232. wizard_mode = true;
  233. return true;
  234. } },
  235. CommandArgument{ "--system-information", CommandArgument::Values::Zero,
  236. [&](std::string const&) -> bool {
  237. sysinfo = true;
  238. return true;
  239. } },
  240. CommandArgument{ "-N", CommandArgument::Values::Zero,
  241. [&](std::string const&) -> bool {
  242. view_only = true;
  243. return true;
  244. } },
  245. CommandArgument{ "-LAH", CommandArgument::Values::Zero,
  246. [&](std::string const&) -> bool {
  247. list_all_cached = true;
  248. list_help = true;
  249. return true;
  250. } },
  251. CommandArgument{ "-LA", CommandArgument::Values::Zero,
  252. [&](std::string const&) -> bool {
  253. list_all_cached = true;
  254. return true;
  255. } },
  256. CommandArgument{ "-LH", CommandArgument::Values::Zero,
  257. [&](std::string const&) -> bool {
  258. list_cached = true;
  259. list_help = true;
  260. return true;
  261. } },
  262. CommandArgument{ "-L", CommandArgument::Values::Zero,
  263. [&](std::string const&) -> bool {
  264. list_cached = true;
  265. return true;
  266. } },
  267. CommandArgument{ "-P", "No script specified for argument -P",
  268. CommandArgument::Values::One,
  269. CommandArgument::RequiresSeparator::No,
  270. [&](std::string const& value) -> bool {
  271. workingMode = cmake::SCRIPT_MODE;
  272. parsedArgs.emplace_back("-P");
  273. parsedArgs.push_back(value);
  274. return true;
  275. } },
  276. CommandArgument{ "--find-package", CommandArgument::Values::Zero,
  277. [&](std::string const&) -> bool {
  278. workingMode = cmake::FIND_PACKAGE_MODE;
  279. parsedArgs.emplace_back("--find-package");
  280. return true;
  281. } },
  282. CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
  283. [&](std::string const&) -> bool {
  284. workingMode = cmake::HELP_MODE;
  285. parsedArgs.emplace_back("--list-presets");
  286. return true;
  287. } },
  288. };
  289. std::vector<std::string> inputArgs;
  290. inputArgs.reserve(ac);
  291. cm::append(inputArgs, av, av + ac);
  292. for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) {
  293. std::string const& arg = inputArgs[i];
  294. bool matched = false;
  295. for (auto const& m : arguments) {
  296. if (m.matches(arg)) {
  297. matched = true;
  298. if (m.parse(arg, i, inputArgs)) {
  299. break;
  300. }
  301. return 1; // failed to parse
  302. }
  303. }
  304. if (!matched) {
  305. parsedArgs.emplace_back(av[i]);
  306. }
  307. }
  308. if (wizard_mode) {
  309. return 1;
  310. }
  311. if (sysinfo) {
  312. cmake cm(cmake::RoleProject, cmState::Project);
  313. cm.SetHomeDirectory("");
  314. cm.SetHomeOutputDirectory("");
  315. int ret = cm.GetSystemInformation(parsedArgs);
  316. return ret;
  317. }
  318. cmake::Role const role =
  319. workingMode == cmake::SCRIPT_MODE ? cmake::RoleScript : cmake::RoleProject;
  320. cmState::Mode mode = cmState::Unknown;
  321. switch (workingMode) {
  322. case cmake::NORMAL_MODE:
  323. case cmake::HELP_MODE:
  324. mode = cmState::Project;
  325. break;
  326. case cmake::SCRIPT_MODE:
  327. mode = cmState::Script;
  328. break;
  329. case cmake::FIND_PACKAGE_MODE:
  330. mode = cmState::FindPackage;
  331. break;
  332. }
  333. cmake cm(role, mode);
  334. cm.SetHomeDirectory("");
  335. cm.SetHomeOutputDirectory("");
  336. cmSystemTools::SetMessageCallback(
  337. [&cm](const std::string& msg, const cmMessageMetadata& md) {
  338. cmakemainMessageCallback(msg, md, &cm);
  339. });
  340. cm.SetProgressCallback([&cm](const std::string& msg, float prog) {
  341. cmakemainProgressCallback(msg, prog, &cm);
  342. });
  343. cm.SetWorkingMode(workingMode);
  344. int res = cm.Run(parsedArgs, view_only);
  345. if (list_cached || list_all_cached) {
  346. std::cout << "-- Cache values" << std::endl;
  347. std::vector<std::string> keys = cm.GetState()->GetCacheEntryKeys();
  348. for (std::string const& k : keys) {
  349. cmStateEnums::CacheEntryType t = cm.GetState()->GetCacheEntryType(k);
  350. if (t != cmStateEnums::INTERNAL && t != cmStateEnums::STATIC &&
  351. t != cmStateEnums::UNINITIALIZED) {
  352. cmValue advancedProp =
  353. cm.GetState()->GetCacheEntryProperty(k, "ADVANCED");
  354. if (list_all_cached || !advancedProp) {
  355. if (list_help) {
  356. cmValue help =
  357. cm.GetState()->GetCacheEntryProperty(k, "HELPSTRING");
  358. std::cout << "// " << (help ? *help : "") << std::endl;
  359. }
  360. std::cout << k << ":" << cmState::CacheEntryTypeToString(t) << "="
  361. << cm.GetState()->GetSafeCacheEntryValue(k) << std::endl;
  362. if (list_help) {
  363. std::cout << std::endl;
  364. }
  365. }
  366. }
  367. }
  368. }
  369. // Always return a non-negative value. Windows tools do not always
  370. // interpret negative return values as errors.
  371. if (res != 0) {
  372. return 1;
  373. }
  374. return 0;
  375. }
  376. #ifndef CMAKE_BOOTSTRAP
  377. int extract_job_number(std::string const& command,
  378. std::string const& jobString)
  379. {
  380. int jobs = -1;
  381. unsigned long numJobs = 0;
  382. if (jobString.empty()) {
  383. jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL;
  384. } else if (cmStrToULong(jobString, &numJobs)) {
  385. if (numJobs == 0) {
  386. std::cerr
  387. << "The <jobs> value requires a positive integer argument.\n\n";
  388. } else if (numJobs > INT_MAX) {
  389. std::cerr << "The <jobs> value is too large.\n\n";
  390. } else {
  391. jobs = int(numJobs);
  392. }
  393. } else {
  394. std::cerr << "'" << command << "' invalid number '" << jobString
  395. << "' given.\n\n";
  396. }
  397. return jobs;
  398. }
  399. #endif
  400. int do_build(int ac, char const* const* av)
  401. {
  402. #ifdef CMAKE_BOOTSTRAP
  403. std::cerr << "This cmake does not support --build\n";
  404. return -1;
  405. #else
  406. int jobs = cmake::NO_BUILD_PARALLEL_LEVEL;
  407. std::vector<std::string> targets;
  408. std::string config;
  409. std::string dir;
  410. std::vector<std::string> nativeOptions;
  411. bool nativeOptionsPassed = false;
  412. bool cleanFirst = false;
  413. bool foundClean = false;
  414. bool foundNonClean = false;
  415. PackageResolveMode resolveMode = PackageResolveMode::Default;
  416. bool verbose = cmSystemTools::HasEnv("VERBOSE");
  417. std::string presetName;
  418. bool listPresets = false;
  419. auto jLambda = [&](std::string const& value) -> bool {
  420. jobs = extract_job_number("-j", value);
  421. if (jobs < 0) {
  422. dir.clear();
  423. }
  424. return true;
  425. };
  426. auto parallelLambda = [&](std::string const& value) -> bool {
  427. jobs = extract_job_number("--parallel", value);
  428. if (jobs < 0) {
  429. dir.clear();
  430. }
  431. return true;
  432. };
  433. auto targetLambda = [&](std::string const& value) -> bool {
  434. if (!value.empty()) {
  435. std::vector<std::string> values = cmExpandedList(value);
  436. for (auto const& v : values) {
  437. targets.emplace_back(v);
  438. if (v == "clean") {
  439. foundClean = true;
  440. } else {
  441. foundNonClean = true;
  442. }
  443. }
  444. return true;
  445. }
  446. return false;
  447. };
  448. auto resolvePackagesLambda = [&](std::string const& value) -> bool {
  449. std::string v = value;
  450. std::transform(v.begin(), v.end(), v.begin(), ::tolower);
  451. if (v == "on") {
  452. resolveMode = PackageResolveMode::Force;
  453. } else if (v == "only") {
  454. resolveMode = PackageResolveMode::OnlyResolve;
  455. } else if (v == "off") {
  456. resolveMode = PackageResolveMode::Disable;
  457. } else {
  458. return false;
  459. }
  460. return true;
  461. };
  462. auto verboseLambda = [&](std::string const&) -> bool {
  463. verbose = true;
  464. return true;
  465. };
  466. using CommandArgument =
  467. cmCommandLineArgument<bool(std::string const& value)>;
  468. std::vector<CommandArgument> arguments = {
  469. CommandArgument{ "--preset", CommandArgument::Values::One,
  470. [&](std::string const& value) -> bool {
  471. presetName = value;
  472. return true;
  473. } },
  474. CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
  475. [&](std::string const&) -> bool {
  476. listPresets = true;
  477. return true;
  478. } },
  479. CommandArgument{ "-j", CommandArgument::Values::ZeroOrOne,
  480. CommandArgument::RequiresSeparator::No, jLambda },
  481. CommandArgument{ "--parallel", CommandArgument::Values::ZeroOrOne,
  482. CommandArgument::RequiresSeparator::No, parallelLambda },
  483. CommandArgument{ "-t", CommandArgument::Values::OneOrMore, targetLambda },
  484. CommandArgument{ "--target", CommandArgument::Values::OneOrMore,
  485. targetLambda },
  486. CommandArgument{ "--config", CommandArgument::Values::One,
  487. [&](std::string const& value) -> bool {
  488. config = value;
  489. return true;
  490. } },
  491. CommandArgument{ "--clean-first", CommandArgument::Values::Zero,
  492. [&](std::string const&) -> bool {
  493. cleanFirst = true;
  494. return true;
  495. } },
  496. CommandArgument{ "--resolve-package-references",
  497. CommandArgument::Values::One, resolvePackagesLambda },
  498. CommandArgument{ "-v", CommandArgument::Values::Zero, verboseLambda },
  499. CommandArgument{ "--verbose", CommandArgument::Values::Zero,
  500. verboseLambda },
  501. /* legacy option no-op*/
  502. CommandArgument{ "--use-stderr", CommandArgument::Values::Zero,
  503. [](std::string const&) -> bool { return true; } },
  504. CommandArgument{ "--", CommandArgument::Values::Zero,
  505. [&](std::string const&) -> bool {
  506. nativeOptionsPassed = true;
  507. return true;
  508. } },
  509. };
  510. if (ac >= 3) {
  511. std::vector<std::string> inputArgs;
  512. bool hasPreset = false;
  513. for (int i = 2; i < ac; ++i) {
  514. if (strcmp(av[i], "--list-presets") == 0 ||
  515. cmHasLiteralPrefix(av[i], "--preset=") ||
  516. strcmp(av[i], "--preset") == 0) {
  517. hasPreset = true;
  518. break;
  519. }
  520. }
  521. if (hasPreset) {
  522. inputArgs.reserve(ac - 2);
  523. cm::append(inputArgs, av + 2, av + ac);
  524. } else {
  525. dir = cmSystemTools::CollapseFullPath(av[2]);
  526. inputArgs.reserve(ac - 3);
  527. cm::append(inputArgs, av + 3, av + ac);
  528. }
  529. decltype(inputArgs.size()) i = 0;
  530. for (; i < inputArgs.size() && !nativeOptionsPassed; ++i) {
  531. std::string const& arg = inputArgs[i];
  532. bool matched = false;
  533. bool parsed = false;
  534. for (auto const& m : arguments) {
  535. matched = m.matches(arg);
  536. if (matched) {
  537. parsed = m.parse(arg, i, inputArgs);
  538. break;
  539. }
  540. }
  541. if (!(matched && parsed)) {
  542. dir.clear();
  543. if (!matched) {
  544. std::cerr << "Unknown argument " << arg << std::endl;
  545. }
  546. break;
  547. }
  548. }
  549. if (nativeOptionsPassed) {
  550. cm::append(nativeOptions, inputArgs.begin() + i, inputArgs.end());
  551. }
  552. }
  553. if (foundClean && foundNonClean) {
  554. std::cerr << "Error: Building 'clean' and other targets together "
  555. "is not supported."
  556. << std::endl;
  557. dir.clear();
  558. }
  559. if (jobs == cmake::NO_BUILD_PARALLEL_LEVEL) {
  560. std::string parallel;
  561. if (cmSystemTools::GetEnv("CMAKE_BUILD_PARALLEL_LEVEL", parallel)) {
  562. if (parallel.empty()) {
  563. jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL;
  564. } else {
  565. unsigned long numJobs = 0;
  566. if (cmStrToULong(parallel, &numJobs)) {
  567. if (numJobs == 0) {
  568. std::cerr << "The CMAKE_BUILD_PARALLEL_LEVEL environment variable "
  569. "requires a positive integer argument.\n\n";
  570. dir.clear();
  571. } else if (numJobs > INT_MAX) {
  572. std::cerr << "The CMAKE_BUILD_PARALLEL_LEVEL environment variable "
  573. "is too large.\n\n";
  574. dir.clear();
  575. } else {
  576. jobs = int(numJobs);
  577. }
  578. } else {
  579. std::cerr << "'CMAKE_BUILD_PARALLEL_LEVEL' environment variable\n"
  580. << "invalid number '" << parallel << "' given.\n\n";
  581. dir.clear();
  582. }
  583. }
  584. }
  585. }
  586. if (dir.empty() && presetName.empty() && !listPresets) {
  587. /* clang-format off */
  588. std::cerr <<
  589. "Usage: cmake --build <dir> "
  590. " [options] [-- [native-options]]\n"
  591. " cmake --build --preset <preset>"
  592. " [options] [-- [native-options]]\n"
  593. "Options:\n"
  594. " <dir> = Project binary directory to be built.\n"
  595. " --preset <preset>, --preset=<preset>\n"
  596. " = Specify a build preset.\n"
  597. " --list-presets\n"
  598. " = List available build presets.\n"
  599. " --parallel [<jobs>], -j [<jobs>]\n"
  600. " = Build in parallel using the given number of jobs. \n"
  601. " If <jobs> is omitted the native build tool's \n"
  602. " default number is used.\n"
  603. " The CMAKE_BUILD_PARALLEL_LEVEL environment "
  604. "variable\n"
  605. " specifies a default parallel level when this "
  606. "option\n"
  607. " is not given.\n"
  608. " --target <tgt>..., -t <tgt>... \n"
  609. " = Build <tgt> instead of default targets.\n"
  610. " --config <cfg> = For multi-configuration tools, choose <cfg>.\n"
  611. " --clean-first = Build target 'clean' first, then build.\n"
  612. " (To clean only, use --target 'clean'.)\n"
  613. " --resolve-package-references={on|only|off}\n"
  614. " = Restore/resolve package references during build.\n"
  615. " --verbose, -v = Enable verbose output - if supported - including\n"
  616. " the build commands to be executed. \n"
  617. " -- = Pass remaining options to the native tool.\n"
  618. ;
  619. /* clang-format on */
  620. return 1;
  621. }
  622. cmake cm(cmake::RoleInternal, cmState::Project);
  623. cmSystemTools::SetMessageCallback(
  624. [&cm](const std::string& msg, const cmMessageMetadata& md) {
  625. cmakemainMessageCallback(msg, md, &cm);
  626. });
  627. cm.SetProgressCallback([&cm](const std::string& msg, float prog) {
  628. cmakemainProgressCallback(msg, prog, &cm);
  629. });
  630. cmBuildOptions buildOptions(cleanFirst, false, resolveMode);
  631. return cm.Build(jobs, std::move(dir), std::move(targets), std::move(config),
  632. std::move(nativeOptions), buildOptions, verbose, presetName,
  633. listPresets);
  634. #endif
  635. }
  636. bool parse_default_directory_permissions(const std::string& permissions,
  637. std::string& parsedPermissionsVar)
  638. {
  639. std::vector<std::string> parsedPermissions;
  640. enum Doing
  641. {
  642. DoingNone,
  643. DoingOwner,
  644. DoingGroup,
  645. DoingWorld,
  646. DoingOwnerAssignment,
  647. DoingGroupAssignment,
  648. DoingWorldAssignment,
  649. };
  650. Doing doing = DoingNone;
  651. auto uniquePushBack = [&parsedPermissions](const std::string& e) {
  652. if (std::find(parsedPermissions.begin(), parsedPermissions.end(), e) ==
  653. parsedPermissions.end()) {
  654. parsedPermissions.push_back(e);
  655. }
  656. };
  657. for (auto const& e : permissions) {
  658. switch (doing) {
  659. case DoingNone:
  660. if (e == 'u') {
  661. doing = DoingOwner;
  662. } else if (e == 'g') {
  663. doing = DoingGroup;
  664. } else if (e == 'o') {
  665. doing = DoingWorld;
  666. } else {
  667. return false;
  668. }
  669. break;
  670. case DoingOwner:
  671. if (e == '=') {
  672. doing = DoingOwnerAssignment;
  673. } else {
  674. return false;
  675. }
  676. break;
  677. case DoingGroup:
  678. if (e == '=') {
  679. doing = DoingGroupAssignment;
  680. } else {
  681. return false;
  682. }
  683. break;
  684. case DoingWorld:
  685. if (e == '=') {
  686. doing = DoingWorldAssignment;
  687. } else {
  688. return false;
  689. }
  690. break;
  691. case DoingOwnerAssignment:
  692. if (e == 'r') {
  693. uniquePushBack("OWNER_READ");
  694. } else if (e == 'w') {
  695. uniquePushBack("OWNER_WRITE");
  696. } else if (e == 'x') {
  697. uniquePushBack("OWNER_EXECUTE");
  698. } else if (e == ',') {
  699. doing = DoingNone;
  700. } else {
  701. return false;
  702. }
  703. break;
  704. case DoingGroupAssignment:
  705. if (e == 'r') {
  706. uniquePushBack("GROUP_READ");
  707. } else if (e == 'w') {
  708. uniquePushBack("GROUP_WRITE");
  709. } else if (e == 'x') {
  710. uniquePushBack("GROUP_EXECUTE");
  711. } else if (e == ',') {
  712. doing = DoingNone;
  713. } else {
  714. return false;
  715. }
  716. break;
  717. case DoingWorldAssignment:
  718. if (e == 'r') {
  719. uniquePushBack("WORLD_READ");
  720. } else if (e == 'w') {
  721. uniquePushBack("WORLD_WRITE");
  722. } else if (e == 'x') {
  723. uniquePushBack("WORLD_EXECUTE");
  724. } else if (e == ',') {
  725. doing = DoingNone;
  726. } else {
  727. return false;
  728. }
  729. break;
  730. }
  731. }
  732. if (doing != DoingOwnerAssignment && doing != DoingGroupAssignment &&
  733. doing != DoingWorldAssignment) {
  734. return false;
  735. }
  736. std::ostringstream oss;
  737. for (auto i = 0u; i < parsedPermissions.size(); i++) {
  738. if (i != 0) {
  739. oss << ";";
  740. }
  741. oss << parsedPermissions[i];
  742. }
  743. parsedPermissionsVar = oss.str();
  744. return true;
  745. }
  746. int do_install(int ac, char const* const* av)
  747. {
  748. #ifdef CMAKE_BOOTSTRAP
  749. std::cerr << "This cmake does not support --install\n";
  750. return -1;
  751. #else
  752. assert(1 < ac);
  753. std::string config;
  754. std::string component;
  755. std::string defaultDirectoryPermissions;
  756. std::string prefix;
  757. std::string dir;
  758. bool strip = false;
  759. bool verbose = cmSystemTools::HasEnv("VERBOSE");
  760. auto verboseLambda = [&](std::string const&) -> bool {
  761. verbose = true;
  762. return true;
  763. };
  764. using CommandArgument =
  765. cmCommandLineArgument<bool(std::string const& value)>;
  766. std::vector<CommandArgument> arguments = {
  767. CommandArgument{ "--config", CommandArgument::Values::One,
  768. [&](std::string const& value) -> bool {
  769. config = value;
  770. return true;
  771. } },
  772. CommandArgument{ "--component", CommandArgument::Values::One,
  773. [&](std::string const& value) -> bool {
  774. component = value;
  775. return true;
  776. } },
  777. CommandArgument{ "--default-directory-permissions",
  778. CommandArgument::Values::One,
  779. [&](std::string const& value) -> bool {
  780. defaultDirectoryPermissions = value;
  781. return true;
  782. } },
  783. CommandArgument{ "--prefix", CommandArgument::Values::One,
  784. [&](std::string const& value) -> bool {
  785. prefix = value;
  786. return true;
  787. } },
  788. CommandArgument{ "--strip", CommandArgument::Values::Zero,
  789. [&](std::string const&) -> bool {
  790. strip = true;
  791. return true;
  792. } },
  793. CommandArgument{ "-v", CommandArgument::Values::Zero, verboseLambda },
  794. CommandArgument{ "--verbose", CommandArgument::Values::Zero,
  795. verboseLambda }
  796. };
  797. if (ac >= 3) {
  798. dir = cmSystemTools::CollapseFullPath(av[2]);
  799. std::vector<std::string> inputArgs;
  800. inputArgs.reserve(ac - 3);
  801. cm::append(inputArgs, av + 3, av + ac);
  802. for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) {
  803. std::string const& arg = inputArgs[i];
  804. bool matched = false;
  805. bool parsed = false;
  806. for (auto const& m : arguments) {
  807. matched = m.matches(arg);
  808. if (matched) {
  809. parsed = m.parse(arg, i, inputArgs);
  810. break;
  811. }
  812. }
  813. if (!(matched && parsed)) {
  814. dir.clear();
  815. if (!matched) {
  816. std::cerr << "Unknown argument " << arg << std::endl;
  817. }
  818. break;
  819. }
  820. }
  821. }
  822. if (dir.empty()) {
  823. /* clang-format off */
  824. std::cerr <<
  825. "Usage: cmake --install <dir> [options]\n"
  826. "Options:\n"
  827. " <dir> = Project binary directory to install.\n"
  828. " --config <cfg> = For multi-configuration tools, choose <cfg>.\n"
  829. " --component <comp> = Component-based install. Only install <comp>.\n"
  830. " --default-directory-permissions <permission> \n"
  831. " Default install permission. Use default permission <permission>.\n"
  832. " --prefix <prefix> = The installation prefix CMAKE_INSTALL_PREFIX.\n"
  833. " --strip = Performing install/strip.\n"
  834. " -v --verbose = Enable verbose output.\n"
  835. ;
  836. /* clang-format on */
  837. return 1;
  838. }
  839. cmake cm(cmake::RoleScript, cmState::Script);
  840. cmSystemTools::SetMessageCallback(
  841. [&cm](const std::string& msg, const cmMessageMetadata& md) {
  842. cmakemainMessageCallback(msg, md, &cm);
  843. });
  844. cm.SetProgressCallback([&cm](const std::string& msg, float prog) {
  845. cmakemainProgressCallback(msg, prog, &cm);
  846. });
  847. cm.SetHomeDirectory("");
  848. cm.SetHomeOutputDirectory("");
  849. cm.SetDebugOutputOn(verbose);
  850. cm.SetWorkingMode(cmake::SCRIPT_MODE);
  851. std::vector<std::string> args{ av[0] };
  852. if (!prefix.empty()) {
  853. args.emplace_back("-DCMAKE_INSTALL_PREFIX=" + prefix);
  854. }
  855. if (!component.empty()) {
  856. args.emplace_back("-DCMAKE_INSTALL_COMPONENT=" + component);
  857. }
  858. if (strip) {
  859. args.emplace_back("-DCMAKE_INSTALL_DO_STRIP=1");
  860. }
  861. if (!config.empty()) {
  862. args.emplace_back("-DCMAKE_INSTALL_CONFIG_NAME=" + config);
  863. }
  864. if (!defaultDirectoryPermissions.empty()) {
  865. std::string parsedPermissionsVar;
  866. if (!parse_default_directory_permissions(defaultDirectoryPermissions,
  867. parsedPermissionsVar)) {
  868. std::cerr << "--default-directory-permissions is in incorrect format"
  869. << std::endl;
  870. return 1;
  871. }
  872. args.emplace_back("-DCMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS=" +
  873. parsedPermissionsVar);
  874. }
  875. args.emplace_back("-P");
  876. args.emplace_back(dir + "/cmake_install.cmake");
  877. return cm.Run(args) ? 1 : 0;
  878. #endif
  879. }
  880. int do_open(int ac, char const* const* av)
  881. {
  882. #ifdef CMAKE_BOOTSTRAP
  883. std::cerr << "This cmake does not support --open\n";
  884. return -1;
  885. #else
  886. std::string dir;
  887. enum Doing
  888. {
  889. DoingNone,
  890. DoingDir,
  891. };
  892. Doing doing = DoingDir;
  893. for (int i = 2; i < ac; ++i) {
  894. switch (doing) {
  895. case DoingDir:
  896. dir = cmSystemTools::CollapseFullPath(av[i]);
  897. doing = DoingNone;
  898. break;
  899. default:
  900. std::cerr << "Unknown argument " << av[i] << std::endl;
  901. dir.clear();
  902. break;
  903. }
  904. }
  905. if (dir.empty()) {
  906. std::cerr << "Usage: cmake --open <dir>\n";
  907. return 1;
  908. }
  909. cmake cm(cmake::RoleInternal, cmState::Unknown);
  910. cmSystemTools::SetMessageCallback(
  911. [&cm](const std::string& msg, const cmMessageMetadata& md) {
  912. cmakemainMessageCallback(msg, md, &cm);
  913. });
  914. cm.SetProgressCallback([&cm](const std::string& msg, float prog) {
  915. cmakemainProgressCallback(msg, prog, &cm);
  916. });
  917. return cm.Open(dir, false) ? 0 : 1;
  918. #endif
  919. }
  920. } // namespace
  921. int main(int ac, char const* const* av)
  922. {
  923. cmSystemTools::EnsureStdPipes();
  924. // Replace streambuf so we can output Unicode to console
  925. auto consoleBuf = cm::make_unique<cmConsoleBuf>();
  926. consoleBuf->SetUTF8Pipes();
  927. cmsys::Encoding::CommandLineArguments args =
  928. cmsys::Encoding::CommandLineArguments::Main(ac, av);
  929. ac = args.argc();
  930. av = args.argv();
  931. cmSystemTools::InitializeLibUV();
  932. cmSystemTools::FindCMakeResources(av[0]);
  933. if (ac > 1) {
  934. if (strcmp(av[1], "--build") == 0) {
  935. return do_build(ac, av);
  936. }
  937. if (strcmp(av[1], "--install") == 0) {
  938. return do_install(ac, av);
  939. }
  940. if (strcmp(av[1], "--open") == 0) {
  941. return do_open(ac, av);
  942. }
  943. if (strcmp(av[1], "-E") == 0) {
  944. return do_command(ac, av, std::move(consoleBuf));
  945. }
  946. }
  947. int ret = do_cmake(ac, av);
  948. #ifndef CMAKE_BOOTSTRAP
  949. cmDynamicLoader::FlushCache();
  950. #endif
  951. if (uv_loop_t* loop = uv_default_loop()) {
  952. uv_loop_close(loop);
  953. }
  954. return ret;
  955. }