1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
9 #include <cmext/algorithm>
14 #include "cmCommandLineArgument.h"
15 #include "cmConsoleBuf.h"
16 #include "cmCryptoHash.h"
17 #include "cmDuration.h"
18 #include "cmGlobalGenerator.h"
20 #include "cmLocalGenerator.h"
21 #include "cmMakefile.h"
22 #include "cmQtAutoMocUic.h"
23 #include "cmQtAutoRcc.h"
26 #include "cmStateDirectory.h"
27 #include "cmStateSnapshot.h"
28 #include "cmStringAlgorithms.h"
29 #include "cmSystemTools.h"
30 #include "cmTransformDepfile.h"
31 #include "cmUVProcessChain.h"
32 #include "cmUVStream.h"
33 #include "cmUtils.hxx"
35 #include "cmVersion.h"
38 #if !defined(CMAKE_BOOTSTRAP)
39 # include "cmDependsFortran.h" // For -E cmake_copy_f90_mod callback.
40 # include "cmFileTime.h"
42 # include "bindexplib.h"
45 #if !defined(CMAKE_BOOTSTRAP) || defined(CMAKE_BOOTSTRAP_MAKEFILES)
48 # include "cmCMakePath.h"
49 # include "cmProcessTools.h"
52 #if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32) && !defined(__CYGWIN__)
53 # include "cmVisualStudioWCEPlatformParser.h"
67 # include <fcntl.h> // for _O_BINARY
68 # include <io.h> // for _setmode
71 #include <cm/string_view>
73 #include "cmsys/Directory.hxx"
74 #include "cmsys/FStream.hxx"
75 #include "cmsys/RegularExpression.hxx"
76 #include "cmsys/Terminal.h"
78 int cmcmd_cmake_ninja_depends(std::vector
<std::string
>::const_iterator argBeg
,
79 std::vector
<std::string
>::const_iterator argEnd
);
80 int cmcmd_cmake_ninja_dyndep(std::vector
<std::string
>::const_iterator argBeg
,
81 std::vector
<std::string
>::const_iterator argEnd
);
84 void CMakeCommandUsage(std::string
const& program
)
86 std::ostringstream errorStream
;
88 #ifndef CMAKE_BOOTSTRAP
89 /* clang-format off */
91 << "cmake version " << cmVersion::GetCMakeVersion() << "\n";
94 /* clang-format off */
96 << "cmake bootstrap\n";
99 // If you add new commands, change here,
100 // and in cmakemain.cxx in the options table
101 /* clang-format off */
103 << "Usage: " << program
<< " -E <command> [arguments...]\n"
104 << "Available commands: \n"
105 << " capabilities - Report capabilities built into cmake "
107 << " cat [--] <files>... - concat the files and print them to the "
109 << " chdir dir cmd [args...] - run command in a given directory\n"
110 << " compare_files [--ignore-eol] file1 file2\n"
111 << " - check if file1 is same as file2\n"
112 << " copy <file>... destination - copy files to destination "
113 "(either file or directory)\n"
114 << " copy_directory <dir>... destination - copy content of <dir>... "
115 "directories to 'destination' directory\n"
116 << " copy_directory_if_different <dir>... destination - copy changed content of <dir>... "
117 "directories to 'destination' directory\n"
118 << " copy_if_different <file>... destination - copy files if it has "
120 << " echo [<string>...] - displays arguments as text\n"
121 << " echo_append [<string>...] - displays arguments as text but no new "
123 << " env [--unset=NAME ...] [NAME=VALUE ...] [--] <command> [<arg>...]\n"
124 << " - run command in a modified environment\n"
125 << " environment - display the current environment\n"
126 << " make_directory <dir>... - create parent and <dir> directories\n"
127 << " md5sum <file>... - create MD5 checksum of files\n"
128 << " sha1sum <file>... - create SHA1 checksum of files\n"
129 << " sha224sum <file>... - create SHA224 checksum of files\n"
130 << " sha256sum <file>... - create SHA256 checksum of files\n"
131 << " sha384sum <file>... - create SHA384 checksum of files\n"
132 << " sha512sum <file>... - create SHA512 checksum of files\n"
133 << " remove [-f] <file>... - remove the file(s), use -f to force "
134 "it (deprecated: use rm instead)\n"
135 << " remove_directory <dir>... - remove directories and their contents (deprecated: use rm instead)\n"
136 << " rename oldname newname - rename a file or directory "
138 << " rm [-rRf] [--] <file/dir>... - remove files or directories, use -f "
139 "to force it, r or R to remove directories and their contents "
141 << " sleep <number>... - sleep for given number of seconds\n"
142 << " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n"
143 << " - create or extract a tar or zip archive\n"
144 << " time command [args...] - run command and display elapsed time\n"
145 << " touch <file>... - touch a <file>.\n"
146 << " touch_nocreate <file>... - touch a <file> but do not create it.\n"
147 << " create_symlink old new - create a symbolic link new -> old\n"
148 << " create_hardlink old new - create a hard link new -> old\n"
149 << " true - do nothing with an exit code of 0\n"
150 << " false - do nothing with an exit code of 1\n"
151 #if defined(_WIN32) && !defined(__CYGWIN__)
152 << "Available on Windows only:\n"
153 << " delete_regv key - delete registry value\n"
154 << " env_vs8_wince sdkname - displays a batch file which sets the "
155 "environment for the provided Windows CE SDK installed in VS2005\n"
156 << " env_vs9_wince sdkname - displays a batch file which sets the "
157 "environment for the provided Windows CE SDK installed in VS2008\n"
158 << " write_regv key value - write registry value\n"
161 /* clang-format on */
163 cmSystemTools::Error(errorStream
.str());
166 bool cmTarFilesFrom(std::string
const& file
, std::vector
<std::string
>& files
)
168 if (cmSystemTools::FileIsDirectory(file
)) {
169 std::ostringstream e
;
170 e
<< "-E tar --files-from= file '" << file
<< "' is a directory";
171 cmSystemTools::Error(e
.str());
174 cmsys::ifstream
fin(file
.c_str());
176 std::ostringstream e
;
177 e
<< "-E tar --files-from= file '" << file
<< "' not found";
178 cmSystemTools::Error(e
.str());
182 while (cmSystemTools::GetLineFromStream(fin
, line
)) {
186 if (cmHasLiteralPrefix(line
, "--add-file=")) {
187 files
.push_back(line
.substr(11));
188 } else if (cmHasLiteralPrefix(line
, "-")) {
189 std::ostringstream e
;
190 e
<< "-E tar --files-from='" << file
<< "' file invalid line:\n"
192 cmSystemTools::Error(e
.str());
195 files
.push_back(line
);
201 void cmCatFile(const std::string
& fileToAppend
)
204 _setmode(fileno(stdin
), _O_BINARY
);
205 _setmode(fileno(stdout
), _O_BINARY
);
207 std::streambuf
* buf
= std::cin
.rdbuf();
208 cmsys::ifstream source
;
209 if (fileToAppend
!= "-") {
210 source
.open(fileToAppend
.c_str(), (std::ios::binary
| std::ios::in
));
211 buf
= source
.rdbuf();
216 bool cmRemoveDirectory(const std::string
& dir
, bool recursive
= true)
218 if (cmSystemTools::FileIsSymlink(dir
)) {
219 if (!cmSystemTools::RemoveFile(dir
)) {
220 std::cerr
<< "Error removing directory symlink \"" << dir
<< "\".\n";
223 } else if (!recursive
) {
224 std::cerr
<< "Error removing directory \"" << dir
225 << "\" without recursive option.\n";
227 } else if (!cmSystemTools::RemoveADirectory(dir
)) {
228 std::cerr
<< "Error removing directory \"" << dir
<< "\".\n";
234 #if !defined(CMAKE_BOOTSTRAP) || defined(CMAKE_BOOTSTRAP_MAKEFILES)
235 class CLIncludeParser
: public cmProcessTools::LineParser
238 CLIncludeParser(cm::string_view includePrefix
, cmsys::ofstream
& depFile
,
239 std::ostream
& output
)
240 : IncludePrefix(includePrefix
)
247 bool ProcessLine() override
249 if (cmHasPrefix(this->Line
, this->IncludePrefix
)) {
251 cmTrimWhitespace(this->Line
.c_str() + this->IncludePrefix
.size());
252 cmSystemTools::ConvertToLongPath(path
);
253 this->DepFile
<< cmCMakePath(path
).GenericString() << std::endl
;
255 this->Output
<< this->Line
<< std::endl
<< std::flush
;
261 cm::string_view IncludePrefix
;
262 cmsys::ofstream
& DepFile
;
263 std::ostream
& Output
;
266 class CLOutputLogger
: public cmProcessTools::OutputLogger
269 CLOutputLogger(std::ostream
& log
)
270 : cmProcessTools::OutputLogger(log
)
274 bool ProcessLine() override
276 *this->Log
<< std::flush
;
281 int CLCompileAndDependencies(const std::vector
<std::string
>& args
)
284 std::string currentBinaryDir
;
285 std::string filterPrefix
;
286 std::vector
<std::string
> command
;
287 for (auto it
= args
.cbegin() + 2; it
!= args
.cend(); it
++) {
288 if (cmHasLiteralPrefix(*it
, "--dep-file=")) {
289 depFile
= it
->substr(11);
290 } else if (cmHasLiteralPrefix(*it
, "--working-dir=")) {
291 currentBinaryDir
= it
->substr(14);
292 } else if (cmHasLiteralPrefix(*it
, "--filter-prefix=")) {
293 filterPrefix
= it
->substr(16);
294 } else if (*it
== "--") {
295 command
.insert(command
.begin(), ++it
, args
.cend());
302 cmUVProcessChainBuilder builder
;
303 builder
.AddCommand(command
).SetWorkingDirectory(currentBinaryDir
);
305 cmsys::ofstream
fout(depFile
.c_str());
310 CLIncludeParser
includeParser(filterPrefix
, fout
, std::cout
);
311 CLOutputLogger
errLogger(std::cerr
);
313 // Start the process.
315 cmProcessTools::RunProcess(builder
, &includeParser
, &errLogger
);
316 auto const& subStatus
= result
.front();
319 // handle status of process
320 if (subStatus
.SpawnResult
!= 0) {
322 } else if (subStatus
.TermSignal
!= 0) {
325 status
= static_cast<int>(subStatus
.ExitStatus
);
329 // remove the dependencies file because potentially invalid
331 cmSystemTools::RemoveFile(depFile
);
338 int HandleIWYU(const std::string
& runCmd
, const std::string
& /* sourceFile */,
339 const std::vector
<std::string
>& orig_cmd
)
341 // Construct the iwyu command line by taking what was given
342 // and adding all the arguments we give to the compiler.
343 cmList iwyu_cmd
{ runCmd
, cmList::EmptyElements::Yes
};
344 cm::append(iwyu_cmd
, orig_cmd
.begin() + 1, orig_cmd
.end());
345 // Run the iwyu command line. Capture its stderr and hide its stdout.
348 if (!cmSystemTools::RunSingleCommand(iwyu_cmd
, nullptr, &stdErr
, &ret
,
349 nullptr, cmSystemTools::OUTPUT_NONE
)) {
350 std::cerr
<< "Error running '" << iwyu_cmd
[0] << "': " << stdErr
<< "\n";
353 // Warn if iwyu reported anything.
354 if (stdErr
.find("should remove these lines:") != std::string::npos
||
355 stdErr
.find("should add these lines:") != std::string::npos
) {
356 std::cerr
<< "Warning: include-what-you-use reported diagnostics:\n"
359 // Older versions of iwyu always returned a non-zero exit code,
360 // so ignore it unless the user has enabled errors.
361 auto has_error_opt
= std::find_if(
362 iwyu_cmd
.cbegin(), iwyu_cmd
.cend(),
363 [](std::string
const& opt
) { return cmHasLiteralPrefix(opt
, "--error"); });
364 bool errors_enabled
= has_error_opt
!= iwyu_cmd
.cend() &&
365 has_error_opt
!= iwyu_cmd
.cbegin() &&
366 *std::prev(has_error_opt
) == "-Xiwyu";
367 return errors_enabled
? ret
: 0;
370 int HandleTidy(const std::string
& runCmd
, const std::string
& sourceFile
,
371 const std::vector
<std::string
>& orig_cmd
)
373 cmList tidy_cmd
{ runCmd
, cmList::EmptyElements::Yes
};
374 tidy_cmd
.push_back(sourceFile
);
376 for (auto const& arg
: tidy_cmd
) {
377 if (cmHasLiteralPrefix(arg
, "--export-fixes=")) {
378 cmSystemTools::RemoveFile(arg
.substr(cmStrLen("--export-fixes=")));
382 // clang-tidy supports working out the compile commands from a
383 // compile_commands.json file in a directory given by a "-p" option, or by
384 // passing the compiler command line arguments after --. When the latter
385 // strategy is used and the build is using a compiler other than the system
386 // default, clang-tidy may erroneously use the system default compiler's
387 // headers instead of those from the custom compiler. It doesn't do that if
388 // given a compile_commands.json to work with instead, so prefer to use the
389 // compile_commands.json file when "-p" is present.
390 if (!cm::contains(tidy_cmd
.cbegin(), tidy_cmd
.cend() - 1, "-p")) {
391 // Construct the clang-tidy command line by taking what was given
392 // and adding our compiler command line. The clang-tidy tool will
393 // automatically skip over the compiler itself and extract the
394 // options. If the compiler is a custom compiler, clang-tidy might
395 // not correctly handle that with this approach.
396 tidy_cmd
.emplace_back("--");
397 cm::append(tidy_cmd
, orig_cmd
);
400 // Run the tidy command line. Capture its stdout and hide its stderr.
404 if (!cmSystemTools::RunSingleCommand(tidy_cmd
, &stdOut
, &stdErr
, &ret
,
405 nullptr, cmSystemTools::OUTPUT_NONE
)) {
406 std::cerr
<< "Error running '" << tidy_cmd
[0] << "': " << stdErr
<< "\n";
409 // Output the stdout from clang-tidy to stderr
411 // If clang-tidy exited with an error do the same.
418 int HandleLWYU(const std::string
& runCmd
, const std::string
& sourceFile
,
419 const std::vector
<std::string
>&)
421 // Construct the ldd -r -u (link what you use lwyu) command line
422 // ldd -u -r lwuy target
423 cmList lwyu_cmd
{ runCmd
, cmList::EmptyElements::Yes
};
424 lwyu_cmd
.push_back(sourceFile
);
426 // Run the lwyu check command line, currently ldd is expected.
427 // Capture its stdout and hide its stderr.
428 // Ignore its return code because the tool always returns non-zero
429 // if there are any warnings, but we just want to warn.
433 if (!cmSystemTools::RunSingleCommand(lwyu_cmd
, &stdOut
, &stdErr
, &ret
,
434 nullptr, cmSystemTools::OUTPUT_NONE
)) {
435 std::cerr
<< "Error running '" << lwyu_cmd
[0] << "': " << stdErr
<< "\n";
439 // Output the stdout from ldd -r -u to stderr
440 // Warn if lwyu reported anything.
441 if (stdOut
.find("Unused direct dependencies:") != std::string::npos
) {
442 std::cerr
<< "Warning: " << stdOut
;
447 int HandleCppLint(const std::string
& runCmd
, const std::string
& sourceFile
,
448 const std::vector
<std::string
>&)
450 // Construct the cpplint command line.
451 cmList cpplint_cmd
{ runCmd
, cmList::EmptyElements::Yes
};
452 cpplint_cmd
.push_back(sourceFile
);
454 // Run the cpplint command line. Capture its output.
457 if (!cmSystemTools::RunSingleCommand(cpplint_cmd
, &stdOut
, &stdOut
, &ret
,
458 nullptr, cmSystemTools::OUTPUT_NONE
)) {
459 std::cerr
<< "Error running '" << cpplint_cmd
[0] << "': " << stdOut
463 if (!stdOut
.empty()) {
464 std::cerr
<< "Warning: cpplint diagnostics:\n";
465 // Output the output from cpplint to stderr
469 // always return 0 so the build can continue as cpplint returns non-zero
474 int HandleCppCheck(const std::string
& runCmd
, const std::string
& sourceFile
,
475 const std::vector
<std::string
>& orig_cmd
)
477 // Construct the cpplint command line.
478 cmList cppcheck_cmd
{ runCmd
, cmList::EmptyElements::Yes
};
479 // extract all the -D, -U, and -I options from the compile line
480 for (auto const& opt
: orig_cmd
) {
481 if (opt
.size() > 2) {
482 if ((opt
[0] == '-') &&
483 ((opt
[1] == 'D') || (opt
[1] == 'I') || (opt
[1] == 'U'))) {
484 cppcheck_cmd
.push_back(opt
);
485 // convert cl / options to - options if needed
487 } else if ((opt
[0] == '/') &&
488 ((opt
[1] == 'D') || (opt
[1] == 'I') || (opt
[1] == 'U'))) {
489 std::string optcopy
= opt
;
491 cppcheck_cmd
.push_back(optcopy
);
496 // add the source file
497 cppcheck_cmd
.push_back(sourceFile
);
499 // Run the cpplint command line. Capture its output.
503 if (!cmSystemTools::RunSingleCommand(cppcheck_cmd
, &stdOut
, &stdErr
, &ret
,
504 nullptr, cmSystemTools::OUTPUT_NONE
)) {
505 std::cerr
<< "Error running '" << cppcheck_cmd
[0] << "': " << stdOut
510 // Output the output from cpplint to stderr
511 if (stdErr
.find("(error)") != std::string::npos
||
512 stdErr
.find("(warning)") != std::string::npos
||
513 stdErr
.find("(style)") != std::string::npos
||
514 stdErr
.find("(performance)") != std::string::npos
||
515 stdErr
.find("(portability)") != std::string::npos
||
516 stdErr
.find("(information)") != std::string::npos
) {
518 std::cerr
<< "Warning: cppcheck reported diagnostics:\n";
520 std::cerr
<< "Error: cppcheck reported failure:\n";
528 using CoCompileHandler
= int (*)(const std::string
&, const std::string
&,
529 const std::vector
<std::string
>&);
534 CoCompileHandler Handler
;
535 bool NoOriginalCommand
;
538 const std::array
<CoCompiler
, 5> CoCompilers
= {
539 { // Table of options and handlers.
540 { "--cppcheck=", HandleCppCheck
, false },
541 { "--cpplint=", HandleCppLint
, false },
542 { "--iwyu=", HandleIWYU
, false },
543 { "--lwyu=", HandleLWYU
, true },
544 { "--tidy=", HandleTidy
, false } }
550 CoCompileHandler Handler
;
554 // called when args[0] == "__run_co_compile"
555 int cmcmd::HandleCoCompileCommands(std::vector
<std::string
> const& args
)
557 std::vector
<CoCompileJob
> jobs
;
558 std::string sourceFile
; // store --source=
559 cmList launchers
; // store --launcher=
561 // Default is to run the original command found after -- if the option
562 // does not need to do that, it should be specified here, currently only
564 bool runOriginalCmd
= true;
566 std::vector
<std::string
> orig_cmd
;
567 bool doing_options
= true;
568 for (std::string
const& arg
: cmMakeRange(args
).advance(2)) {
569 // if the arg is -- then the rest of the args after
572 doing_options
= false;
573 } else if (doing_options
) {
574 bool optionFound
= false;
575 for (CoCompiler
const& cc
: CoCompilers
) {
576 size_t optionLen
= strlen(cc
.Option
);
577 if (arg
.compare(0, optionLen
, cc
.Option
) == 0) {
580 job
.Command
= arg
.substr(optionLen
);
581 job
.Handler
= cc
.Handler
;
582 jobs
.push_back(std::move(job
));
583 if (cc
.NoOriginalCommand
) {
584 runOriginalCmd
= false;
589 if (cmHasLiteralPrefix(arg
, "--source=")) {
590 sourceFile
= arg
.substr(9);
591 } else if (cmHasLiteralPrefix(arg
, "--launcher=")) {
592 launchers
.append(arg
.substr(11), cmList::EmptyElements::Yes
);
594 // if it was not a co-compiler or --source/--launcher then error
595 std::cerr
<< "__run_co_compile given unknown argument: " << arg
600 } else { // if not doing_options then push to orig_cmd
601 orig_cmd
.push_back(arg
);
605 std::cerr
<< "__run_co_compile missing command to run. "
606 "Looking for one or more of the following:\n";
607 for (CoCompiler
const& cc
: CoCompilers
) {
608 std::cerr
<< cc
.Option
<< "\n";
613 if (runOriginalCmd
&& orig_cmd
.empty()) {
614 std::cerr
<< "__run_co_compile missing compile command after --\n";
618 for (CoCompileJob
const& job
: jobs
) {
619 // call the command handler here
620 int ret
= job
.Handler(job
.Command
, sourceFile
, orig_cmd
);
622 // if the command returns non-zero then return and fail.
623 // for commands that do not want to break the build, they should return
630 // if there is no original command to run return now
631 if (!runOriginalCmd
) {
635 // Prepend launcher argument(s), if any
636 if (!launchers
.empty()) {
637 orig_cmd
.insert(orig_cmd
.begin(), launchers
.begin(), launchers
.end());
640 // Now run the real compiler command and return its result value
642 if (!cmSystemTools::RunSingleCommand(orig_cmd
, nullptr, nullptr, &ret
,
644 cmSystemTools::OUTPUT_PASSTHROUGH
)) {
645 std::cerr
<< "Error running '" << orig_cmd
[0] << "'\n";
648 // return the return value from the original compiler command
652 int cmcmd::ExecuteCMakeCommand(std::vector
<std::string
> const& args
,
653 std::unique_ptr
<cmConsoleBuf
> consoleBuf
)
655 // IF YOU ADD A NEW COMMAND, DOCUMENT IT ABOVE and in cmakemain.cxx
656 if (args
.size() > 1) {
658 if (args
[1] == "copy" && args
.size() > 3) {
659 using CommandArgument
=
660 cmCommandLineArgument
<bool(const std::string
& value
)>;
662 cm::optional
<std::string
> targetArg
;
663 std::vector
<CommandArgument
> argParsers
{
664 { "-t", CommandArgument::Values::One
,
665 CommandArgument::setToValue(targetArg
) },
668 std::vector
<std::string
> files
;
669 for (decltype(args
.size()) i
= 2; i
< args
.size(); i
++) {
670 const std::string
& arg
= args
[i
];
671 bool matched
= false;
672 for (auto const& m
: argParsers
) {
673 if (m
.matches(arg
)) {
675 if (m
.parse(arg
, i
, args
)) {
678 return 1; // failed to parse
682 files
.push_back(arg
);
686 // If multiple source files specified,
687 // then destination must be directory
688 if (files
.size() > 2 && !targetArg
) {
689 targetArg
= files
.back();
692 if (targetArg
&& (!cmSystemTools::FileIsDirectory(*targetArg
))) {
693 std::cerr
<< "Error: Target (for copy command) \"" << *targetArg
694 << "\" is not a directory.\n";
698 if (files
.size() < 2) {
700 << "Error: No files or target specified (for copy command).\n";
703 targetArg
= files
.back();
706 // If error occurs we want to continue copying next files.
707 bool return_value
= false;
708 for (auto const& file
: files
) {
709 if (!cmsys::SystemTools::CopyFileAlways(file
, *targetArg
)) {
710 std::cerr
<< "Error copying file \"" << file
<< "\" to \""
711 << *targetArg
<< "\".\n";
718 // Copy file if different.
719 if (args
[1] == "copy_if_different" && args
.size() > 3) {
720 // If multiple source files specified,
721 // then destination must be directory
722 if ((args
.size() > 4) &&
723 (!cmSystemTools::FileIsDirectory(args
.back()))) {
724 std::cerr
<< "Error: Target (for copy_if_different command) \""
725 << args
.back() << "\" is not a directory.\n";
728 // If error occurs we want to continue copying next files.
729 bool return_value
= false;
730 for (auto const& arg
: cmMakeRange(args
).advance(2).retreat(1)) {
731 if (!cmSystemTools::CopyFileIfDifferent(arg
, args
.back())) {
732 std::cerr
<< "Error copying file (if different) from \"" << arg
733 << "\" to \"" << args
.back() << "\".\n";
740 // Copy directory contents
741 if ((args
[1] == "copy_directory" ||
742 args
[1] == "copy_directory_if_different") &&
744 // If error occurs we want to continue copying next files.
745 bool return_value
= false;
746 const bool copy_always
= (args
[1] == "copy_directory");
747 for (auto const& arg
: cmMakeRange(args
).advance(2).retreat(1)) {
748 if (!cmSystemTools::CopyADirectory(arg
, args
.back(), copy_always
)) {
749 std::cerr
<< "Error copying directory from \"" << arg
<< "\" to \""
750 << args
.back() << "\".\n";
757 // Rename a file or directory
758 if (args
[1] == "rename" && args
.size() == 4) {
759 if (!cmSystemTools::RenameFile(args
[2], args
[3])) {
760 std::string e
= cmSystemTools::GetLastSystemError();
761 std::cerr
<< "Error renaming from \"" << args
[2] << "\" to \""
762 << args
[3] << "\": " << e
<< "\n";
769 if (args
[1] == "compare_files" && (args
.size() == 4 || args
.size() == 5)) {
771 if (args
.size() == 4) {
772 filesDiffer
= cmSystemTools::FilesDiffer(args
[2], args
[3]);
773 } else if (args
[2] == "--ignore-eol") {
774 filesDiffer
= cmsys::SystemTools::TextFilesDiffer(args
[3], args
[4]);
776 CMakeCommandUsage(args
[0]);
786 #if !defined(CMAKE_BOOTSTRAP)
787 if (args
[1] == "__create_def") {
788 if (args
.size() < 4) {
789 std::cerr
<< "__create_def Usage: -E __create_def outfile.def "
790 "objlistfile [--nm=nm-path]\n";
793 cmsys::ifstream
fin(args
[3].c_str(), std::ios::in
| std::ios::binary
);
795 std::cerr
<< "could not open object list file: " << args
[3] << "\n";
798 std::vector
<std::string
> files
;
802 bool outValid
= outTime
.Load(args
[2]);
803 while (cmSystemTools::GetLineFromStream(fin
, file
)) {
804 files
.push_back(file
);
807 outValid
= inTime
.Load(file
) && inTime
.Older(outTime
);
811 // The def file already exists and all input files are older than
812 // the existing def file.
816 FILE* fout
= cmsys::SystemTools::Fopen(args
[2], "w+");
818 std::cerr
<< "could not open output .def file: " << args
[2] << "\n";
822 if (args
.size() >= 5) {
823 std::string
const& a
= args
[4];
824 if (cmHasLiteralPrefix(a
, "--nm=")) {
825 deffile
.SetNmPath(a
.substr(5));
827 std::cerr
<< "unknown argument: " << a
<< "\n";
830 for (std::string
const& file
: files
) {
831 std::string
const& ext
= cmSystemTools::GetFilenameLastExtension(file
);
832 if (cmSystemTools::LowerCase(ext
) == ".def") {
833 if (!deffile
.AddDefinitionFile(file
.c_str())) {
837 if (!deffile
.AddObjectFile(file
.c_str())) {
842 deffile
.WriteFile(fout
);
847 if (args
[1] == "__run_co_compile") {
848 return cmcmd::HandleCoCompileCommands(args
);
852 if (args
[1] == "echo") {
853 std::cout
<< cmJoin(cmMakeRange(args
).advance(2), " ") << std::endl
;
857 // Echo string no new line
858 if (args
[1] == "echo_append") {
859 std::cout
<< cmJoin(cmMakeRange(args
).advance(2), " ");
863 if (args
[1] == "env") {
864 #ifndef CMAKE_BOOTSTRAP
865 cmSystemTools::EnvDiff env
;
868 auto ai
= args
.cbegin() + 2;
869 auto ae
= args
.cend();
870 for (; ai
!= ae
; ++ai
) {
871 std::string
const& a
= *ai
;
873 // Stop parsing options/environment variables; the next argument
874 // should be the command.
878 if (cmHasLiteralPrefix(a
, "--unset=")) {
879 // Unset environment variable.
880 #ifdef CMAKE_BOOTSTRAP
881 cmSystemTools::UnPutEnv(a
.substr(8));
883 env
.UnPutEnv(a
.substr(8));
885 } else if (a
== "--modify") {
886 #ifdef CMAKE_BOOTSTRAP
888 << "cmake -E env: --modify not available during bootstrapping\n";
892 std::cerr
<< "cmake -E env: --modify missing a parameter\n";
895 std::string
const& op
= *ai
;
896 if (!env
.ParseOperation(op
)) {
897 std::cerr
<< "cmake -E env: invalid parameter to --modify: " << op
902 } else if (!a
.empty() && a
[0] == '-') {
903 // Environment variable and command names cannot start in '-',
904 // so this must be an unknown option.
905 std::cerr
<< "cmake -E env: unknown option '" << a
<< "'\n";
907 } else if (a
.find('=') != std::string::npos
) {
908 // Set environment variable.
909 #ifdef CMAKE_BOOTSTRAP
910 cmSystemTools::PutEnv(a
);
915 // This is the beginning of the command.
921 std::cerr
<< "cmake -E env: no command given\n";
925 #ifndef CMAKE_BOOTSTRAP
926 env
.ApplyToCurrentEnv();
929 // Execute command from remaining arguments.
930 std::vector
<std::string
> cmd(ai
, ae
);
932 if (cmSystemTools::RunSingleCommand(cmd
, nullptr, nullptr, &retval
,
934 cmSystemTools::OUTPUT_PASSTHROUGH
)) {
940 #if !defined(CMAKE_BOOTSTRAP)
941 if (args
[1] == "environment") {
942 for (auto const& env
: cmSystemTools::GetEnvironmentVariables()) {
943 std::cout
<< env
<< std::endl
;
949 if (args
[1] == "make_directory" && args
.size() > 2) {
950 // If an error occurs, we want to continue making directories.
951 bool return_value
= false;
952 for (auto const& arg
: cmMakeRange(args
).advance(2)) {
953 if (!cmSystemTools::MakeDirectory(arg
)) {
954 std::cerr
<< "Error creating directory \"" << arg
<< "\".\n";
961 if (args
[1] == "remove_directory" && args
.size() > 2) {
962 // If an error occurs, we want to continue removing directories.
963 bool return_value
= false;
964 for (auto const& arg
: cmMakeRange(args
).advance(2)) {
965 if (cmSystemTools::FileIsDirectory(arg
)) {
966 if (!cmRemoveDirectory(arg
)) {
975 if (args
[1] == "remove" && args
.size() > 2) {
977 for (auto const& arg
: cmMakeRange(args
).advance(2)) {
978 if (arg
== "\\-f" || arg
== "-f") {
981 // Complain if the file could not be removed, still exists,
982 // and the -f option was not given.
983 if (!cmSystemTools::RemoveFile(arg
) && !force
&&
984 cmSystemTools::FileExists(arg
)) {
992 // Remove directories or files with rm
993 if (args
[1] == "rm" && args
.size() > 2) {
994 // If an error occurs, we want to continue removing the remaining
995 // files/directories.
996 int return_value
= 0;
998 bool recursive
= false;
999 bool doing_options
= true;
1000 bool at_least_one_file
= false;
1001 for (auto const& arg
: cmMakeRange(args
).advance(2)) {
1002 if (doing_options
&& cmHasLiteralPrefix(arg
, "-")) {
1004 doing_options
= false;
1006 if (arg
.find('f') != std::string::npos
) {
1009 if (arg
.find_first_of("rR") != std::string::npos
) {
1012 if (arg
.find_first_not_of("-frR") != std::string::npos
) {
1013 cmSystemTools::Error("Unknown -E rm argument: " + arg
);
1020 at_least_one_file
= true;
1021 // Complain if the -f option was not given and
1022 // either file does not exist or
1023 // file could not be removed and still exists
1024 bool file_exists_or_forced_remove
=
1025 cmSystemTools::PathExists(arg
) || force
;
1026 if (cmSystemTools::FileIsDirectory(arg
)) {
1027 if (!cmRemoveDirectory(arg
, recursive
)) {
1030 } else if ((!file_exists_or_forced_remove
) ||
1031 (!cmSystemTools::RemoveFile(arg
) &&
1032 cmSystemTools::FileExists(arg
))) {
1033 if (!file_exists_or_forced_remove
) {
1034 cmSystemTools::Error(
1035 "File to remove does not exist and force is not set: " + arg
);
1037 cmSystemTools::Error("File can't be removed and still exist: " +
1044 if (!at_least_one_file
) {
1045 cmSystemTools::Error("Missing file/directory to remove");
1048 return return_value
;
1052 if (args
[1] == "touch" && args
.size() > 2) {
1053 for (auto const& arg
: cmMakeRange(args
).advance(2)) {
1054 if (!cmSystemTools::Touch(arg
, true)) {
1055 std::cerr
<< "cmake -E touch: failed to update \"";
1056 std::cerr
<< arg
<< "\".\n";
1064 if (args
[1] == "touch_nocreate" && args
.size() > 2) {
1065 for (auto const& arg
: cmMakeRange(args
).advance(2)) {
1066 if (!cmSystemTools::Touch(arg
, false)) {
1067 std::cerr
<< "cmake -E touch_nocreate: failed to update \"";
1068 std::cerr
<< arg
<< "\".\n";
1076 if (args
[1] == "capabilities") {
1077 if (args
.size() > 2) {
1078 std::cerr
<< "-E capabilities accepts no additional arguments\n";
1081 cmake
cm(cmake::RoleInternal
, cmState::Unknown
);
1082 std::cout
<< cm
.ReportCapabilities();
1087 if (args
[1] == "sleep" && args
.size() > 2) {
1089 for (auto const& arg
: cmMakeRange(args
).advance(2)) {
1093 int n
= sscanf(arg
.c_str(), "%lg%c%c", &num
, &unit
, &extra
);
1094 if ((n
== 1 || (n
== 2 && unit
== 's')) && num
>= 0) {
1097 std::cerr
<< "Unknown sleep time format \"" << arg
<< "\".\n";
1102 cmSystemTools::Delay(static_cast<unsigned int>(total
* 1000));
1108 if (args
[1] == "time" && args
.size() > 2) {
1109 std::vector
<std::string
> command(args
.begin() + 2, args
.end());
1112 auto time_start
= std::chrono::steady_clock::now();
1113 cmSystemTools::RunSingleCommand(command
, nullptr, nullptr, &ret
, nullptr,
1114 cmSystemTools::OUTPUT_PASSTHROUGH
);
1115 auto time_finish
= std::chrono::steady_clock::now();
1117 std::chrono::duration
<double> time_elapsed
= time_finish
- time_start
;
1118 std::cout
<< "Elapsed time (seconds): " << time_elapsed
.count() << "\n";
1122 // Command to calculate the md5sum of a file
1123 if (args
[1] == "md5sum" && args
.size() >= 3) {
1124 return HashSumFile(args
, cmCryptoHash::AlgoMD5
);
1127 // Command to calculate the sha1sum of a file
1128 if (args
[1] == "sha1sum" && args
.size() >= 3) {
1129 return HashSumFile(args
, cmCryptoHash::AlgoSHA1
);
1132 if (args
[1] == "sha224sum" && args
.size() >= 3) {
1133 return HashSumFile(args
, cmCryptoHash::AlgoSHA224
);
1136 if (args
[1] == "sha256sum" && args
.size() >= 3) {
1137 return HashSumFile(args
, cmCryptoHash::AlgoSHA256
);
1140 if (args
[1] == "sha384sum" && args
.size() >= 3) {
1141 return HashSumFile(args
, cmCryptoHash::AlgoSHA384
);
1144 if (args
[1] == "sha512sum" && args
.size() >= 3) {
1145 return HashSumFile(args
, cmCryptoHash::AlgoSHA512
);
1148 // Command to concat files into one
1149 if (args
[1] == "cat" && args
.size() >= 3) {
1150 int return_value
= 0;
1151 bool doing_options
= true;
1152 for (auto const& arg
: cmMakeRange(args
).advance(2)) {
1154 doing_options
= false;
1155 // Destroy console buffers to drop cout/cerr encoding transform.
1158 } else if (doing_options
&& cmHasLiteralPrefix(arg
, "-")) {
1160 doing_options
= false;
1162 cmSystemTools::Error(arg
+ ": option not handled");
1165 } else if (!cmSystemTools::TestFileAccess(arg
,
1166 cmsys::TEST_FILE_READ
) &&
1167 cmSystemTools::TestFileAccess(arg
, cmsys::TEST_FILE_OK
)) {
1168 cmSystemTools::Error(arg
+ ": permission denied (ignoring)");
1170 } else if (cmSystemTools::FileIsDirectory(arg
)) {
1171 cmSystemTools::Error(arg
+ ": is a directory (ignoring)");
1173 } else if (!cmSystemTools::FileExists(arg
)) {
1174 cmSystemTools::Error(arg
+ ": no such file or directory (ignoring)");
1176 } else if (cmSystemTools::FileLength(arg
) == 0) {
1177 // Ignore empty files, this is not an error
1179 // Destroy console buffers to drop cout/cerr encoding transform.
1184 return return_value
;
1187 // Command to change directory and run a program.
1188 if (args
[1] == "chdir" && args
.size() >= 4) {
1189 std::string
const& directory
= args
[2];
1190 if (!cmSystemTools::FileExists(directory
)) {
1191 cmSystemTools::Error("Directory does not exist for chdir command: " +
1196 std::string command
=
1197 cmWrap('"', cmMakeRange(args
).advance(3), '"', " ");
1199 if (cmSystemTools::RunSingleCommand(
1200 command
, nullptr, nullptr, &retval
, directory
.c_str(),
1201 cmSystemTools::OUTPUT_PASSTHROUGH
, cmDuration::zero())) {
1208 // Command to start progress for a build
1209 if (args
[1] == "cmake_progress_start" && args
.size() == 4) {
1210 // basically remove the directory
1211 std::string dirName
= cmStrCat(args
[2], "/Progress");
1212 cmSystemTools::RemoveADirectory(dirName
);
1214 // is the last argument a filename that exists?
1215 FILE* countFile
= cmsys::SystemTools::Fopen(args
[3], "r");
1218 if (1 != fscanf(countFile
, "%i", &count
)) {
1219 std::cerr
<< "Could not read from count file.\n";
1223 count
= atoi(args
[3].c_str());
1226 cmSystemTools::MakeDirectory(dirName
);
1227 // write the count into the directory
1228 std::string fName
= cmStrCat(dirName
, "/count.txt");
1229 FILE* progFile
= cmsys::SystemTools::Fopen(fName
, "w");
1231 fprintf(progFile
, "%i\n", count
);
1238 // Command to report progress for a build
1239 if (args
[1] == "cmake_progress_report" && args
.size() >= 3) {
1240 // This has been superseded by cmake_echo_color --progress-*
1241 // options. We leave it here to avoid errors if somehow this
1242 // is invoked by an existing makefile without regenerating.
1246 // Command to create a symbolic link. Fails on platforms not
1248 if (args
[1] == "create_symlink" && args
.size() == 4) {
1249 std::string
const& destinationFileName
= args
[3];
1250 if (cmSystemTools::PathExists(destinationFileName
) &&
1251 !cmSystemTools::RemoveFile(destinationFileName
)) {
1252 std::string emsg
= cmSystemTools::GetLastSystemError();
1253 std::cerr
<< "failed to create symbolic link '" << destinationFileName
1254 << "' because existing path cannot be removed: " << emsg
1258 if (!cmSystemTools::CreateSymlink(args
[2], destinationFileName
)) {
1264 // Command to create a hard link. Fails on platforms not
1266 if (args
[1] == "create_hardlink" && args
.size() == 4) {
1267 std::string
const& sourceFileName
= args
[2];
1268 std::string
const& destinationFileName
= args
[3];
1270 if (!cmSystemTools::FileExists(sourceFileName
)) {
1271 std::cerr
<< "failed to create hard link because source path '"
1272 << sourceFileName
<< "' does not exist \n";
1276 if (cmSystemTools::PathExists(destinationFileName
) &&
1277 !cmSystemTools::RemoveFile(destinationFileName
)) {
1278 std::string emsg
= cmSystemTools::GetLastSystemError();
1279 std::cerr
<< "failed to create hard link '" << destinationFileName
1280 << "' because existing path cannot be removed: " << emsg
1285 if (!cmSystemTools::CreateLink(sourceFileName
, destinationFileName
)) {
1291 // Command to do nothing with an exit code of 0.
1292 if (args
[1] == "true") {
1296 // Command to do nothing with an exit code of 1.
1297 if (args
[1] == "false") {
1301 // Internal CMake shared library support.
1302 if (args
[1] == "cmake_symlink_library" && args
.size() == 5) {
1303 return cmcmd::SymlinkLibrary(args
);
1306 // Internal CMake versioned executable support.
1307 if (args
[1] == "cmake_symlink_executable" && args
.size() == 4) {
1308 return cmcmd::SymlinkExecutable(args
);
1311 // Internal CMake dependency scanning support.
1312 if (args
[1] == "cmake_depends" && args
.size() >= 6) {
1313 const bool verbose
= isCMakeVerbose();
1315 // Create a cmake object instance to process dependencies.
1316 // All we need is the `set` command.
1317 cmake
cm(cmake::RoleScript
, cmState::Unknown
);
1319 std::string homeDir
;
1320 std::string startDir
;
1321 std::string homeOutDir
;
1322 std::string startOutDir
;
1323 std::string depInfo
;
1325 if (args
.size() >= 8) {
1328 // -E cmake_depends <generator>
1329 // <home-src-dir> <start-src-dir>
1330 // <home-out-dir> <start-out-dir>
1331 // <dep-info> [--color=$(COLOR)]
1333 // All paths are provided.
1337 homeOutDir
= args
[5];
1338 startOutDir
= args
[6];
1340 if (args
.size() >= 9 && cmHasLiteralPrefix(args
[8], "--color=")) {
1341 // Enable or disable color based on the switch value.
1342 color
= (args
[8].size() == 8 || cmIsOn(args
[8].substr(8)));
1345 // Support older signature for existing makefiles:
1347 // -E cmake_depends <generator>
1348 // <home-out-dir> <start-out-dir>
1351 // Just pretend the source directories are the same as the
1352 // binary directories so at least scanning will work.
1356 homeOutDir
= args
[3];
1357 startOutDir
= args
[3];
1361 // Create a local generator configured for the directory in
1362 // which dependencies will be scanned.
1363 homeDir
= cmSystemTools::CollapseFullPath(homeDir
);
1364 startDir
= cmSystemTools::CollapseFullPath(startDir
);
1365 homeOutDir
= cmSystemTools::CollapseFullPath(homeOutDir
);
1366 startOutDir
= cmSystemTools::CollapseFullPath(startOutDir
);
1367 cm
.SetHomeDirectory(homeDir
);
1368 cm
.SetHomeOutputDirectory(homeOutDir
);
1369 cm
.GetCurrentSnapshot().SetDefaultDefinitions();
1370 if (auto ggd
= cm
.CreateGlobalGenerator(gen
)) {
1371 cm
.SetGlobalGenerator(std::move(ggd
));
1372 cmStateSnapshot snapshot
= cm
.GetCurrentSnapshot();
1373 snapshot
.GetDirectory().SetCurrentBinary(startOutDir
);
1374 snapshot
.GetDirectory().SetCurrentSource(startDir
);
1375 cmMakefile
mf(cm
.GetGlobalGenerator(), snapshot
);
1376 auto lgd
= cm
.GetGlobalGenerator()->CreateLocalGenerator(&mf
);
1378 // FIXME: With advanced add_subdirectory usage, these are
1379 // not necessarily the same as the generator originally used.
1380 // We should pass all these directories through an info file.
1381 lgd
->SetRelativePathTop(homeDir
, homeOutDir
);
1383 // Actually scan dependencies.
1384 return lgd
->UpdateDependencies(depInfo
, verbose
, color
) ? 0 : 2;
1389 #if !defined(CMAKE_BOOTSTRAP) || defined(CMAKE_BOOTSTRAP_MAKEFILES)
1390 // Internal CMake compiler dependencies filtering
1391 if (args
[1] == "cmake_cl_compile_depends") {
1392 return CLCompileAndDependencies(args
);
1396 // Internal CMake link script support.
1397 if (args
[1] == "cmake_link_script" && args
.size() >= 3) {
1398 return cmcmd::ExecuteLinkScript(args
);
1401 #if !defined(CMAKE_BOOTSTRAP)
1402 // Internal CMake ninja dependency scanning support.
1403 if (args
[1] == "cmake_ninja_depends") {
1404 return cmcmd_cmake_ninja_depends(args
.begin() + 2, args
.end());
1407 // Internal CMake ninja dyndep support.
1408 if (args
[1] == "cmake_ninja_dyndep") {
1409 return cmcmd_cmake_ninja_dyndep(args
.begin() + 2, args
.end());
1413 // Internal CMake unimplemented feature notification.
1414 if (args
[1] == "cmake_unimplemented_variable") {
1415 std::cerr
<< "Feature not implemented for this platform.";
1416 if (args
.size() == 3) {
1417 std::cerr
<< " Variable " << args
[2] << " is not set.";
1419 std::cerr
<< std::endl
;
1423 if (args
[1] == "vs_link_exe") {
1424 return cmcmd::VisualStudioLink(args
, 1);
1427 if (args
[1] == "vs_link_dll") {
1428 return cmcmd::VisualStudioLink(args
, 2);
1431 if (args
[1] == "cmake_llvm_rc") {
1432 return cmcmd::RunLLVMRC(args
);
1435 // Internal CMake color makefile support.
1436 if (args
[1] == "cmake_echo_color") {
1437 return cmcmd::ExecuteEchoColor(args
);
1440 #ifndef CMAKE_BOOTSTRAP
1441 if ((args
[1] == "cmake_autogen") && (args
.size() >= 4)) {
1442 cm::string_view
const infoFile
= args
[2];
1443 cm::string_view
const config
= args
[3];
1444 cm::string_view
const executableConfig
=
1445 (args
.size() >= 5) ? cm::string_view(args
[4]) : cm::string_view();
1446 return cmQtAutoMocUic(infoFile
, config
, executableConfig
) ? 0 : 1;
1448 if ((args
[1] == "cmake_autorcc") && (args
.size() >= 3)) {
1449 cm::string_view
const infoFile
= args
[2];
1450 cm::string_view
const config
=
1451 (args
.size() > 3) ? cm::string_view(args
[3]) : cm::string_view();
1452 cm::string_view
const executableConfig
=
1453 (args
.size() >= 5) ? cm::string_view(args
[4]) : cm::string_view();
1454 return cmQtAutoRcc(infoFile
, config
, executableConfig
) ? 0 : 1;
1459 if (args
[1] == "tar" && args
.size() > 3) {
1460 const char* knownFormats
[] = { "7zip", "gnutar", "pax", "paxr", "zip" };
1462 std::string
const& flags
= args
[2];
1463 std::string
const& outFile
= args
[3];
1464 std::vector
<std::string
> files
;
1467 cmSystemTools::cmTarExtractTimestamps extractTimestamps
=
1468 cmSystemTools::cmTarExtractTimestamps::Yes
;
1469 cmSystemTools::cmTarCompression compress
=
1470 cmSystemTools::TarCompressNone
;
1472 bool doing_options
= true;
1473 for (auto const& arg
: cmMakeRange(args
).advance(4)) {
1474 if (doing_options
&& cmHasLiteralPrefix(arg
, "--")) {
1476 doing_options
= false;
1477 } else if (arg
== "--zstd") {
1478 compress
= cmSystemTools::TarCompressZstd
;
1480 } else if (cmHasLiteralPrefix(arg
, "--mtime=")) {
1481 mtime
= arg
.substr(8);
1482 } else if (cmHasLiteralPrefix(arg
, "--files-from=")) {
1483 std::string
const& files_from
= arg
.substr(13);
1484 if (!cmTarFilesFrom(files_from
, files
)) {
1487 } else if (cmHasLiteralPrefix(arg
, "--format=")) {
1488 format
= arg
.substr(9);
1489 if (!cm::contains(knownFormats
, format
)) {
1490 cmSystemTools::Error("Unknown -E tar --format= argument: " +
1494 } else if (arg
== "--touch") {
1495 extractTimestamps
= cmSystemTools::cmTarExtractTimestamps::No
;
1497 cmSystemTools::Error("Unknown option to -E tar: " + arg
);
1501 files
.push_back(arg
);
1504 cmSystemTools::cmTarAction action
= cmSystemTools::TarActionNone
;
1505 bool verbose
= false;
1507 for (auto flag
: flags
) {
1511 // Keep for backward compatibility. Ignored
1514 compress
= cmSystemTools::TarCompressBZip2
;
1518 compress
= cmSystemTools::TarCompressXZ
;
1522 compress
= cmSystemTools::TarCompressGZip
;
1529 action
= cmSystemTools::TarActionList
;
1532 action
= cmSystemTools::TarActionCreate
;
1535 action
= cmSystemTools::TarActionExtract
;
1538 std::cerr
<< "tar: Unknown argument: " << flag
<< "\n";
1542 if ((format
== "7zip" || format
== "zip") && nCompress
> 0) {
1543 cmSystemTools::Error("Can not use compression flags with format: " +
1547 if (nCompress
> 1) {
1548 cmSystemTools::Error("Can only compress a tar file one way; "
1549 "at most one flag of z, j, or J may be used");
1552 if (action
== cmSystemTools::TarActionList
) {
1553 if (!cmSystemTools::ListTar(outFile
, files
, verbose
)) {
1554 cmSystemTools::Error("Problem listing tar: " + outFile
);
1557 } else if (action
== cmSystemTools::TarActionCreate
) {
1558 if (files
.empty()) {
1559 std::cerr
<< "tar: No files or directories specified\n";
1561 if (!cmSystemTools::CreateTar(outFile
, files
, compress
, verbose
, mtime
,
1563 cmSystemTools::Error("Problem creating tar: " + outFile
);
1566 } else if (action
== cmSystemTools::TarActionExtract
) {
1567 if (!cmSystemTools::ExtractTar(outFile
, files
, extractTimestamps
,
1569 cmSystemTools::Error("Problem extracting tar: " + outFile
);
1573 // OK, on windows 7 after we untar some files,
1574 // sometimes we can not rename the directory after
1575 // the untar is done. This breaks the external project
1576 // untar and rename code. So, by default we will wait
1577 // 1/10th of a second after the untar. If CMAKE_UNTAR_DELAY
1578 // is set in the env, its value will be used instead of 100.
1580 std::string delayVar
;
1581 if (cmSystemTools::GetEnv("CMAKE_UNTAR_DELAY", delayVar
)) {
1582 delay
= atoi(delayVar
.c_str());
1585 cmSystemTools::Delay(delay
);
1589 cmSystemTools::Error("tar: No action specified. Please choose: 't' "
1590 "(list), 'c' (create) or 'x' (extract)");
1596 if (args
[1] == "server") {
1597 cmSystemTools::Error(
1598 "CMake server mode has been removed in favor of the file-api.");
1602 #if !defined(CMAKE_BOOTSTRAP)
1603 // Internal CMake Fortran module support.
1604 if (args
[1] == "cmake_copy_f90_mod" && args
.size() >= 4) {
1605 return cmDependsFortran::CopyModule(args
) ? 0 : 1;
1609 #if defined(_WIN32) && !defined(__CYGWIN__)
1610 // Write registry value
1611 if (args
[1] == "write_regv" && args
.size() > 3) {
1612 return cmSystemTools::WriteRegistryValue(args
[2], args
[3]) ? 0 : 1;
1615 // Delete registry value
1616 if (args
[1] == "delete_regv" && args
.size() > 2) {
1617 return cmSystemTools::DeleteRegistryValue(args
[2]) ? 0 : 1;
1621 if (args
[1] == "comspec" && args
.size() > 2) {
1622 std::cerr
<< "Win9x helper \"cmake -E comspec\" no longer supported\n";
1626 if (args
[1] == "env_vs8_wince" && args
.size() == 3) {
1627 return cmcmd::WindowsCEEnvironment("8.0", args
[2]);
1630 if (args
[1] == "env_vs9_wince" && args
.size() == 3) {
1631 return cmcmd::WindowsCEEnvironment("9.0", args
[2]);
1635 // Internal depfile transformation
1636 if (args
[1] == "cmake_transform_depfile" && args
.size() == 10) {
1637 auto format
= cmDepfileFormat::GccDepfile
;
1638 if (args
[3] == "gccdepfile") {
1639 format
= cmDepfileFormat::GccDepfile
;
1640 } else if (args
[3] == "makedepfile") {
1641 format
= cmDepfileFormat::MakeDepfile
;
1642 } else if (args
[3] == "MSBuildAdditionalInputs") {
1643 format
= cmDepfileFormat::MSBuildAdditionalInputs
;
1647 // Create a cmake object instance to process dependencies.
1648 // All we need is the `set` command.
1649 cmake
cm(cmake::RoleScript
, cmState::Unknown
);
1650 std::string homeDir
;
1651 std::string startDir
;
1652 std::string homeOutDir
;
1653 std::string startOutDir
;
1654 homeDir
= cmSystemTools::CollapseFullPath(args
[4]);
1655 startDir
= cmSystemTools::CollapseFullPath(args
[5]);
1656 homeOutDir
= cmSystemTools::CollapseFullPath(args
[6]);
1657 startOutDir
= cmSystemTools::CollapseFullPath(args
[7]);
1658 cm
.SetHomeDirectory(homeDir
);
1659 cm
.SetHomeOutputDirectory(homeOutDir
);
1660 cm
.GetCurrentSnapshot().SetDefaultDefinitions();
1661 if (auto ggd
= cm
.CreateGlobalGenerator(args
[2])) {
1662 cm
.SetGlobalGenerator(std::move(ggd
));
1663 cmStateSnapshot snapshot
= cm
.GetCurrentSnapshot();
1664 snapshot
.GetDirectory().SetCurrentBinary(startOutDir
);
1665 snapshot
.GetDirectory().SetCurrentSource(startDir
);
1666 cmMakefile
mf(cm
.GetGlobalGenerator(), snapshot
);
1667 auto lgd
= cm
.GetGlobalGenerator()->CreateLocalGenerator(&mf
);
1669 // FIXME: With advanced add_subdirectory usage, these are
1670 // not necessarily the same as the generator originally used.
1671 // We should pass all these directories through an info file.
1672 lgd
->SetRelativePathTop(homeDir
, homeOutDir
);
1674 return cmTransformDepfile(format
, *lgd
, args
[8], args
[9]) ? 0 : 2;
1680 CMakeCommandUsage(args
[0]);
1684 int cmcmd::HashSumFile(std::vector
<std::string
> const& args
,
1685 cmCryptoHash::Algo algo
)
1687 if (args
.size() < 3) {
1692 for (auto const& filename
: cmMakeRange(args
).advance(2)) {
1693 // Cannot compute sum of a directory
1694 if (cmSystemTools::FileIsDirectory(filename
)) {
1695 std::cerr
<< "Error: " << filename
<< " is a directory" << std::endl
;
1698 cmCryptoHash
hasher(algo
);
1699 std::string value
= hasher
.HashFile(filename
);
1700 if (value
.empty()) {
1701 // To mimic "md5sum/shasum" behavior in a shell:
1702 std::cerr
<< filename
<< ": No such file or directory" << std::endl
;
1705 std::cout
<< value
<< " " << filename
<< std::endl
;
1712 int cmcmd::SymlinkLibrary(std::vector
<std::string
> const& args
)
1715 std::string realName
= args
[2];
1716 std::string soName
= args
[3];
1717 std::string name
= args
[4];
1718 cmSystemTools::ConvertToUnixSlashes(realName
);
1719 cmSystemTools::ConvertToUnixSlashes(soName
);
1720 cmSystemTools::ConvertToUnixSlashes(name
);
1721 if (soName
!= realName
) {
1722 cmsys::Status status
= cmcmd::SymlinkInternal(realName
, soName
);
1724 cmSystemTools::Error(
1725 cmStrCat("cmake_symlink_library: System Error: ", status
.GetString()));
1729 if (name
!= soName
) {
1730 cmsys::Status status
= cmcmd::SymlinkInternal(soName
, name
);
1732 cmSystemTools::Error(
1733 cmStrCat("cmake_symlink_library: System Error: ", status
.GetString()));
1740 int cmcmd::SymlinkExecutable(std::vector
<std::string
> const& args
)
1743 std::string
const& realName
= args
[2];
1744 std::string
const& name
= args
[3];
1745 if (name
!= realName
) {
1746 cmsys::Status status
= cmcmd::SymlinkInternal(realName
, name
);
1748 cmSystemTools::Error(cmStrCat("cmake_symlink_executable: System Error: ",
1749 status
.GetString()));
1756 cmsys::Status
cmcmd::SymlinkInternal(std::string
const& file
,
1757 std::string
const& link
)
1759 if (cmSystemTools::PathExists(link
)) {
1760 cmSystemTools::RemoveFile(link
);
1762 std::string linktext
= cmSystemTools::GetFilenameName(file
);
1763 #if defined(_WIN32) && !defined(__CYGWIN__)
1764 cmsys::Status status
= cmSystemTools::CreateSymlinkQuietly(linktext
, link
);
1765 // Creating a symlink will fail with ERROR_PRIVILEGE_NOT_HELD if the user
1766 // does not have SeCreateSymbolicLinkPrivilege, or if developer mode is not
1767 // active. In that case, we try to copy the file.
1768 if (status
.GetWindows() == ERROR_PRIVILEGE_NOT_HELD
) {
1769 status
= cmSystemTools::CopyFileAlways(file
, link
);
1770 } else if (!status
) {
1771 cmSystemTools::Error(cmStrCat("failed to create symbolic link '", link
,
1772 "': ", status
.GetString()));
1776 return cmSystemTools::CreateSymlink(linktext
, link
);
1780 static void cmcmdProgressReport(std::string
const& dir
, std::string
const& num
)
1782 std::string dirName
= cmStrCat(dir
, "/Progress");
1787 fName
= cmStrCat(dirName
, "/count.txt");
1788 progFile
= cmsys::SystemTools::Fopen(fName
, "r");
1793 if (1 != fscanf(progFile
, "%i", &count
)) {
1794 std::cerr
<< "Could not read from progress file.\n";
1798 const char* last
= num
.c_str();
1799 for (const char* c
= last
;; ++c
) {
1800 if (*c
== ',' || *c
== '\0') {
1802 fName
= cmStrCat(dirName
, '/');
1803 fName
.append(last
, c
- last
);
1804 progFile
= cmsys::SystemTools::Fopen(fName
, "w");
1806 fprintf(progFile
, "empty");
1817 static_cast<int>(cmsys::Directory::GetNumberOfFilesInDirectory(dirName
));
1819 // print the progress
1820 fprintf(stdout
, "[%3i%%] ", ((fileNum
- 3) * 100) / count
);
1824 int cmcmd::ExecuteEchoColor(std::vector
<std::string
> const& args
)
1826 // The arguments are
1827 // args[0] == <cmake-executable>
1828 // args[1] == cmake_echo_color
1830 bool enabled
= true;
1831 int color
= cmsysTerminal_Color_Normal
;
1832 bool newline
= true;
1833 std::string progressDir
;
1834 for (auto const& arg
: cmMakeRange(args
).advance(2)) {
1835 if (cmHasLiteralPrefix(arg
, "--switch=")) {
1836 // Enable or disable color based on the switch value.
1837 std::string value
= arg
.substr(9);
1838 if (!value
.empty()) {
1839 enabled
= cmIsOn(value
);
1841 } else if (cmHasLiteralPrefix(arg
, "--progress-dir=")) {
1842 progressDir
= arg
.substr(15);
1843 } else if (cmHasLiteralPrefix(arg
, "--progress-num=")) {
1844 if (!progressDir
.empty()) {
1845 std::string
const& progressNum
= arg
.substr(15);
1846 cmcmdProgressReport(progressDir
, progressNum
);
1848 } else if (arg
== "--normal") {
1849 color
= cmsysTerminal_Color_Normal
;
1850 } else if (arg
== "--black") {
1851 color
= cmsysTerminal_Color_ForegroundBlack
;
1852 } else if (arg
== "--red") {
1853 color
= cmsysTerminal_Color_ForegroundRed
;
1854 } else if (arg
== "--green") {
1855 color
= cmsysTerminal_Color_ForegroundGreen
;
1856 } else if (arg
== "--yellow") {
1857 color
= cmsysTerminal_Color_ForegroundYellow
;
1858 } else if (arg
== "--blue") {
1859 color
= cmsysTerminal_Color_ForegroundBlue
;
1860 } else if (arg
== "--magenta") {
1861 color
= cmsysTerminal_Color_ForegroundMagenta
;
1862 } else if (arg
== "--cyan") {
1863 color
= cmsysTerminal_Color_ForegroundCyan
;
1864 } else if (arg
== "--white") {
1865 color
= cmsysTerminal_Color_ForegroundWhite
;
1866 } else if (arg
== "--bold") {
1867 color
|= cmsysTerminal_Color_ForegroundBold
;
1868 } else if (arg
== "--no-newline") {
1870 } else if (arg
== "--newline") {
1873 // Color is enabled. Print with the current color.
1874 cmSystemTools::MakefileColorEcho(color
, arg
.c_str(), newline
, enabled
);
1881 int cmcmd::ExecuteLinkScript(std::vector
<std::string
> const& args
)
1883 // The arguments are
1884 // args[0] == <cmake-executable>
1885 // args[1] == cmake_link_script
1886 // args[2] == <link-script-name>
1887 // args[3] == --verbose=?
1888 bool verbose
= false;
1889 if (args
.size() >= 4) {
1890 if (cmHasLiteralPrefix(args
[3], "--verbose=")) {
1891 if (!cmIsOff(args
[3].substr(10))) {
1897 // Read command lines from the script.
1898 cmsys::ifstream
fin(args
[2].c_str());
1900 std::cerr
<< "Error opening link script \"" << args
[2] << "\""
1905 // Run one command at a time.
1906 std::string command
;
1908 while (result
== 0 && cmSystemTools::GetLineFromStream(fin
, command
)) {
1909 // Skip empty command lines.
1910 if (command
.find_first_not_of(" \t") == std::string::npos
) {
1914 // Allocate a process instance.
1915 cmUVProcessChainBuilder builder
;
1917 // Children should share stdout and stderr with this process.
1918 builder
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT
, stdout
)
1919 .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR
, stderr
);
1921 // Setup this command line.
1922 std::vector
<std::string
> args2
;
1924 cmSystemTools::ParseWindowsCommandLine(command
.c_str(), args2
);
1926 cmSystemTools::ParseUnixCommandLine(command
.c_str(), args2
);
1928 builder
.AddCommand(args2
);
1930 // Report the command if verbose output is enabled.
1932 std::cout
<< command
<< std::endl
;
1935 // Run the command and wait for it to exit.
1936 auto chain
= builder
.Start();
1939 // Report failure if any.
1940 auto const& status
= chain
.GetStatus(0);
1941 auto exception
= status
.GetException();
1942 switch (exception
.first
) {
1943 case cmUVProcessChain::ExceptionCode::None
:
1944 if (status
.ExitStatus
!= 0) {
1945 result
= static_cast<int>(status
.ExitStatus
);
1948 case cmUVProcessChain::ExceptionCode::Spawn
:
1949 std::cerr
<< "Error running link command: " << exception
.second
;
1953 std::cerr
<< "Error running link command: " << exception
.second
;
1959 // Return the final resulting return value.
1963 int cmcmd::WindowsCEEnvironment(const char* version
, const std::string
& name
)
1965 #if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32) && !defined(__CYGWIN__)
1966 cmVisualStudioWCEPlatformParser
parser(name
.c_str());
1967 parser
.ParseVersion(version
);
1968 if (parser
.Found()) {
1969 /* clang-format off */
1970 std::cout
<< "@echo off\n"
1971 "echo Environment Selection: " << name
<< "\n"
1972 "set PATH=" << parser
.GetPathDirectories() << "\n"
1973 "set INCLUDE=" << parser
.GetIncludeDirectories() << "\n"
1974 "set LIB=" << parser
.GetLibraryDirectories() << std::endl
;
1975 /* clang-format on */
1982 std::cerr
<< "Could not find " << name
;
1986 int cmcmd::RunPreprocessor(const std::vector
<std::string
>& command
,
1987 const std::string
& intermediate_file
)
1989 cmUVProcessChainBuilder builder
;
1992 int preprocessedFile
=
1993 uv_fs_open(nullptr, &fs_req
, intermediate_file
.c_str(), O_CREAT
| O_RDWR
,
1995 uv_fs_req_cleanup(&fs_req
);
1998 .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT
,
2000 .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR
)
2001 .AddCommand(command
);
2002 auto process
= builder
.Start();
2003 if (!process
.Valid() || process
.GetStatus(0).SpawnResult
!= 0) {
2004 std::cerr
<< "Failed to start preprocessor.";
2007 if (!process
.Wait()) {
2008 std::cerr
<< "Failed to wait for preprocessor";
2011 if (process
.GetStatus(0).ExitStatus
!= 0) {
2012 cmUVPipeIStream
errorStream(process
.GetLoop(), process
.ErrorStream());
2013 std::cerr
<< errorStream
.rdbuf();
2020 int cmcmd::RunLLVMRC(std::vector
<std::string
> const& args
)
2022 // The arguments are
2023 // args[0] == <cmake-executable>
2024 // args[1] == cmake_llvm_rc
2025 // args[2] == source_file_path
2026 // args[3] == intermediate_file
2027 // args[4..n] == preprocess+args
2029 // args[n+2...] == llvm-rc+args
2030 if (args
.size() < 3) {
2031 std::cerr
<< "Invalid cmake_llvm_rc arguments";
2035 const std::string
& intermediate_file
= args
[3];
2036 const std::string
& source_file
= args
[2];
2037 std::vector
<std::string
> preprocess
;
2038 std::vector
<std::string
> resource_compile
;
2039 std::vector
<std::string
>* pArgTgt
= &preprocess
;
2041 static const cmsys::RegularExpression
llvm_rc_only_single_arg("^[-/](N|Y)");
2042 static const cmsys::RegularExpression
llvm_rc_only_double_arg(
2043 "^[-/](C|LN|L)(.)?");
2044 static const cmsys::RegularExpression
common_double_arg(
2045 "^[-/](D|U|I|FO|fo|Fo)(.)?");
2046 bool acceptNextArg
= false;
2047 bool skipNextArg
= false;
2048 for (std::string
const& arg
: cmMakeRange(args
).advance(4)) {
2050 skipNextArg
= false;
2053 // We use ++ as separator between the preprocessing step definition and
2054 // the rc compilation step because we need to prepend a -- to separate the
2055 // source file properly from other options when using clang-cl for
2058 pArgTgt
= &resource_compile
;
2059 skipNextArg
= false;
2060 acceptNextArg
= true;
2062 cmsys::RegularExpressionMatch match
;
2063 if (!acceptNextArg
) {
2064 if (common_double_arg
.find(arg
.c_str(), match
)) {
2065 acceptNextArg
= match
.match(2).empty();
2067 if (llvm_rc_only_single_arg
.find(arg
.c_str(), match
)) {
2068 if (pArgTgt
== &preprocess
) {
2071 } else if (llvm_rc_only_double_arg
.find(arg
.c_str(), match
)) {
2072 if (pArgTgt
== &preprocess
) {
2073 skipNextArg
= match
.match(2).empty();
2076 acceptNextArg
= match
.match(2).empty();
2077 } else if (pArgTgt
== &resource_compile
) {
2082 acceptNextArg
= false;
2084 if (arg
.find("SOURCE_DIR") != std::string::npos
) {
2085 std::string sourceDirArg
= arg
;
2086 cmSystemTools::ReplaceString(
2087 sourceDirArg
, "SOURCE_DIR",
2088 cmSystemTools::GetFilenamePath(source_file
));
2089 pArgTgt
->push_back(sourceDirArg
);
2091 pArgTgt
->push_back(arg
);
2095 if (preprocess
.empty()) {
2096 std::cerr
<< "Empty preprocessing command";
2099 if (resource_compile
.empty()) {
2100 std::cerr
<< "Empty resource compilation command";
2103 // Since we might have skipped the last argument to llvm-rc
2104 // we need to make sure the llvm-rc source file is present in the
2106 if (resource_compile
.back() != intermediate_file
) {
2107 resource_compile
.push_back(intermediate_file
);
2110 auto result
= RunPreprocessor(preprocess
, intermediate_file
);
2112 cmSystemTools::RemoveFile(intermediate_file
);
2115 cmUVProcessChainBuilder builder
;
2117 builder
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT
)
2118 .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR
)
2119 .AddCommand(resource_compile
);
2120 auto process
= builder
.Start();
2122 if (!process
.Valid() || process
.GetStatus(0).SpawnResult
!= 0) {
2123 std::cerr
<< "Failed to start resource compiler.";
2126 if (!process
.Wait()) {
2127 std::cerr
<< "Failed to wait for resource compiler";
2132 cmSystemTools::RemoveFile(intermediate_file
);
2136 if (process
.GetStatus(0).ExitStatus
!= 0) {
2137 cmUVPipeIStream
errorStream(process
.GetLoop(), process
.ErrorStream());
2138 std::cerr
<< errorStream
.rdbuf();
2149 bool Incremental
= false;
2150 bool LinkGeneratesManifest
= true;
2151 std::vector
<std::string
> LinkCommand
;
2152 std::vector
<std::string
> UserManifests
;
2153 std::string LinkerManifestFile
;
2154 std::string ManifestFile
;
2155 std::string ManifestFileRC
;
2156 std::string ManifestFileRes
;
2157 std::string TargetFile
;
2162 cmVSLink(int type
, bool verbose
)
2167 bool Parse(std::vector
<std::string
>::const_iterator argBeg
,
2168 std::vector
<std::string
>::const_iterator argEnd
);
2172 int LinkIncremental();
2173 int LinkNonIncremental();
2174 int RunMT(std::string
const& out
, bool notify
);
2177 // For visual studio 2005 and newer manifest files need to be embedded into
2178 // exe and dll's. This code does that in such a way that incremental linking
2180 int cmcmd::VisualStudioLink(std::vector
<std::string
> const& args
, int type
)
2182 // Replace streambuf so we output in the system codepage. CMake is set up
2183 // to output in Unicode (see SetUTF8Pipes) but the Visual Studio linker
2184 // outputs using the system codepage so we need to change behavior when
2185 // we run the link command.
2186 cmConsoleBuf consoleBuf
;
2188 if (args
.size() < 2) {
2191 const bool verbose
= cmSystemTools::HasEnv("VERBOSE");
2192 std::vector
<std::string
> expandedArgs
;
2193 for (std::string
const& i
: args
) {
2194 // check for nmake temporary files
2195 if (i
[0] == '@' && !cmHasLiteralPrefix(i
, "@CMakeFiles")) {
2196 cmsys::ifstream
fin(i
.substr(1).c_str());
2198 while (cmSystemTools::GetLineFromStream(fin
, line
)) {
2199 cmSystemTools::ParseWindowsCommandLine(line
.c_str(), expandedArgs
);
2202 expandedArgs
.push_back(i
);
2206 cmVSLink
vsLink(type
, verbose
);
2207 if (!vsLink
.Parse(expandedArgs
.begin() + 2, expandedArgs
.end())) {
2210 return vsLink
.Link();
2218 struct NumberFormatter
2220 NumberFormat Format
;
2222 NumberFormatter(NumberFormat format
, int value
)
2228 static std::ostream
& operator<<(std::ostream
& stream
,
2229 NumberFormatter
const& formatter
)
2231 auto const& flags
= stream
.flags();
2232 if (formatter
.Format
== FORMAT_DECIMAL
) {
2233 stream
<< std::dec
<< formatter
.Value
;
2235 stream
<< "0x" << std::hex
<< formatter
.Value
;
2237 stream
.flags(flags
);
2241 static bool RunCommand(const char* comment
,
2242 std::vector
<std::string
> const& command
, bool verbose
,
2243 NumberFormat exitFormat
, int* retCodeOut
= nullptr,
2244 bool (*retCodeOkay
)(int) = nullptr)
2247 std::cout
<< comment
<< ":\n";
2248 std::cout
<< cmJoin(command
, " ") << "\n";
2252 bool commandResult
= cmSystemTools::RunSingleCommand(
2253 command
, &output
, &output
, &retCode
, nullptr, cmSystemTools::OUTPUT_NONE
);
2254 bool const retCodeSuccess
=
2255 retCode
== 0 || (retCodeOkay
&& retCodeOkay(retCode
));
2256 bool const success
= commandResult
&& retCodeSuccess
;
2258 if (commandResult
|| !retCodeSuccess
) {
2259 *retCodeOut
= retCode
;
2265 std::cout
<< comment
<< ": command \"" << cmJoin(command
, " ")
2266 << "\" failed (exit code "
2267 << NumberFormatter(exitFormat
, retCode
)
2268 << ") with the following output:\n"
2270 } else if (verbose
) {
2271 // always print the output of the command, unless
2272 // it is the dumb rc command banner
2273 if (output
.find("Resource Compiler Version") == std::string::npos
) {
2274 std::cout
<< output
;
2280 bool cmVSLink::Parse(std::vector
<std::string
>::const_iterator argBeg
,
2281 std::vector
<std::string
>::const_iterator argEnd
)
2283 // Parse our own arguments.
2286 while (arg
!= argEnd
&& cmHasLiteralPrefix(*arg
, "-")) {
2291 if (*arg
== "--manifests") {
2292 for (++arg
; arg
!= argEnd
&& !cmHasLiteralPrefix(*arg
, "-"); ++arg
) {
2293 this->UserManifests
.push_back(*arg
);
2295 } else if (cmHasLiteralPrefix(*arg
, "--intdir=")) {
2296 intDir
= arg
->substr(9);
2298 } else if (cmHasLiteralPrefix(*arg
, "--rc=")) {
2299 this->RcPath
= arg
->substr(5);
2301 } else if (cmHasLiteralPrefix(*arg
, "--mt=")) {
2302 this->MtPath
= arg
->substr(5);
2305 std::cerr
<< "unknown argument '" << *arg
<< "'\n";
2309 if (intDir
.empty()) {
2313 // The rest of the arguments form the link command.
2314 if (arg
== argEnd
) {
2317 this->LinkCommand
.insert(this->LinkCommand
.begin(), arg
, argEnd
);
2319 // Parse the link command to extract information we need.
2320 for (; arg
!= argEnd
; ++arg
) {
2321 if (cmSystemTools::Strucmp(arg
->c_str(), "/INCREMENTAL:YES") == 0 ||
2322 cmSystemTools::Strucmp(arg
->c_str(), "-INCREMENTAL:YES") == 0 ||
2323 cmSystemTools::Strucmp(arg
->c_str(), "/INCREMENTAL") == 0 ||
2324 cmSystemTools::Strucmp(arg
->c_str(), "-INCREMENTAL") == 0) {
2325 this->Incremental
= true;
2326 } else if (cmSystemTools::Strucmp(arg
->c_str(), "/INCREMENTAL:NO") == 0 ||
2327 cmSystemTools::Strucmp(arg
->c_str(), "-INCREMENTAL:NO") == 0) {
2328 this->Incremental
= false;
2329 } else if (cmSystemTools::Strucmp(arg
->c_str(), "/MANIFEST:NO") == 0 ||
2330 cmSystemTools::Strucmp(arg
->c_str(), "-MANIFEST:NO") == 0) {
2331 this->LinkGeneratesManifest
= false;
2332 } else if (cmHasLiteralPrefix(*arg
, "/Fe") ||
2333 cmHasLiteralPrefix(*arg
, "-Fe")) {
2334 this->TargetFile
= arg
->substr(3);
2335 } else if (cmHasLiteralPrefix(*arg
, "/out:") ||
2336 cmHasLiteralPrefix(*arg
, "-out:")) {
2337 this->TargetFile
= arg
->substr(5);
2341 if (this->TargetFile
.empty()) {
2345 this->ManifestFile
= intDir
+ "/embed.manifest";
2346 this->LinkerManifestFile
= intDir
+ "/intermediate.manifest";
2348 if (this->Incremental
) {
2349 // We will compile a resource containing the manifest and
2350 // pass it to the link command.
2351 this->ManifestFileRC
= intDir
+ "/manifest.rc";
2352 this->ManifestFileRes
= intDir
+ "/manifest.res";
2354 if (this->LinkGeneratesManifest
) {
2355 this->LinkCommand
.emplace_back("/MANIFEST");
2356 this->LinkCommand
.push_back("/MANIFESTFILE:" + this->LinkerManifestFile
);
2363 int cmVSLink::Link()
2365 if (this->Incremental
&&
2366 (this->LinkGeneratesManifest
|| !this->UserManifests
.empty())) {
2367 if (this->Verbose
) {
2368 std::cout
<< "Visual Studio Incremental Link with embedded manifests\n";
2370 return this->LinkIncremental();
2372 if (this->Verbose
) {
2373 if (!this->Incremental
) {
2374 std::cout
<< "Visual Studio Non-Incremental Link\n";
2376 std::cout
<< "Visual Studio Incremental Link without manifests\n";
2379 return this->LinkNonIncremental();
2382 static bool mtRetIsUpdate(int mtRet
)
2384 // 'mt /notify_update' returns a special value (differing between
2385 // Windows and POSIX hosts) when it updated the manifest file.
2386 return mtRet
== 0x41020001 || mtRet
== 0xbb;
2389 int cmVSLink::LinkIncremental()
2391 // This follows the steps listed here:
2392 // http://blogs.msdn.com/zakramer/archive/2006/05/22/603558.aspx
2394 // 1. Compiler compiles the application and generates the *.obj files.
2395 // 2. An empty manifest file is generated if this is a clean build and
2396 // if not the previous one is reused.
2397 // 3. The resource compiler (rc.exe) compiles the *.manifest file to a
2399 // 4. Linker generates the binary (EXE or DLL) with the /incremental
2400 // switch and embeds the dummy manifest file. The linker also generates
2401 // the real manifest file based on the binaries that your binary depends
2403 // 5. The manifest tool (mt.exe) is then used to generate the final
2406 // If the final manifest is changed, then 6 and 7 are run, if not
2407 // they are skipped, and it is done.
2409 // 6. The resource compiler is invoked one more time.
2410 // 7. Finally, the Linker does another incremental link, but since the
2411 // only thing that has changed is the *.res file that contains the
2412 // manifest it is a short link.
2414 // Create a resource file referencing the manifest.
2415 std::string absManifestFile
=
2416 cmSystemTools::CollapseFullPath(this->ManifestFile
);
2417 if (this->Verbose
) {
2418 std::cout
<< "Create " << this->ManifestFileRC
<< "\n";
2421 cmsys::ofstream
fout(this->ManifestFileRC
.c_str());
2425 // Insert a pragma statement to specify utf-8 encoding.
2426 fout
<< "#pragma code_page(65001)\n";
2428 << " /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ "
2429 "24 /* RT_MANIFEST */ \""
2430 << absManifestFile
<< "\"";
2433 // If we have not previously generated a manifest file,
2434 // generate a manifest file so the resource compiler succeeds.
2435 if (!cmSystemTools::FileExists(this->ManifestFile
)) {
2436 if (this->Verbose
) {
2437 std::cout
<< "Create empty: " << this->ManifestFile
<< "\n";
2439 if (this->UserManifests
.empty()) {
2440 // generate an empty manifest because there are no user provided
2442 cmsys::ofstream
foutTmp(this->ManifestFile
.c_str());
2444 this->RunMT("/out:" + this->ManifestFile
, false);
2448 // Compile the resource file.
2449 std::vector
<std::string
> rcCommand
;
2450 rcCommand
.push_back(this->RcPath
.empty() ? "rc" : this->RcPath
);
2451 rcCommand
.emplace_back("/fo");
2452 rcCommand
.push_back(this->ManifestFileRes
);
2453 rcCommand
.push_back(this->ManifestFileRC
);
2454 if (!RunCommand("RC Pass 1", rcCommand
, this->Verbose
, FORMAT_DECIMAL
)) {
2458 // Tell the linker to use our manifest compiled into a resource.
2459 this->LinkCommand
.push_back(this->ManifestFileRes
);
2461 // Run the link command (possibly generates intermediate manifest).
2462 if (!RunCommand("LINK Pass 1", this->LinkCommand
, this->Verbose
,
2467 // Run the manifest tool to create the final manifest.
2468 int mtRet
= this->RunMT("/out:" + this->ManifestFile
, true);
2470 // If mt returns a special value then it updated the manifest file so
2471 // we need to embed it again. Otherwise we are done.
2472 if (!mtRetIsUpdate(mtRet
)) {
2476 // Compile the resource file again.
2477 if (!RunCommand("RC Pass 2", rcCommand
, this->Verbose
, FORMAT_DECIMAL
)) {
2481 // Link incrementally again to use the updated resource.
2482 if (!RunCommand("FINAL LINK", this->LinkCommand
, this->Verbose
,
2489 int cmVSLink::LinkNonIncremental()
2491 // The MSVC link tool expects 'rc' to be in the PATH if it needs to embed
2492 // manifests, but the user might explicitly set 'CMAKE_RC_COMPILER' instead.
2493 // Add its location as a fallback at the end of PATH.
2494 if (cmSystemTools::FileIsFullPath(this->RcPath
)) {
2495 std::string rcDir
= cmSystemTools::GetFilenamePath(this->RcPath
);
2497 std::replace(rcDir
.begin(), rcDir
.end(), '/', '\\');
2498 char const pathSep
= ';';
2500 char const pathSep
= ':';
2502 cm::optional
<std::string
> path
= cmSystemTools::GetEnvVar("PATH");
2504 path
= cmStrCat(*path
, pathSep
, rcDir
);
2508 cmSystemTools::PutEnv(cmStrCat("PATH=", *path
));
2511 // Sort out any manifests.
2512 if (this->LinkGeneratesManifest
|| !this->UserManifests
.empty()) {
2514 std::string("/MANIFEST:EMBED,ID=") + (this->Type
== 1 ? '1' : '2');
2515 this->LinkCommand
.emplace_back(opt
);
2517 for (auto const& m
: this->UserManifests
) {
2518 opt
= "/MANIFESTINPUT:" + m
;
2519 this->LinkCommand
.emplace_back(opt
);
2523 // Run the link command.
2524 if (!RunCommand("LINK", this->LinkCommand
, this->Verbose
, FORMAT_DECIMAL
)) {
2530 int cmVSLink::RunMT(std::string
const& out
, bool notify
)
2532 std::vector
<std::string
> mtCommand
;
2533 mtCommand
.push_back(this->MtPath
.empty() ? "mt" : this->MtPath
);
2534 mtCommand
.emplace_back("/nologo");
2536 // add the linker generated manifest if the file exists.
2537 if (this->LinkGeneratesManifest
&&
2538 cmSystemTools::FileExists(this->LinkerManifestFile
)) {
2539 mtCommand
.emplace_back("/manifest");
2540 mtCommand
.push_back(this->LinkerManifestFile
);
2542 for (auto const& m
: this->UserManifests
) {
2543 mtCommand
.emplace_back("/manifest");
2544 mtCommand
.push_back(m
);
2546 mtCommand
.push_back(out
);
2548 // Add an undocumented option that enables a special return
2549 // code to notify us when the manifest is modified.
2550 mtCommand
.emplace_back("/notify_update");
2553 if (!RunCommand("MT", mtCommand
, this->Verbose
, FORMAT_HEX
, &mtRet
,