CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / cmSystemTools.cxx
blob093a18b82bd5e3eee1347cff452f375c20fd74e2
1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
4 #if !defined(_WIN32) && !defined(__sun) && !defined(__OpenBSD__)
5 // POSIX APIs are needed
6 // NOLINTNEXTLINE(bugprone-reserved-identifier)
7 # define _POSIX_C_SOURCE 200809L
8 #endif
9 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || \
10 defined(__QNX__)
11 // For isascii
12 // NOLINTNEXTLINE(bugprone-reserved-identifier)
13 # define _XOPEN_SOURCE 700
14 #endif
15 #if defined(__APPLE__)
16 // Restore Darwin APIs removed by _POSIX_C_SOURCE.
17 // NOLINTNEXTLINE(bugprone-reserved-identifier)
18 # define _DARWIN_C_SOURCE
19 #endif
21 #include "cmSystemTools.h"
23 #include <cm/optional>
24 #include <cmext/algorithm>
25 #include <cmext/string_view>
27 #include <cm3p/uv.h>
29 #include "cmDuration.h"
30 #include "cmELF.h"
31 #include "cmMessageMetadata.h"
32 #include "cmProcessOutput.h"
33 #include "cmRange.h"
34 #include "cmStringAlgorithms.h"
35 #include "cmUVHandlePtr.h"
36 #include "cmUVProcessChain.h"
37 #include "cmUVStream.h"
38 #include "cmValue.h"
40 #if !defined(CMAKE_BOOTSTRAP)
41 # include <cm3p/archive.h>
42 # include <cm3p/archive_entry.h>
44 # include "cmArchiveWrite.h"
45 # include "cmLocale.h"
46 # ifndef __LA_INT64_T
47 # define __LA_INT64_T la_int64_t
48 # endif
49 # ifndef __LA_SSIZE_T
50 # define __LA_SSIZE_T la_ssize_t
51 # endif
52 #endif
54 #if defined(CMake_USE_MACH_PARSER)
55 # include "cmMachO.h"
56 #endif
58 #if defined(CMake_USE_XCOFF_PARSER)
59 # include "cmXCOFF.h"
60 #endif
62 #include <algorithm>
63 #include <cassert>
64 #include <cctype>
65 #include <cerrno>
66 #include <cstdint>
67 #include <cstdio>
68 #include <cstdlib>
69 #include <cstring>
70 #include <ctime>
71 #include <functional>
72 #include <iostream>
73 #include <memory>
74 #include <sstream>
75 #include <utility>
76 #include <vector>
78 #include <fcntl.h>
80 #include "cmsys/Directory.hxx"
81 #include "cmsys/Encoding.hxx"
82 #include "cmsys/FStream.hxx"
83 #include "cmsys/RegularExpression.hxx"
84 #include "cmsys/System.h"
85 #include "cmsys/Terminal.h"
87 #if defined(_WIN32)
88 # include <windows.h>
89 // include wincrypt.h after windows.h
90 # include <wincrypt.h>
91 #else
92 # include <unistd.h>
94 # include <sys/time.h>
95 #endif
97 #if defined(_WIN32) && \
98 (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__MINGW32__))
99 # include <io.h>
100 #endif
102 #if defined(__APPLE__)
103 # include <mach-o/dyld.h>
104 #endif
106 #ifdef __QNX__
107 # include <malloc.h> /* for malloc/free on QNX */
108 #endif
110 #if !defined(_WIN32) && !defined(__ANDROID__)
111 # include <sys/utsname.h>
112 #endif
114 #if defined(_MSC_VER) && _MSC_VER >= 1800
115 # define CM_WINDOWS_DEPRECATED_GetVersionEx
116 #endif
118 namespace {
120 cmSystemTools::InterruptCallback s_InterruptCallback;
121 cmSystemTools::MessageCallback s_MessageCallback;
122 cmSystemTools::OutputCallback s_StderrCallback;
123 cmSystemTools::OutputCallback s_StdoutCallback;
125 } // namespace
127 #if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE)
128 // For GetEnvironmentVariables
129 # if defined(_WIN32)
130 extern __declspec(dllimport) char** environ;
131 # else
132 extern char** environ;
133 # endif
134 #endif
136 #if !defined(CMAKE_BOOTSTRAP)
137 static std::string cm_archive_entry_pathname(struct archive_entry* entry)
139 # if cmsys_STL_HAS_WSTRING
140 return cmsys::Encoding::ToNarrow(archive_entry_pathname_w(entry));
141 # else
142 return archive_entry_pathname(entry);
143 # endif
146 static int cm_archive_read_open_file(struct archive* a, const char* file,
147 int block_size)
149 # if cmsys_STL_HAS_WSTRING
150 std::wstring wfile = cmsys::Encoding::ToWide(file);
151 return archive_read_open_filename_w(a, wfile.c_str(), block_size);
152 # else
153 return archive_read_open_filename(a, file, block_size);
154 # endif
156 #endif
158 #ifdef _WIN32
159 #elif defined(__APPLE__)
160 # include <crt_externs.h>
162 # define environ (*_NSGetEnviron())
163 #endif
165 bool cmSystemTools::s_RunCommandHideConsole = false;
166 bool cmSystemTools::s_DisableRunCommandOutput = false;
167 bool cmSystemTools::s_ErrorOccurred = false;
168 bool cmSystemTools::s_FatalErrorOccurred = false;
169 bool cmSystemTools::s_ForceUnixPaths = false;
171 // replace replace with with as many times as it shows up in source.
172 // write the result into source.
173 #if defined(_WIN32) && !defined(__CYGWIN__)
174 void cmSystemTools::ExpandRegistryValues(std::string& source, KeyWOW64 view)
176 // Regular expression to match anything inside [...] that begins in HKEY.
177 // Note that there is a special rule for regular expressions to match a
178 // close square-bracket inside a list delimited by square brackets.
179 // The "[^]]" part of this expression will match any character except
180 // a close square-bracket. The ']' character must be the first in the
181 // list of characters inside the [^...] block of the expression.
182 cmsys::RegularExpression regEntry("\\[(HKEY[^]]*)\\]");
184 // check for black line or comment
185 while (regEntry.find(source)) {
186 // the arguments are the second match
187 std::string key = regEntry.match(1);
188 std::string val;
189 if (ReadRegistryValue(key.c_str(), val, view)) {
190 std::string reg = cmStrCat('[', key, ']');
191 cmSystemTools::ReplaceString(source, reg.c_str(), val.c_str());
192 } else {
193 std::string reg = cmStrCat('[', key, ']');
194 cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
198 #else
199 void cmSystemTools::ExpandRegistryValues(std::string& source,
200 KeyWOW64 /*unused*/)
202 cmsys::RegularExpression regEntry("\\[(HKEY[^]]*)\\]");
203 while (regEntry.find(source)) {
204 // the arguments are the second match
205 std::string key = regEntry.match(1);
206 std::string reg = cmStrCat('[', key, ']');
207 cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
210 #endif
212 std::string cmSystemTools::HelpFileName(cm::string_view str)
214 std::string name(str);
215 cmSystemTools::ReplaceString(name, "<", "");
216 cmSystemTools::ReplaceString(name, ">", "");
217 return name;
220 void cmSystemTools::Error(const std::string& m)
222 std::string message = "CMake Error: " + m;
223 cmSystemTools::s_ErrorOccurred = true;
224 cmSystemTools::Message(message, "Error");
227 void cmSystemTools::SetInterruptCallback(InterruptCallback f)
229 s_InterruptCallback = std::move(f);
232 bool cmSystemTools::GetInterruptFlag()
234 if (s_InterruptCallback) {
235 return s_InterruptCallback();
237 return false;
240 void cmSystemTools::SetMessageCallback(MessageCallback f)
242 s_MessageCallback = std::move(f);
245 void cmSystemTools::SetStdoutCallback(OutputCallback f)
247 s_StdoutCallback = std::move(f);
250 void cmSystemTools::SetStderrCallback(OutputCallback f)
252 s_StderrCallback = std::move(f);
255 void cmSystemTools::Stderr(const std::string& s)
257 if (s_StderrCallback) {
258 s_StderrCallback(s);
259 } else {
260 std::cerr << s << std::flush;
264 void cmSystemTools::Stdout(const std::string& s)
266 if (s_StdoutCallback) {
267 s_StdoutCallback(s);
268 } else {
269 std::cout << s << std::flush;
273 void cmSystemTools::Message(const std::string& m, const char* title)
275 cmMessageMetadata md;
276 md.title = title;
277 Message(m, md);
280 void cmSystemTools::Message(const std::string& m, const cmMessageMetadata& md)
282 if (s_MessageCallback) {
283 s_MessageCallback(m, md);
284 } else {
285 std::cerr << m << std::endl;
289 void cmSystemTools::ReportLastSystemError(const char* msg)
291 std::string m =
292 cmStrCat(msg, ": System Error: ", Superclass::GetLastSystemError());
293 cmSystemTools::Error(m);
296 void cmSystemTools::ParseWindowsCommandLine(const char* command,
297 std::vector<std::string>& args)
299 // See the MSDN document "Parsing C Command-Line Arguments" at
300 // http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx for rules
301 // of parsing the windows command line.
303 bool in_argument = false;
304 bool in_quotes = false;
305 int backslashes = 0;
306 std::string arg;
307 for (const char* c = command; *c; ++c) {
308 if (*c == '\\') {
309 ++backslashes;
310 in_argument = true;
311 } else if (*c == '"') {
312 int backslash_pairs = backslashes >> 1;
313 int backslash_escaped = backslashes & 1;
314 arg.append(backslash_pairs, '\\');
315 backslashes = 0;
316 if (backslash_escaped) {
317 /* An odd number of backslashes precede this quote.
318 It is escaped. */
319 arg.append(1, '"');
320 } else {
321 /* An even number of backslashes precede this quote.
322 It is not escaped. */
323 in_quotes = !in_quotes;
325 in_argument = true;
326 } else {
327 arg.append(backslashes, '\\');
328 backslashes = 0;
329 if (cmIsSpace(*c)) {
330 if (in_quotes) {
331 arg.append(1, *c);
332 } else if (in_argument) {
333 args.push_back(arg);
334 arg.clear();
335 in_argument = false;
337 } else {
338 in_argument = true;
339 arg.append(1, *c);
343 arg.append(backslashes, '\\');
344 if (in_argument) {
345 args.push_back(arg);
349 class cmSystemToolsArgV
351 char** ArgV;
353 public:
354 cmSystemToolsArgV(char** argv)
355 : ArgV(argv)
358 ~cmSystemToolsArgV()
360 for (char** arg = this->ArgV; arg && *arg; ++arg) {
361 free(*arg);
363 free(this->ArgV);
365 cmSystemToolsArgV(const cmSystemToolsArgV&) = delete;
366 cmSystemToolsArgV& operator=(const cmSystemToolsArgV&) = delete;
367 void Store(std::vector<std::string>& args) const
369 for (char** arg = this->ArgV; arg && *arg; ++arg) {
370 args.emplace_back(*arg);
375 void cmSystemTools::ParseUnixCommandLine(const char* command,
376 std::vector<std::string>& args)
378 // Invoke the underlying parser.
379 cmSystemToolsArgV argv(cmsysSystem_Parse_CommandForUnix(command, 0));
380 argv.Store(args);
383 std::vector<std::string> cmSystemTools::HandleResponseFile(
384 std::vector<std::string>::const_iterator argBeg,
385 std::vector<std::string>::const_iterator argEnd)
387 std::vector<std::string> arg_full;
388 for (std::string const& arg : cmMakeRange(argBeg, argEnd)) {
389 if (cmHasLiteralPrefix(arg, "@")) {
390 cmsys::ifstream responseFile(arg.substr(1).c_str(), std::ios::in);
391 if (!responseFile) {
392 std::string error = cmStrCat("failed to open for reading (",
393 cmSystemTools::GetLastSystemError(),
394 "):\n ", cm::string_view(arg).substr(1));
395 cmSystemTools::Error(error);
396 } else {
397 std::string line;
398 cmSystemTools::GetLineFromStream(responseFile, line);
399 std::vector<std::string> args2;
400 #ifdef _WIN32
401 cmSystemTools::ParseWindowsCommandLine(line.c_str(), args2);
402 #else
403 cmSystemTools::ParseUnixCommandLine(line.c_str(), args2);
404 #endif
405 cm::append(arg_full, args2);
407 } else {
408 arg_full.push_back(arg);
411 return arg_full;
414 std::vector<std::string> cmSystemTools::ParseArguments(const std::string& cmd)
416 std::vector<std::string> args;
417 std::string arg;
419 bool win_path = false;
421 const char* command = cmd.c_str();
422 if (command[0] && command[1] &&
423 ((command[0] != '/' && command[1] == ':' && command[2] == '\\') ||
424 (command[0] == '\"' && command[1] != '/' && command[2] == ':' &&
425 command[3] == '\\') ||
426 (command[0] == '\'' && command[1] != '/' && command[2] == ':' &&
427 command[3] == '\\') ||
428 (command[0] == '\\' && command[1] == '\\'))) {
429 win_path = true;
431 // Split the command into an argv array.
432 for (const char* c = command; *c;) {
433 // Skip over whitespace.
434 while (*c == ' ' || *c == '\t') {
435 ++c;
437 arg.clear();
438 if (*c == '"') {
439 // Parse a quoted argument.
440 ++c;
441 while (*c && *c != '"') {
442 arg.append(1, *c);
443 ++c;
445 if (*c) {
446 ++c;
448 args.push_back(arg);
449 } else if (*c == '\'') {
450 // Parse a quoted argument.
451 ++c;
452 while (*c && *c != '\'') {
453 arg.append(1, *c);
454 ++c;
456 if (*c) {
457 ++c;
459 args.push_back(arg);
460 } else if (*c) {
461 // Parse an unquoted argument.
462 while (*c && *c != ' ' && *c != '\t') {
463 if (*c == '\\' && !win_path) {
464 ++c;
465 if (*c) {
466 arg.append(1, *c);
467 ++c;
469 } else {
470 arg.append(1, *c);
471 ++c;
474 args.push_back(arg);
478 return args;
481 bool cmSystemTools::SplitProgramFromArgs(std::string const& command,
482 std::string& program,
483 std::string& args)
485 const char* c = command.c_str();
487 // Skip leading whitespace.
488 while (cmIsSpace(*c)) {
489 ++c;
492 // Parse one command-line element up to an unquoted space.
493 bool in_escape = false;
494 bool in_double = false;
495 bool in_single = false;
496 for (; *c; ++c) {
497 if (in_single) {
498 if (*c == '\'') {
499 in_single = false;
500 } else {
501 program += *c;
503 } else if (in_escape) {
504 in_escape = false;
505 program += *c;
506 } else if (*c == '\\') {
507 in_escape = true;
508 } else if (in_double) {
509 if (*c == '"') {
510 in_double = false;
511 } else {
512 program += *c;
514 } else if (*c == '"') {
515 in_double = true;
516 } else if (*c == '\'') {
517 in_single = true;
518 } else if (cmIsSpace(*c)) {
519 break;
520 } else {
521 program += *c;
525 // The remainder of the command line holds unparsed arguments.
526 args = c;
528 return !in_single && !in_escape && !in_double;
531 std::size_t cmSystemTools::CalculateCommandLineLengthLimit()
533 size_t sz =
534 #ifdef _WIN32
535 // There's a maximum of 65536 bytes and thus 32768 WCHARs on Windows
536 // However, cmd.exe itself can only handle 8191 WCHARs and Ninja for
537 // example uses it to spawn processes.
538 size_t(8191);
539 #elif defined(__linux)
540 // MAX_ARG_STRLEN is the maximum length of a string permissible for
541 // the execve() syscall on Linux. It's defined as (PAGE_SIZE * 32)
542 // in Linux's binfmts.h
543 static_cast<size_t>(sysconf(_SC_PAGESIZE) * 32);
544 #else
545 size_t(0);
546 #endif
548 #if defined(_SC_ARG_MAX)
549 // ARG_MAX is the maximum size of the command and environment
550 // that can be passed to the exec functions on UNIX.
551 // The value in climits does not need to be present and may
552 // depend upon runtime memory constraints, hence sysconf()
553 // should be used to query it.
554 long szArgMax = sysconf(_SC_ARG_MAX);
555 // A return value of -1 signifies an undetermined limit, but
556 // it does not imply an infinite limit, and thus is ignored.
557 if (szArgMax != -1) {
558 // We estimate the size of the environment block to be 1000.
559 // This isn't accurate at all, but leaves some headroom.
560 szArgMax = szArgMax < 1000 ? 0 : szArgMax - 1000;
561 # if defined(_WIN32) || defined(__linux)
562 sz = std::min(sz, static_cast<size_t>(szArgMax));
563 # else
564 sz = static_cast<size_t>(szArgMax);
565 # endif
567 #endif
568 return sz;
571 bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
572 std::string* captureStdOut,
573 std::string* captureStdErr, int* retVal,
574 const char* dir, OutputOption outputflag,
575 cmDuration timeout, Encoding encoding)
577 cmUVProcessChainBuilder builder;
578 builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin)
579 .AddCommand(command);
580 if (dir) {
581 builder.SetWorkingDirectory(dir);
584 if (outputflag == OUTPUT_PASSTHROUGH) {
585 captureStdOut = nullptr;
586 captureStdErr = nullptr;
587 builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
588 .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
589 } else if (outputflag == OUTPUT_MERGE ||
590 (captureStdErr && captureStdErr == captureStdOut)) {
591 builder.SetMergedBuiltinStreams();
592 captureStdErr = nullptr;
593 } else {
594 builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
595 .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
597 assert(!captureStdErr || captureStdErr != captureStdOut);
599 auto chain = builder.Start();
600 bool timedOut = false;
601 cm::uv_timer_ptr timer;
602 if (timeout.count()) {
603 timer.init(chain.GetLoop(), &timedOut);
604 timer.start(
605 [](uv_timer_t* t) {
606 auto* timedOutPtr = static_cast<bool*>(t->data);
607 *timedOutPtr = true;
609 static_cast<uint64_t>(timeout.count() * 1000.0), 0);
612 std::vector<char> tempStdOut;
613 std::vector<char> tempStdErr;
614 cm::uv_pipe_ptr outStream;
615 bool outFinished = true;
616 cm::uv_pipe_ptr errStream;
617 bool errFinished = true;
618 cmProcessOutput processOutput(encoding);
619 std::unique_ptr<cmUVStreamReadHandle> outputHandle;
620 std::unique_ptr<cmUVStreamReadHandle> errorHandle;
621 if (outputflag != OUTPUT_PASSTHROUGH &&
622 (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) {
623 auto startRead =
624 [&outputflag, &processOutput,
625 &chain](cm::uv_pipe_ptr& pipe, int stream, std::string* captureStd,
626 std::vector<char>& tempStd, int id,
627 void (*outputFunc)(const std::string&),
628 bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
629 if (stream < 0) {
630 return nullptr;
633 pipe.init(chain.GetLoop(), 0);
634 uv_pipe_open(pipe, stream);
636 finished = false;
637 return cmUVStreamRead(
638 pipe,
639 [outputflag, &processOutput, captureStd, &tempStd, id,
640 outputFunc](std::vector<char> data) {
641 // Translate NULL characters in the output into valid text.
642 for (auto& c : data) {
643 if (c == '\0') {
644 c = ' ';
648 if (outputflag != OUTPUT_NONE) {
649 std::string strdata;
650 processOutput.DecodeText(data.data(), data.size(), strdata, id);
651 outputFunc(strdata);
653 if (captureStd) {
654 cm::append(tempStd, data.data(), data.data() + data.size());
657 [&finished, outputflag, &processOutput, id, outputFunc]() {
658 finished = true;
659 if (outputflag != OUTPUT_NONE) {
660 std::string strdata;
661 processOutput.DecodeText(std::string(), strdata, id);
662 if (!strdata.empty()) {
663 outputFunc(strdata);
669 outputHandle =
670 startRead(outStream, chain.OutputStream(), captureStdOut, tempStdOut, 1,
671 cmSystemTools::Stdout, outFinished);
672 if (chain.OutputStream() != chain.ErrorStream()) {
673 errorHandle =
674 startRead(errStream, chain.ErrorStream(), captureStdErr, tempStdErr, 2,
675 cmSystemTools::Stderr, errFinished);
679 while (!timedOut && !(chain.Finished() && outFinished && errFinished)) {
680 uv_run(&chain.GetLoop(), UV_RUN_ONCE);
683 if (captureStdOut) {
684 captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
685 processOutput.DecodeText(*captureStdOut, *captureStdOut);
687 if (captureStdErr) {
688 captureStdErr->assign(tempStdErr.begin(), tempStdErr.end());
689 processOutput.DecodeText(*captureStdErr, *captureStdErr);
692 bool result = true;
693 if (timedOut) {
694 const char* error_str = "Process terminated due to timeout\n";
695 if (outputflag != OUTPUT_NONE) {
696 std::cerr << error_str << std::endl;
698 if (captureStdErr) {
699 captureStdErr->append(error_str, strlen(error_str));
701 result = false;
702 } else {
703 auto const& status = chain.GetStatus(0);
704 auto exception = status.GetException();
706 switch (exception.first) {
707 case cmUVProcessChain::ExceptionCode::None:
708 if (retVal) {
709 *retVal = static_cast<int>(status.ExitStatus);
710 } else {
711 if (status.ExitStatus != 0) {
712 result = false;
715 break;
716 default: {
717 if (outputflag != OUTPUT_NONE) {
718 std::cerr << exception.second << std::endl;
720 if (captureStdErr) {
721 captureStdErr->append(exception.second);
722 } else if (captureStdOut) {
723 captureStdOut->append(exception.second);
725 result = false;
726 } break;
730 return result;
733 bool cmSystemTools::RunSingleCommand(const std::string& command,
734 std::string* captureStdOut,
735 std::string* captureStdErr, int* retVal,
736 const char* dir, OutputOption outputflag,
737 cmDuration timeout)
739 if (s_DisableRunCommandOutput) {
740 outputflag = OUTPUT_NONE;
743 std::vector<std::string> args = cmSystemTools::ParseArguments(command);
745 if (args.empty()) {
746 return false;
748 return cmSystemTools::RunSingleCommand(args, captureStdOut, captureStdErr,
749 retVal, dir, outputflag, timeout);
752 std::string cmSystemTools::PrintSingleCommand(
753 std::vector<std::string> const& command)
755 if (command.empty()) {
756 return std::string();
759 return cmWrap('"', command, '"', " ");
762 bool cmSystemTools::DoesFileExistWithExtensions(
763 const std::string& name, const std::vector<std::string>& headerExts)
765 std::string hname;
767 for (std::string const& headerExt : headerExts) {
768 hname = cmStrCat(name, '.', headerExt);
769 if (cmSystemTools::FileExists(hname)) {
770 return true;
773 return false;
776 std::string cmSystemTools::FileExistsInParentDirectories(
777 const std::string& fname, const std::string& directory,
778 const std::string& toplevel)
780 std::string file = fname;
781 cmSystemTools::ConvertToUnixSlashes(file);
782 std::string dir = directory;
783 cmSystemTools::ConvertToUnixSlashes(dir);
784 std::string prevDir;
785 while (dir != prevDir) {
786 std::string path = cmStrCat(dir, "/", file);
787 if (cmSystemTools::FileExists(path)) {
788 return path;
790 if (dir.size() < toplevel.size()) {
791 break;
793 prevDir = dir;
794 dir = cmSystemTools::GetParentDirectory(dir);
796 return "";
799 #ifdef _WIN32
800 namespace {
802 /* Helper class to save and restore the specified file (or directory)
803 attribute bits. Instantiate this class as an automatic variable on the
804 stack. Its constructor saves a copy of the file attributes, and then its
805 destructor restores the original attribute settings. */
806 class SaveRestoreFileAttributes
808 public:
809 SaveRestoreFileAttributes(std::wstring const& path,
810 uint32_t file_attrs_to_set);
811 ~SaveRestoreFileAttributes();
813 SaveRestoreFileAttributes(SaveRestoreFileAttributes const&) = delete;
814 SaveRestoreFileAttributes& operator=(SaveRestoreFileAttributes const&) =
815 delete;
817 void SetPath(std::wstring const& path) { path_ = path; }
819 private:
820 std::wstring path_;
821 uint32_t original_attr_bits_;
824 SaveRestoreFileAttributes::SaveRestoreFileAttributes(
825 std::wstring const& path, uint32_t file_attrs_to_set)
826 : path_(path)
827 , original_attr_bits_(0)
829 // Set the specified attributes for the source file/directory.
830 original_attr_bits_ = GetFileAttributesW(path_.c_str());
831 if ((INVALID_FILE_ATTRIBUTES != original_attr_bits_) &&
832 ((file_attrs_to_set & original_attr_bits_) != file_attrs_to_set)) {
833 SetFileAttributesW(path_.c_str(), original_attr_bits_ | file_attrs_to_set);
837 // We set attribute bits. Now we need to restore their original state.
838 SaveRestoreFileAttributes::~SaveRestoreFileAttributes()
840 DWORD last_error = GetLastError();
841 // Verify or restore the original attributes.
842 const DWORD source_attr_bits = GetFileAttributesW(path_.c_str());
843 if (INVALID_FILE_ATTRIBUTES != source_attr_bits) {
844 if (original_attr_bits_ != source_attr_bits) {
845 // The file still exists, and its attributes aren't our saved values.
846 // Time to restore them.
847 SetFileAttributesW(path_.c_str(), original_attr_bits_);
850 SetLastError(last_error);
853 struct WindowsFileRetryInit
855 cmSystemTools::WindowsFileRetry Retry;
856 bool Explicit;
859 WindowsFileRetryInit InitWindowsFileRetry(wchar_t const* const values[2],
860 unsigned int const defaults[2])
862 unsigned int data[2] = { 0, 0 };
863 HKEY const keys[2] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE };
864 for (int k = 0; k < 2; ++k) {
865 HKEY hKey;
866 if (RegOpenKeyExW(keys[k], L"Software\\Kitware\\CMake\\Config", 0,
867 KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
868 for (int v = 0; v < 2; ++v) {
869 DWORD dwData, dwType, dwSize = 4;
870 if (!data[v] &&
871 RegQueryValueExW(hKey, values[v], 0, &dwType, (BYTE*)&dwData,
872 &dwSize) == ERROR_SUCCESS &&
873 dwType == REG_DWORD && dwSize == 4) {
874 data[v] = static_cast<unsigned int>(dwData);
877 RegCloseKey(hKey);
880 WindowsFileRetryInit init;
881 init.Explicit = data[0] || data[1];
882 init.Retry.Count = data[0] ? data[0] : defaults[0];
883 init.Retry.Delay = data[1] ? data[1] : defaults[1];
884 return init;
887 WindowsFileRetryInit InitWindowsFileRetry()
889 static wchar_t const* const values[2] = { L"FilesystemRetryCount",
890 L"FilesystemRetryDelay" };
891 static unsigned int const defaults[2] = { 5, 500 };
892 return InitWindowsFileRetry(values, defaults);
895 WindowsFileRetryInit InitWindowsDirectoryRetry()
897 static wchar_t const* const values[2] = { L"FilesystemDirectoryRetryCount",
898 L"FilesystemDirectoryRetryDelay" };
899 static unsigned int const defaults[2] = { 120, 500 };
900 WindowsFileRetryInit dirInit = InitWindowsFileRetry(values, defaults);
901 if (dirInit.Explicit) {
902 return dirInit;
904 WindowsFileRetryInit fileInit = InitWindowsFileRetry();
905 if (fileInit.Explicit) {
906 return fileInit;
908 return dirInit;
911 cmSystemTools::WindowsFileRetry GetWindowsRetry(std::wstring const& path)
913 // If we are performing a directory operation, then try and get the
914 // appropriate timing info.
915 DWORD const attrs = GetFileAttributesW(path.c_str());
916 if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
917 return cmSystemTools::GetWindowsDirectoryRetry();
919 return cmSystemTools::GetWindowsFileRetry();
922 } // end of anonymous namespace
924 cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsFileRetry()
926 static WindowsFileRetry retry = InitWindowsFileRetry().Retry;
927 return retry;
930 cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsDirectoryRetry()
932 static cmSystemTools::WindowsFileRetry retry =
933 InitWindowsDirectoryRetry().Retry;
934 return retry;
937 cmSystemTools::WindowsVersion cmSystemTools::GetWindowsVersion()
939 /* Windows version number data. */
940 OSVERSIONINFOEXW osviex;
941 ZeroMemory(&osviex, sizeof(osviex));
942 osviex.dwOSVersionInfoSize = sizeof(osviex);
944 # ifdef CM_WINDOWS_DEPRECATED_GetVersionEx
945 # pragma warning(push)
946 # ifdef __INTEL_COMPILER
947 # pragma warning(disable : 1478)
948 # elif defined __clang__
949 # pragma clang diagnostic push
950 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
951 # else
952 # pragma warning(disable : 4996)
953 # endif
954 # endif
955 GetVersionExW((OSVERSIONINFOW*)&osviex);
956 # ifdef CM_WINDOWS_DEPRECATED_GetVersionEx
957 # ifdef __clang__
958 # pragma clang diagnostic pop
959 # else
960 # pragma warning(pop)
961 # endif
962 # endif
964 WindowsVersion result;
965 result.dwMajorVersion = osviex.dwMajorVersion;
966 result.dwMinorVersion = osviex.dwMinorVersion;
967 result.dwBuildNumber = osviex.dwBuildNumber;
968 return result;
970 #endif
972 std::string cmSystemTools::GetRealPathResolvingWindowsSubst(
973 const std::string& path, std::string* errorMessage)
975 #ifdef _WIN32
976 // uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found
977 std::string resolved_path;
978 uv_fs_t req;
979 int err = uv_fs_realpath(nullptr, &req, path.c_str(), nullptr);
980 if (!err) {
981 resolved_path = std::string((char*)req.ptr);
982 cmSystemTools::ConvertToUnixSlashes(resolved_path);
983 // Normalize to upper-case drive letter as GetActualCaseForPath does.
984 if (resolved_path.size() > 1 && resolved_path[1] == ':') {
985 resolved_path[0] = toupper(resolved_path[0]);
987 } else if (err == UV_ENOSYS) {
988 resolved_path = cmsys::SystemTools::GetRealPath(path, errorMessage);
989 } else if (errorMessage) {
990 LPSTR message = nullptr;
991 DWORD size = FormatMessageA(
992 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
993 FORMAT_MESSAGE_IGNORE_INSERTS,
994 nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&message,
995 0, nullptr);
996 *errorMessage = std::string(message, size);
997 LocalFree(message);
999 resolved_path = "";
1000 } else {
1001 resolved_path = path;
1003 return resolved_path;
1004 #else
1005 return cmsys::SystemTools::GetRealPath(path, errorMessage);
1006 #endif
1009 void cmSystemTools::InitializeLibUV()
1011 #if defined(_WIN32)
1012 // Perform libuv one-time initialization now, and then un-do its
1013 // global _fmode setting so that using libuv does not change the
1014 // default file text/binary mode. See libuv issue 840.
1015 if (uv_loop_t* loop = uv_default_loop()) {
1016 uv_loop_close(loop);
1018 # ifdef _MSC_VER
1019 _set_fmode(_O_TEXT);
1020 # else
1021 _fmode = _O_TEXT;
1022 # endif
1023 // Replace libuv's report handler with our own to suppress popups.
1024 cmSystemTools::EnableMSVCDebugHook();
1025 #endif
1028 #if defined(_WIN32)
1029 # include <random>
1031 # include <wctype.h>
1032 # ifdef _MSC_VER
1033 using mode_t = cmSystemTools::SystemTools::mode_t;
1034 # endif
1035 #else
1036 # include <sys/stat.h>
1037 #endif
1039 inline int Mkdir(const char* dir, const mode_t* mode)
1041 #if defined(_WIN32)
1042 int ret = _wmkdir(cmSystemTools::ConvertToWindowsExtendedPath(dir).c_str());
1043 if (ret == 0 && mode)
1044 cmSystemTools::SystemTools::SetPermissions(dir, *mode);
1045 return ret;
1046 #else
1047 return mkdir(dir, mode ? *mode : 0777);
1048 #endif
1051 cmsys::Status cmSystemTools::MakeTempDirectory(std::string& path,
1052 const mode_t* mode)
1054 if (path.empty()) {
1055 return cmsys::Status::POSIX(EINVAL);
1057 return cmSystemTools::MakeTempDirectory(&path.front(), mode);
1060 cmsys::Status cmSystemTools::MakeTempDirectory(char* path, const mode_t* mode)
1062 if (!path) {
1063 return cmsys::Status::POSIX(EINVAL);
1066 // verify that path ends with "XXXXXX"
1067 const auto l = std::strlen(path);
1068 if (!cmHasLiteralSuffix(cm::string_view{ path, l }, "XXXXXX")) {
1069 return cmsys::Status::POSIX(EINVAL);
1072 // create parent directories
1073 auto* sep = path;
1074 while ((sep = strchr(sep, '/'))) {
1075 // all underlying functions use C strings,
1076 // so temporarily end the string here
1077 *sep = '\0';
1078 Mkdir(path, mode);
1080 *sep = '/';
1081 ++sep;
1084 #ifdef _WIN32
1085 const int nchars = 36;
1086 const char chars[nchars + 1] = "abcdefghijklmnopqrstuvwxyz0123456789";
1088 std::random_device rd;
1089 std::mt19937 rg{ rd() };
1090 std::uniform_int_distribution<int> dist{ 0, nchars - 1 };
1092 for (auto tries = 100; tries; --tries) {
1093 for (auto n = l - 6; n < l; ++n) {
1094 path[n] = chars[dist(rg)];
1096 if (Mkdir(path, mode) == 0) {
1097 return cmsys::Status::Success();
1098 } else if (errno != EEXIST) {
1099 return cmsys::Status::POSIX_errno();
1102 return cmsys::Status::POSIX(EAGAIN);
1103 #else
1104 if (mkdtemp(path)) {
1105 if (mode) {
1106 chmod(path, *mode);
1108 } else {
1109 return cmsys::Status::POSIX_errno();
1111 return cmsys::Status::Success();
1112 #endif
1115 #ifdef _WIN32
1116 namespace {
1117 bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname,
1118 cmSystemTools::Replace replace)
1120 // Not only ignore any previous error, but clear any memory of it.
1121 SetLastError(0);
1123 DWORD flags = 0;
1124 if (replace == cmSystemTools::Replace::Yes) {
1125 // Use MOVEFILE_REPLACE_EXISTING to replace an existing destination file.
1126 flags = flags | MOVEFILE_REPLACE_EXISTING;
1129 return MoveFileExW(oldname.c_str(), newname.c_str(), flags);
1132 #endif
1134 cmSystemTools::CopyResult cmSystemTools::CopySingleFile(
1135 std::string const& oldname, std::string const& newname, CopyWhen when,
1136 CopyInputRecent inputRecent, std::string* err)
1138 switch (when) {
1139 case CopyWhen::Always:
1140 break;
1141 case CopyWhen::OnlyIfDifferent:
1142 if (!FilesDiffer(oldname, newname)) {
1143 return CopyResult::Success;
1145 break;
1148 mode_t perm = 0;
1149 cmsys::Status perms = SystemTools::GetPermissions(oldname, perm);
1151 // If files are the same do not copy
1152 if (SystemTools::SameFile(oldname, newname)) {
1153 return CopyResult::Success;
1156 cmsys::SystemTools::CopyStatus status;
1157 status = cmsys::SystemTools::CloneFileContent(oldname, newname);
1158 if (!status) {
1159 // if cloning did not succeed, fall back to blockwise copy
1160 #ifdef _WIN32
1161 if (inputRecent == CopyInputRecent::Yes) {
1162 // Windows sometimes locks a file immediately after creation.
1163 // Retry a few times.
1164 WindowsFileRetry retry = cmSystemTools::GetWindowsFileRetry();
1165 while ((status =
1166 cmsys::SystemTools::CopyFileContentBlockwise(oldname, newname),
1167 status.Path == cmsys::SystemTools::CopyStatus::SourcePath &&
1168 status.GetPOSIX() == EACCES && --retry.Count)) {
1169 cmSystemTools::Delay(retry.Delay);
1171 } else {
1172 status = cmsys::SystemTools::CopyFileContentBlockwise(oldname, newname);
1174 #else
1175 static_cast<void>(inputRecent);
1176 status = cmsys::SystemTools::CopyFileContentBlockwise(oldname, newname);
1177 #endif
1179 if (!status) {
1180 if (err) {
1181 *err = status.GetString();
1182 switch (status.Path) {
1183 case cmsys::SystemTools::CopyStatus::SourcePath:
1184 *err = cmStrCat(*err, " (input)");
1185 break;
1186 case cmsys::SystemTools::CopyStatus::DestPath:
1187 *err = cmStrCat(*err, " (output)");
1188 break;
1189 default:
1190 break;
1193 return CopyResult::Failure;
1195 if (perms) {
1196 perms = SystemTools::SetPermissions(newname, perm);
1197 if (!perms) {
1198 if (err) {
1199 *err = cmStrCat(perms.GetString(), " (output)");
1201 return CopyResult::Failure;
1204 return CopyResult::Success;
1207 bool cmSystemTools::RenameFile(const std::string& oldname,
1208 const std::string& newname)
1210 return cmSystemTools::RenameFile(oldname, newname, Replace::Yes) ==
1211 RenameResult::Success;
1214 cmSystemTools::RenameResult cmSystemTools::RenameFile(
1215 std::string const& oldname, std::string const& newname, Replace replace,
1216 std::string* err)
1218 #ifdef _WIN32
1219 # ifndef INVALID_FILE_ATTRIBUTES
1220 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
1221 # endif
1222 std::wstring const oldname_wstr =
1223 SystemTools::ConvertToWindowsExtendedPath(oldname);
1224 std::wstring const newname_wstr =
1225 SystemTools::ConvertToWindowsExtendedPath(newname);
1227 /* Windows MoveFileEx may not replace read-only or in-use files. If it
1228 fails then remove the read-only attribute from any existing destination.
1229 Try multiple times since we may be racing against another process
1230 creating/opening the destination file just before our MoveFileEx. */
1231 WindowsFileRetry retry = GetWindowsRetry(oldname_wstr);
1233 // Use RAII to set the attribute bit blocking Microsoft Search Indexing,
1234 // and restore the previous value upon return.
1235 SaveRestoreFileAttributes save_restore_file_attributes(
1236 oldname_wstr, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
1238 DWORD move_last_error = 0;
1239 while (!cmMoveFile(oldname_wstr, newname_wstr, replace) && --retry.Count) {
1240 move_last_error = GetLastError();
1242 // There was no error ==> the operation is not yet complete.
1243 if (move_last_error == NO_ERROR) {
1244 break;
1247 // Try again only if failure was due to access/sharing permissions.
1248 // Most often ERROR_ACCESS_DENIED (a.k.a. I/O error) for a directory, and
1249 // ERROR_SHARING_VIOLATION for a file, are caused by one of the following:
1250 // 1) Anti-Virus Software
1251 // 2) Windows Search Indexer
1252 // 3) Windows Explorer has an associated directory already opened.
1253 if (move_last_error != ERROR_ACCESS_DENIED &&
1254 move_last_error != ERROR_SHARING_VIOLATION) {
1255 if (replace == Replace::No && move_last_error == ERROR_ALREADY_EXISTS) {
1256 return RenameResult::NoReplace;
1258 if (err) {
1259 *err = cmsys::Status::Windows(move_last_error).GetString();
1261 return RenameResult::Failure;
1264 DWORD const attrs = GetFileAttributesW(newname_wstr.c_str());
1265 if ((attrs != INVALID_FILE_ATTRIBUTES) &&
1266 (attrs & FILE_ATTRIBUTE_READONLY) &&
1267 // FILE_ATTRIBUTE_READONLY is not honored on directories.
1268 !(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
1269 // Remove the read-only attribute from the destination file.
1270 SetFileAttributesW(newname_wstr.c_str(),
1271 attrs & ~FILE_ATTRIBUTE_READONLY);
1272 } else {
1273 // The file may be temporarily in use so wait a bit.
1274 cmSystemTools::Delay(retry.Delay);
1278 // If we were successful, then there was no error.
1279 if (retry.Count > 0) {
1280 move_last_error = 0;
1281 // Restore the attributes on the new name.
1282 save_restore_file_attributes.SetPath(newname_wstr);
1284 SetLastError(move_last_error);
1285 if (retry.Count > 0) {
1286 return RenameResult::Success;
1288 if (replace == Replace::No && GetLastError() == ERROR_ALREADY_EXISTS) {
1289 return RenameResult::NoReplace;
1291 if (err) {
1292 *err = cmsys::Status::Windows_GetLastError().GetString();
1294 return RenameResult::Failure;
1295 #else
1296 // On UNIX we have OS-provided calls to create 'newname' atomically.
1297 if (replace == Replace::No) {
1298 if (link(oldname.c_str(), newname.c_str()) == 0) {
1299 return RenameResult::Success;
1301 if (errno == EEXIST) {
1302 return RenameResult::NoReplace;
1304 if (err) {
1305 *err = cmsys::Status::POSIX_errno().GetString();
1307 return RenameResult::Failure;
1309 if (rename(oldname.c_str(), newname.c_str()) == 0) {
1310 return RenameResult::Success;
1312 if (err) {
1313 *err = cmsys::Status::POSIX_errno().GetString();
1315 return RenameResult::Failure;
1316 #endif
1319 void cmSystemTools::MoveFileIfDifferent(const std::string& source,
1320 const std::string& destination)
1322 if (FilesDiffer(source, destination)) {
1323 if (RenameFile(source, destination)) {
1324 return;
1326 CopyFileAlways(source, destination);
1328 RemoveFile(source);
1331 void cmSystemTools::Glob(const std::string& directory,
1332 const std::string& regexp,
1333 std::vector<std::string>& files)
1335 cmsys::Directory d;
1336 cmsys::RegularExpression reg(regexp.c_str());
1338 if (d.Load(directory)) {
1339 size_t numf;
1340 unsigned int i;
1341 numf = d.GetNumberOfFiles();
1342 for (i = 0; i < numf; i++) {
1343 std::string fname = d.GetFile(i);
1344 if (reg.find(fname)) {
1345 files.push_back(std::move(fname));
1351 void cmSystemTools::GlobDirs(const std::string& path,
1352 std::vector<std::string>& files)
1354 std::string::size_type pos = path.find("/*");
1355 if (pos == std::string::npos) {
1356 files.push_back(path);
1357 return;
1359 std::string startPath = path.substr(0, pos);
1360 std::string finishPath = path.substr(pos + 2);
1362 cmsys::Directory d;
1363 if (d.Load(startPath)) {
1364 for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) {
1365 if ((std::string(d.GetFile(i)) != ".") &&
1366 (std::string(d.GetFile(i)) != "..")) {
1367 std::string fname = cmStrCat(startPath, '/', d.GetFile(i));
1368 if (cmSystemTools::FileIsDirectory(fname)) {
1369 fname += finishPath;
1370 cmSystemTools::GlobDirs(fname, files);
1377 bool cmSystemTools::SimpleGlob(const std::string& glob,
1378 std::vector<std::string>& files,
1379 int type /* = 0 */)
1381 files.clear();
1382 if (glob.back() != '*') {
1383 return false;
1385 std::string path = cmSystemTools::GetFilenamePath(glob);
1386 std::string ppath = cmSystemTools::GetFilenameName(glob);
1387 ppath = ppath.substr(0, ppath.size() - 1);
1388 if (path.empty()) {
1389 path = "/";
1392 bool res = false;
1393 cmsys::Directory d;
1394 if (d.Load(path)) {
1395 for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) {
1396 if ((std::string(d.GetFile(i)) != ".") &&
1397 (std::string(d.GetFile(i)) != "..")) {
1398 std::string fname = path;
1399 if (path.back() != '/') {
1400 fname += "/";
1402 fname += d.GetFile(i);
1403 std::string sfname = d.GetFile(i);
1404 if (type > 0 && cmSystemTools::FileIsDirectory(fname)) {
1405 continue;
1407 if (type < 0 && !cmSystemTools::FileIsDirectory(fname)) {
1408 continue;
1410 if (cmHasPrefix(sfname, ppath)) {
1411 files.push_back(fname);
1412 res = true;
1417 return res;
1420 std::string cmSystemTools::ConvertToOutputPath(std::string const& path)
1422 #if defined(_WIN32) && !defined(__CYGWIN__)
1423 if (s_ForceUnixPaths) {
1424 return cmSystemTools::ConvertToUnixOutputPath(path);
1426 return cmSystemTools::ConvertToWindowsOutputPath(path);
1427 #else
1428 return cmSystemTools::ConvertToUnixOutputPath(path);
1429 #endif
1432 void cmSystemTools::ConvertToOutputSlashes(std::string& path)
1434 #if defined(_WIN32) && !defined(__CYGWIN__)
1435 if (!s_ForceUnixPaths) {
1436 // Convert to windows slashes.
1437 std::string::size_type pos = 0;
1438 while ((pos = path.find('/', pos)) != std::string::npos) {
1439 path[pos++] = '\\';
1442 #else
1443 static_cast<void>(path);
1444 #endif
1447 void cmSystemTools::ConvertToLongPath(std::string& path)
1449 #if defined(_WIN32) && !defined(__CYGWIN__)
1450 // Try to convert path to a long path only if the path contains character '~'
1451 if (path.find('~') == std::string::npos) {
1452 return;
1455 std::wstring wPath = cmsys::Encoding::ToWide(path);
1456 DWORD ret = GetLongPathNameW(wPath.c_str(), nullptr, 0);
1457 std::vector<wchar_t> buffer(ret);
1458 if (ret != 0) {
1459 ret = GetLongPathNameW(wPath.c_str(), buffer.data(),
1460 static_cast<DWORD>(buffer.size()));
1463 if (ret != 0) {
1464 path = cmsys::Encoding::ToNarrow(buffer.data());
1466 #else
1467 static_cast<void>(path);
1468 #endif
1471 std::string cmSystemTools::ConvertToRunCommandPath(const std::string& path)
1473 #if defined(_WIN32) && !defined(__CYGWIN__)
1474 return cmSystemTools::ConvertToWindowsOutputPath(path);
1475 #else
1476 return cmSystemTools::ConvertToUnixOutputPath(path);
1477 #endif
1480 // compute the relative path from here to there
1481 std::string cmSystemTools::RelativePath(std::string const& local,
1482 std::string const& remote)
1484 if (!cmSystemTools::FileIsFullPath(local)) {
1485 cmSystemTools::Error("RelativePath must be passed a full path to local: " +
1486 local);
1488 if (!cmSystemTools::FileIsFullPath(remote)) {
1489 cmSystemTools::Error(
1490 "RelativePath must be passed a full path to remote: " + remote);
1492 return cmsys::SystemTools::RelativePath(local, remote);
1495 std::string cmSystemTools::ForceToRelativePath(std::string const& local_path,
1496 std::string const& remote_path)
1498 // The paths should never be quoted.
1499 assert(local_path.front() != '\"');
1500 assert(remote_path.front() != '\"');
1502 // The local path should never have a trailing slash except if it is just the
1503 // bare root directory
1504 assert(local_path.empty() || local_path.back() != '/' ||
1505 local_path.size() == 1 ||
1506 (local_path.size() == 3 && local_path[1] == ':' &&
1507 ((local_path[0] >= 'A' && local_path[0] <= 'Z') ||
1508 (local_path[0] >= 'a' && local_path[0] <= 'z'))));
1510 // If the path is already relative then just return the path.
1511 if (!cmSystemTools::FileIsFullPath(remote_path)) {
1512 return remote_path;
1515 // Identify the longest shared path component between the remote
1516 // path and the local path.
1517 std::vector<std::string> local;
1518 cmSystemTools::SplitPath(local_path, local);
1519 std::vector<std::string> remote;
1520 cmSystemTools::SplitPath(remote_path, remote);
1521 unsigned int common = 0;
1522 while (common < remote.size() && common < local.size() &&
1523 cmSystemTools::ComparePath(remote[common], local[common])) {
1524 ++common;
1527 // If no part of the path is in common then return the full path.
1528 if (common == 0) {
1529 return remote_path;
1532 // If the entire path is in common then just return a ".".
1533 if (common == remote.size() && common == local.size()) {
1534 return ".";
1537 // If the entire path is in common except for a trailing slash then
1538 // just return a "./".
1539 if (common + 1 == remote.size() && remote[common].empty() &&
1540 common == local.size()) {
1541 return "./";
1544 // Construct the relative path.
1545 std::string relative;
1547 // First add enough ../ to get up to the level of the shared portion
1548 // of the path. Leave off the trailing slash. Note that the last
1549 // component of local will never be empty because local should never
1550 // have a trailing slash.
1551 for (unsigned int i = common; i < local.size(); ++i) {
1552 relative += "..";
1553 if (i < local.size() - 1) {
1554 relative += "/";
1558 // Now add the portion of the destination path that is not included
1559 // in the shared portion of the path. Add a slash the first time
1560 // only if there was already something in the path. If there was a
1561 // trailing slash in the input then the last iteration of the loop
1562 // will add a slash followed by an empty string which will preserve
1563 // the trailing slash in the output.
1565 if (!relative.empty() && !remote.empty()) {
1566 relative += "/";
1568 relative += cmJoin(cmMakeRange(remote).advance(common), "/");
1570 // Finally return the path.
1571 return relative;
1574 std::string cmSystemTools::RelativeIfUnder(std::string const& top,
1575 std::string const& in)
1577 std::string out;
1578 if (in == top) {
1579 out = ".";
1580 } else if (cmSystemTools::IsSubDirectory(in, top)) {
1581 out = in.substr(top.size() + 1);
1582 } else {
1583 out = in;
1585 return out;
1588 cm::optional<std::string> cmSystemTools::GetEnvVar(std::string const& var)
1590 cm::optional<std::string> result;
1592 std::string value;
1593 if (cmSystemTools::GetEnv(var, value)) {
1594 result = std::move(value);
1597 return result;
1600 std::vector<std::string> cmSystemTools::SplitEnvPath(std::string const& value)
1602 #if defined(_WIN32) && !defined(__CYGWIN__)
1603 static cm::string_view sep = ";"_s;
1604 #else
1605 static cm::string_view sep = ":"_s;
1606 #endif
1607 std::vector<std::string> paths = cmTokenize(value, sep);
1608 for (std::string& p : paths) {
1609 SystemTools::ConvertToUnixSlashes(p);
1611 return paths;
1614 #ifndef CMAKE_BOOTSTRAP
1615 bool cmSystemTools::UnsetEnv(const char* value)
1617 # if !defined(HAVE_UNSETENV)
1618 return cmSystemTools::UnPutEnv(value);
1619 # else
1620 unsetenv(value);
1621 return true;
1622 # endif
1625 std::vector<std::string> cmSystemTools::GetEnvironmentVariables()
1627 std::vector<std::string> env;
1628 int cc;
1629 # ifdef _WIN32
1630 // if program starts with main, _wenviron is initially NULL, call to
1631 // _wgetenv and create wide-character string environment
1632 _wgetenv(L"");
1633 for (cc = 0; _wenviron[cc]; ++cc) {
1634 env.emplace_back(cmsys::Encoding::ToNarrow(_wenviron[cc]));
1636 # else
1637 for (cc = 0; environ[cc]; ++cc) {
1638 env.emplace_back(environ[cc]);
1640 # endif
1641 return env;
1644 void cmSystemTools::AppendEnv(std::vector<std::string> const& env)
1646 for (std::string const& eit : env) {
1647 cmSystemTools::PutEnv(eit);
1651 void cmSystemTools::EnvDiff::AppendEnv(std::vector<std::string> const& env)
1653 for (std::string const& eit : env) {
1654 this->PutEnv(eit);
1658 void cmSystemTools::EnvDiff::PutEnv(const std::string& env)
1660 auto const eq_loc = env.find('=');
1661 if (eq_loc != std::string::npos) {
1662 std::string name = env.substr(0, eq_loc);
1663 diff[name] = env.substr(eq_loc + 1);
1664 } else {
1665 this->UnPutEnv(env);
1669 void cmSystemTools::EnvDiff::UnPutEnv(const std::string& env)
1671 diff[env] = cm::nullopt;
1674 bool cmSystemTools::EnvDiff::ParseOperation(const std::string& envmod)
1676 char path_sep = GetSystemPathlistSeparator();
1678 auto apply_diff = [this](const std::string& name,
1679 std::function<void(std::string&)> const& apply) {
1680 cm::optional<std::string> old_value = diff[name];
1681 std::string output;
1682 if (old_value) {
1683 output = *old_value;
1684 } else {
1685 const char* curval = cmSystemTools::GetEnv(name);
1686 if (curval) {
1687 output = curval;
1690 apply(output);
1691 diff[name] = output;
1694 // Split on `=`
1695 auto const eq_loc = envmod.find_first_of('=');
1696 if (eq_loc == std::string::npos) {
1697 cmSystemTools::Error(cmStrCat(
1698 "Error: Missing `=` after the variable name in: ", envmod, '\n'));
1699 return false;
1702 auto const name = envmod.substr(0, eq_loc);
1704 // Split value on `:`
1705 auto const op_value_start = eq_loc + 1;
1706 auto const colon_loc = envmod.find_first_of(':', op_value_start);
1707 if (colon_loc == std::string::npos) {
1708 cmSystemTools::Error(
1709 cmStrCat("Error: Missing `:` after the operation in: ", envmod, '\n'));
1710 return false;
1712 auto const op = envmod.substr(op_value_start, colon_loc - op_value_start);
1714 auto const value_start = colon_loc + 1;
1715 auto const value = envmod.substr(value_start);
1717 // Determine what to do with the operation.
1718 if (op == "reset"_s) {
1719 auto entry = diff.find(name);
1720 if (entry != diff.end()) {
1721 diff.erase(entry);
1723 } else if (op == "set"_s) {
1724 diff[name] = value;
1725 } else if (op == "unset"_s) {
1726 diff[name] = cm::nullopt;
1727 } else if (op == "string_append"_s) {
1728 apply_diff(name, [&value](std::string& output) { output += value; });
1729 } else if (op == "string_prepend"_s) {
1730 apply_diff(name,
1731 [&value](std::string& output) { output.insert(0, value); });
1732 } else if (op == "path_list_append"_s) {
1733 apply_diff(name, [&value, path_sep](std::string& output) {
1734 if (!output.empty()) {
1735 output += path_sep;
1737 output += value;
1739 } else if (op == "path_list_prepend"_s) {
1740 apply_diff(name, [&value, path_sep](std::string& output) {
1741 if (!output.empty()) {
1742 output.insert(output.begin(), path_sep);
1744 output.insert(0, value);
1746 } else if (op == "cmake_list_append"_s) {
1747 apply_diff(name, [&value](std::string& output) {
1748 if (!output.empty()) {
1749 output += ';';
1751 output += value;
1753 } else if (op == "cmake_list_prepend"_s) {
1754 apply_diff(name, [&value](std::string& output) {
1755 if (!output.empty()) {
1756 output.insert(output.begin(), ';');
1758 output.insert(0, value);
1760 } else {
1761 cmSystemTools::Error(cmStrCat(
1762 "Error: Unrecognized environment manipulation argument: ", op, '\n'));
1763 return false;
1766 return true;
1769 void cmSystemTools::EnvDiff::ApplyToCurrentEnv(std::ostringstream* measurement)
1771 for (auto const& env_apply : diff) {
1772 if (env_apply.second) {
1773 auto const env_update =
1774 cmStrCat(env_apply.first, '=', *env_apply.second);
1775 cmSystemTools::PutEnv(env_update);
1776 if (measurement) {
1777 *measurement << env_update << std::endl;
1779 } else {
1780 cmSystemTools::UnsetEnv(env_apply.first.c_str());
1781 if (measurement) {
1782 // Signify that this variable is being actively unset
1783 *measurement << '#' << env_apply.first << "=\n";
1789 cmSystemTools::SaveRestoreEnvironment::SaveRestoreEnvironment()
1791 this->Env = cmSystemTools::GetEnvironmentVariables();
1794 cmSystemTools::SaveRestoreEnvironment::~SaveRestoreEnvironment()
1796 // First clear everything in the current environment:
1797 std::vector<std::string> currentEnv = GetEnvironmentVariables();
1798 for (std::string var : currentEnv) {
1799 std::string::size_type pos = var.find('=');
1800 if (pos != std::string::npos) {
1801 var = var.substr(0, pos);
1804 cmSystemTools::UnsetEnv(var.c_str());
1807 // Then put back each entry from the original environment:
1808 cmSystemTools::AppendEnv(this->Env);
1810 #endif
1812 void cmSystemTools::EnableVSConsoleOutput()
1814 #ifdef _WIN32
1815 // Visual Studio tools like devenv may not
1816 // display output to the console unless this environment variable is
1817 // set. We need it to capture the output of these build tools.
1818 // Note for future work that one could pass "/out \\.\pipe\NAME" to
1819 // either of these executables where NAME is created with
1820 // CreateNamedPipe. This would bypass the internal buffering of the
1821 // output and allow it to be captured on the fly.
1822 cmSystemTools::PutEnv("vsconsoleoutput=1");
1824 # ifndef CMAKE_BOOTSTRAP
1825 // VS sets an environment variable to tell MS tools like "cl" to report
1826 // output through a backdoor pipe instead of stdout/stderr. Unset the
1827 // environment variable to close this backdoor for any path of process
1828 // invocations that passes through CMake so we can capture the output.
1829 cmSystemTools::UnsetEnv("VS_UNICODE_OUTPUT");
1830 # endif
1831 #endif
1834 bool cmSystemTools::IsPathToFramework(const std::string& path)
1836 return (cmSystemTools::FileIsFullPath(path) &&
1837 cmHasLiteralSuffix(path, ".framework"));
1840 bool cmSystemTools::IsPathToXcFramework(const std::string& path)
1842 return (cmSystemTools::FileIsFullPath(path) &&
1843 cmHasLiteralSuffix(path, ".xcframework"));
1846 bool cmSystemTools::IsPathToMacOSSharedLibrary(const std::string& path)
1848 return (cmSystemTools::FileIsFullPath(path) &&
1849 cmHasLiteralSuffix(path, ".dylib"));
1852 bool cmSystemTools::CreateTar(const std::string& outFileName,
1853 const std::vector<std::string>& files,
1854 cmTarCompression compressType, bool verbose,
1855 std::string const& mtime,
1856 std::string const& format, int compressionLevel)
1858 #if !defined(CMAKE_BOOTSTRAP)
1859 std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
1860 cmsys::ofstream fout(outFileName.c_str(), std::ios::out | std::ios::binary);
1861 if (!fout) {
1862 std::string e = cmStrCat("Cannot open output file \"", outFileName,
1863 "\": ", cmSystemTools::GetLastSystemError());
1864 cmSystemTools::Error(e);
1865 return false;
1867 cmArchiveWrite::Compress compress = cmArchiveWrite::CompressNone;
1868 switch (compressType) {
1869 case TarCompressGZip:
1870 compress = cmArchiveWrite::CompressGZip;
1871 break;
1872 case TarCompressBZip2:
1873 compress = cmArchiveWrite::CompressBZip2;
1874 break;
1875 case TarCompressXZ:
1876 compress = cmArchiveWrite::CompressXZ;
1877 break;
1878 case TarCompressZstd:
1879 compress = cmArchiveWrite::CompressZstd;
1880 break;
1881 case TarCompressNone:
1882 compress = cmArchiveWrite::CompressNone;
1883 break;
1886 cmArchiveWrite a(fout, compress, format.empty() ? "paxr" : format,
1887 compressionLevel);
1889 if (!a.Open()) {
1890 cmSystemTools::Error(a.GetError());
1891 return false;
1893 a.SetMTime(mtime);
1894 a.SetVerbose(verbose);
1895 bool tarCreatedSuccessfully = true;
1896 for (auto path : files) {
1897 if (cmSystemTools::FileIsFullPath(path)) {
1898 // Get the relative path to the file.
1899 path = cmSystemTools::RelativePath(cwd, path);
1901 if (!a.Add(path)) {
1902 cmSystemTools::Error(a.GetError());
1903 tarCreatedSuccessfully = false;
1906 return tarCreatedSuccessfully;
1907 #else
1908 (void)outFileName;
1909 (void)files;
1910 (void)verbose;
1911 return false;
1912 #endif
1915 #if !defined(CMAKE_BOOTSTRAP)
1916 namespace {
1917 # define BSDTAR_FILESIZE_PRINTF "%lu"
1918 # define BSDTAR_FILESIZE_TYPE unsigned long
1919 void list_item_verbose(FILE* out, struct archive_entry* entry)
1921 char tmp[100];
1922 size_t w;
1923 const char* p;
1924 const char* fmt;
1925 time_t tim;
1926 static time_t now;
1927 size_t u_width = 6;
1928 size_t gs_width = 13;
1931 * We avoid collecting the entire list in memory at once by
1932 * listing things as we see them. However, that also means we can't
1933 * just pre-compute the field widths. Instead, we start with guesses
1934 * and just widen them as necessary. These numbers are completely
1935 * arbitrary.
1937 if (!now) {
1938 time(&now);
1940 fprintf(out, "%s %d ", archive_entry_strmode(entry),
1941 archive_entry_nlink(entry));
1943 /* Use uname if it's present, else uid. */
1944 p = archive_entry_uname(entry);
1945 if ((p == nullptr) || (*p == '\0')) {
1946 snprintf(tmp, sizeof(tmp), "%lu ",
1947 static_cast<unsigned long>(archive_entry_uid(entry)));
1948 p = tmp;
1950 w = strlen(p);
1951 if (w > u_width) {
1952 u_width = w;
1954 fprintf(out, "%-*s ", static_cast<int>(u_width), p);
1955 /* Use gname if it's present, else gid. */
1956 p = archive_entry_gname(entry);
1957 if (p != nullptr && p[0] != '\0') {
1958 fprintf(out, "%s", p);
1959 w = strlen(p);
1960 } else {
1961 snprintf(tmp, sizeof(tmp), "%lu",
1962 static_cast<unsigned long>(archive_entry_gid(entry)));
1963 w = strlen(tmp);
1964 fprintf(out, "%s", tmp);
1968 * Print device number or file size, right-aligned so as to make
1969 * total width of group and devnum/filesize fields be gs_width.
1970 * If gs_width is too small, grow it.
1972 if (archive_entry_filetype(entry) == AE_IFCHR ||
1973 archive_entry_filetype(entry) == AE_IFBLK) {
1974 unsigned long rdevmajor = archive_entry_rdevmajor(entry);
1975 unsigned long rdevminor = archive_entry_rdevminor(entry);
1976 snprintf(tmp, sizeof(tmp), "%lu,%lu", rdevmajor, rdevminor);
1977 } else {
1979 * Note the use of platform-dependent macros to format
1980 * the filesize here. We need the format string and the
1981 * corresponding type for the cast.
1983 snprintf(tmp, sizeof(tmp), BSDTAR_FILESIZE_PRINTF,
1984 static_cast<BSDTAR_FILESIZE_TYPE>(archive_entry_size(entry)));
1986 if (w + strlen(tmp) >= gs_width) {
1987 gs_width = w + strlen(tmp) + 1;
1989 fprintf(out, "%*s", static_cast<int>(gs_width - w), tmp);
1991 /* Format the time using 'ls -l' conventions. */
1992 tim = archive_entry_mtime(entry);
1993 # define HALF_YEAR ((time_t)365 * 86400 / 2)
1994 # if defined(_WIN32) && !defined(__CYGWIN__)
1995 /* Windows' strftime function does not support %e format. */
1996 # define DAY_FMT "%d"
1997 # else
1998 # define DAY_FMT "%e" /* Day number without leading zeros */
1999 # endif
2000 if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) {
2001 fmt = DAY_FMT " %b %Y";
2002 } else {
2003 fmt = DAY_FMT " %b %H:%M";
2005 strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
2006 fprintf(out, " %s ", tmp);
2007 fprintf(out, "%s", cm_archive_entry_pathname(entry).c_str());
2009 /* Extra information for links. */
2010 if (archive_entry_hardlink(entry)) /* Hard link */
2012 fprintf(out, " link to %s", archive_entry_hardlink(entry));
2013 } else if (archive_entry_symlink(entry)) /* Symbolic link */
2015 fprintf(out, " -> %s", archive_entry_symlink(entry));
2017 fflush(out);
2020 void ArchiveError(const char* m1, struct archive* a)
2022 std::string message(m1);
2023 const char* m2 = archive_error_string(a);
2024 if (m2) {
2025 message += m2;
2027 cmSystemTools::Error(message);
2030 bool la_diagnostic(struct archive* ar, __LA_SSIZE_T r)
2032 // See archive.h definition of ARCHIVE_OK for return values.
2034 if (r >= ARCHIVE_OK) {
2035 return true;
2038 if (r >= ARCHIVE_WARN) {
2039 const char* warn = archive_error_string(ar);
2040 if (!warn) {
2041 warn = "unknown warning";
2043 std::cerr << "cmake -E tar: warning: " << warn << '\n';
2044 return true;
2047 // Error.
2048 const char* err = archive_error_string(ar);
2049 if (!err) {
2050 err = "unknown error";
2052 std::cerr << "cmake -E tar: error: " << err << '\n';
2053 return false;
2056 // Return 'true' on success
2057 bool copy_data(struct archive* ar, struct archive* aw)
2059 long r;
2060 const void* buff;
2061 size_t size;
2062 # if defined(ARCHIVE_VERSION_NUMBER) && ARCHIVE_VERSION_NUMBER >= 3000000
2063 __LA_INT64_T offset;
2064 # else
2065 off_t offset;
2066 # endif
2068 for (;;) {
2069 // See archive.h definition of ARCHIVE_OK for return values.
2070 r = archive_read_data_block(ar, &buff, &size, &offset);
2071 if (r == ARCHIVE_EOF) {
2072 return true;
2074 if (!la_diagnostic(ar, r)) {
2075 return false;
2077 // See archive.h definition of ARCHIVE_OK for return values.
2078 __LA_SSIZE_T const w = archive_write_data_block(aw, buff, size, offset);
2079 if (!la_diagnostic(ar, w)) {
2080 return false;
2083 # if !defined(__clang__) && !defined(__NVCOMPILER) && !defined(__HP_aCC)
2084 return false; /* this should not happen but it quiets some compilers */
2085 # endif
2088 bool extract_tar(const std::string& outFileName,
2089 const std::vector<std::string>& files, bool verbose,
2090 cmSystemTools::cmTarExtractTimestamps extractTimestamps,
2091 bool extract)
2093 cmLocaleRAII localeRAII;
2094 static_cast<void>(localeRAII);
2095 struct archive* a = archive_read_new();
2096 struct archive* ext = archive_write_disk_new();
2097 archive_read_support_filter_all(a);
2098 archive_read_support_format_all(a);
2099 struct archive_entry* entry;
2101 struct archive* matching = archive_match_new();
2102 if (matching == nullptr) {
2103 cmSystemTools::Error("Out of memory");
2104 return false;
2107 for (const auto& filename : files) {
2108 if (archive_match_include_pattern(matching, filename.c_str()) !=
2109 ARCHIVE_OK) {
2110 cmSystemTools::Error("Failed to add to inclusion list: " + filename);
2111 return false;
2115 int r = cm_archive_read_open_file(a, outFileName.c_str(), 10240);
2116 if (r) {
2117 ArchiveError("Problem with archive_read_open_file(): ", a);
2118 archive_write_free(ext);
2119 archive_read_close(a);
2120 return false;
2122 for (;;) {
2123 r = archive_read_next_header(a, &entry);
2124 if (r == ARCHIVE_EOF) {
2125 break;
2127 if (r != ARCHIVE_OK) {
2128 ArchiveError("Problem with archive_read_next_header(): ", a);
2129 break;
2132 if (archive_match_excluded(matching, entry)) {
2133 continue;
2136 if (verbose) {
2137 if (extract) {
2138 cmSystemTools::Stdout("x ");
2139 cmSystemTools::Stdout(cm_archive_entry_pathname(entry));
2140 } else {
2141 list_item_verbose(stdout, entry);
2143 cmSystemTools::Stdout("\n");
2144 } else if (!extract) {
2145 cmSystemTools::Stdout(cm_archive_entry_pathname(entry));
2146 cmSystemTools::Stdout("\n");
2148 if (extract) {
2149 if (extractTimestamps == cmSystemTools::cmTarExtractTimestamps::Yes) {
2150 r = archive_write_disk_set_options(ext, ARCHIVE_EXTRACT_TIME);
2151 if (r != ARCHIVE_OK) {
2152 ArchiveError("Problem with archive_write_disk_set_options(): ", ext);
2153 break;
2157 r = archive_write_header(ext, entry);
2158 if (r == ARCHIVE_OK) {
2159 if (!copy_data(a, ext)) {
2160 break;
2162 r = archive_write_finish_entry(ext);
2163 if (r != ARCHIVE_OK) {
2164 ArchiveError("Problem with archive_write_finish_entry(): ", ext);
2165 break;
2168 # ifdef _WIN32
2169 else if (const char* linktext = archive_entry_symlink(entry)) {
2170 std::cerr << "cmake -E tar: warning: skipping symbolic link \""
2171 << cm_archive_entry_pathname(entry) << "\" -> \"" << linktext
2172 << "\"." << std::endl;
2174 # endif
2175 else {
2176 ArchiveError("Problem with archive_write_header(): ", ext);
2177 cmSystemTools::Error("Current file: " +
2178 cm_archive_entry_pathname(entry));
2179 break;
2184 bool error_occured = false;
2185 if (matching != nullptr) {
2186 const char* p;
2187 int ar;
2189 while ((ar = archive_match_path_unmatched_inclusions_next(matching, &p)) ==
2190 ARCHIVE_OK) {
2191 cmSystemTools::Error("tar: " + std::string(p) +
2192 ": Not found in archive");
2193 error_occured = true;
2195 if (error_occured) {
2196 return false;
2198 if (ar == ARCHIVE_FATAL) {
2199 cmSystemTools::Error("tar: Out of memory");
2200 return false;
2203 archive_match_free(matching);
2204 archive_write_free(ext);
2205 archive_read_close(a);
2206 archive_read_free(a);
2207 return r == ARCHIVE_EOF || r == ARCHIVE_OK;
2210 #endif
2212 bool cmSystemTools::ExtractTar(const std::string& outFileName,
2213 const std::vector<std::string>& files,
2214 cmTarExtractTimestamps extractTimestamps,
2215 bool verbose)
2217 #if !defined(CMAKE_BOOTSTRAP)
2218 return extract_tar(outFileName, files, verbose, extractTimestamps, true);
2219 #else
2220 (void)outFileName;
2221 (void)files;
2222 (void)extractTimestamps;
2223 (void)verbose;
2224 return false;
2225 #endif
2228 bool cmSystemTools::ListTar(const std::string& outFileName,
2229 const std::vector<std::string>& files,
2230 bool verbose)
2232 #if !defined(CMAKE_BOOTSTRAP)
2233 return extract_tar(outFileName, files, verbose, cmTarExtractTimestamps::Yes,
2234 false);
2235 #else
2236 (void)outFileName;
2237 (void)files;
2238 (void)verbose;
2239 return false;
2240 #endif
2243 cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
2244 uv_loop_t* loop, uv_stream_t* outPipe, uv_stream_t* errPipe,
2245 std::string& line, cmDuration timeout, std::vector<char>& out,
2246 std::vector<char>& err)
2248 line.clear();
2249 auto outiter = out.begin();
2250 auto erriter = err.begin();
2251 cmProcessOutput processOutput;
2252 std::string strdata;
2253 while (true) {
2254 // Check for a newline in stdout.
2255 for (; outiter != out.end(); ++outiter) {
2256 if ((*outiter == '\r') && ((outiter + 1) == out.end())) {
2257 break;
2259 if (*outiter == '\n' || *outiter == '\0') {
2260 std::vector<char>::size_type length = outiter - out.begin();
2261 if (length > 1 && *(outiter - 1) == '\r') {
2262 --length;
2264 if (length > 0) {
2265 line.append(out.data(), length);
2267 out.erase(out.begin(), outiter + 1);
2268 return WaitForLineResult::STDOUT;
2272 // Check for a newline in stderr.
2273 for (; erriter != err.end(); ++erriter) {
2274 if ((*erriter == '\r') && ((erriter + 1) == err.end())) {
2275 break;
2277 if (*erriter == '\n' || *erriter == '\0') {
2278 std::vector<char>::size_type length = erriter - err.begin();
2279 if (length > 1 && *(erriter - 1) == '\r') {
2280 --length;
2282 if (length > 0) {
2283 line.append(err.data(), length);
2285 err.erase(err.begin(), erriter + 1);
2286 return WaitForLineResult::STDERR;
2290 // No newlines found. Wait for more data from the process.
2291 struct ReadData
2293 uv_stream_t* Stream;
2294 std::vector<char> Buffer;
2295 bool Read = false;
2296 bool Finished = false;
2298 auto startRead =
2299 [](uv_stream_t* stream,
2300 ReadData& data) -> std::unique_ptr<cmUVStreamReadHandle> {
2301 data.Stream = stream;
2302 return cmUVStreamRead(
2303 stream,
2304 [&data](std::vector<char> buf) {
2305 data.Buffer = std::move(buf);
2306 data.Read = true;
2307 uv_read_stop(data.Stream);
2309 [&data]() { data.Finished = true; });
2311 ReadData outData;
2312 auto outHandle = startRead(outPipe, outData);
2313 ReadData errData;
2314 auto errHandle = startRead(errPipe, errData);
2316 cm::uv_timer_ptr timer;
2317 bool timedOut = false;
2318 timer.init(*loop, &timedOut);
2319 timer.start(
2320 [](uv_timer_t* handle) {
2321 auto* timedOutPtr = static_cast<bool*>(handle->data);
2322 *timedOutPtr = true;
2324 static_cast<uint64_t>(timeout.count() * 1000.0), 0);
2326 uv_run(loop, UV_RUN_ONCE);
2327 if (timedOut) {
2328 // Timeout has been exceeded.
2329 return WaitForLineResult::Timeout;
2331 if (outData.Read) {
2332 processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(),
2333 strdata, 1);
2334 // Append to the stdout buffer.
2335 std::vector<char>::size_type size = out.size();
2336 cm::append(out, strdata);
2337 outiter = out.begin() + size;
2338 } else if (errData.Read) {
2339 processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(),
2340 strdata, 2);
2341 // Append to the stderr buffer.
2342 std::vector<char>::size_type size = err.size();
2343 cm::append(err, strdata);
2344 erriter = err.begin() + size;
2345 } else if (outData.Finished && errData.Finished) {
2346 // Both stdout and stderr pipes have broken. Return leftover data.
2347 processOutput.DecodeText(std::string(), strdata, 1);
2348 if (!strdata.empty()) {
2349 std::vector<char>::size_type size = out.size();
2350 cm::append(out, strdata);
2351 outiter = out.begin() + size;
2353 processOutput.DecodeText(std::string(), strdata, 2);
2354 if (!strdata.empty()) {
2355 std::vector<char>::size_type size = err.size();
2356 cm::append(err, strdata);
2357 erriter = err.begin() + size;
2359 if (!out.empty()) {
2360 line.append(out.data(), outiter - out.begin());
2361 out.erase(out.begin(), out.end());
2362 return WaitForLineResult::STDOUT;
2364 if (!err.empty()) {
2365 line.append(err.data(), erriter - err.begin());
2366 err.erase(err.begin(), err.end());
2367 return WaitForLineResult::STDERR;
2369 return WaitForLineResult::None;
2371 if (!outData.Finished) {
2372 uv_read_stop(outPipe);
2374 if (!errData.Finished) {
2375 uv_read_stop(errPipe);
2380 #ifdef _WIN32
2381 static void EnsureStdPipe(int stdFd, DWORD nStdHandle, FILE* stream,
2382 const wchar_t* mode)
2384 if (fileno(stream) >= 0) {
2385 return;
2387 _close(stdFd);
2388 _wfreopen(L"NUL", mode, stream);
2389 int fd = fileno(stream);
2390 if (fd < 0) {
2391 perror("failed to open NUL for missing stdio pipe");
2392 abort();
2394 if (fd != stdFd) {
2395 _dup2(fd, stdFd);
2397 SetStdHandle(nStdHandle, reinterpret_cast<HANDLE>(_get_osfhandle(fd)));
2400 void cmSystemTools::EnsureStdPipes()
2402 EnsureStdPipe(0, STD_INPUT_HANDLE, stdin, L"rb");
2403 EnsureStdPipe(1, STD_OUTPUT_HANDLE, stdout, L"wb");
2404 EnsureStdPipe(2, STD_ERROR_HANDLE, stderr, L"wb");
2406 #else
2407 static void EnsureStdPipe(int fd)
2409 if (fcntl(fd, F_GETFD) != -1 || errno != EBADF) {
2410 return;
2413 int f = open("/dev/null", fd == STDIN_FILENO ? O_RDONLY : O_WRONLY);
2414 if (f == -1) {
2415 perror("failed to open /dev/null for missing stdio pipe");
2416 abort();
2418 if (f != fd) {
2419 dup2(f, fd);
2420 close(f);
2424 void cmSystemTools::EnsureStdPipes()
2426 EnsureStdPipe(STDIN_FILENO);
2427 EnsureStdPipe(STDOUT_FILENO);
2428 EnsureStdPipe(STDERR_FILENO);
2430 #endif
2432 #ifdef _WIN32
2433 # ifndef CRYPT_SILENT
2434 # define CRYPT_SILENT 0x40 /* Not defined by VS 6 version of header. */
2435 # endif
2436 static int WinCryptRandom(void* data, size_t size)
2438 int result = 0;
2439 HCRYPTPROV hProvider = 0;
2440 if (CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL,
2441 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
2442 result = CryptGenRandom(hProvider, (DWORD)size, (BYTE*)data) ? 1 : 0;
2443 CryptReleaseContext(hProvider, 0);
2445 return result;
2447 #endif
2449 unsigned int cmSystemTools::RandomSeed()
2451 #if defined(_WIN32) && !defined(__CYGWIN__)
2452 unsigned int seed = 0;
2454 // Try using a real random source.
2455 if (WinCryptRandom(&seed, sizeof(seed))) {
2456 return seed;
2459 // Fall back to the time and pid.
2460 FILETIME ft;
2461 GetSystemTimeAsFileTime(&ft);
2462 unsigned int t1 = static_cast<unsigned int>(ft.dwHighDateTime);
2463 unsigned int t2 = static_cast<unsigned int>(ft.dwLowDateTime);
2464 unsigned int pid = static_cast<unsigned int>(GetCurrentProcessId());
2465 return t1 ^ t2 ^ pid;
2466 #else
2467 union
2469 unsigned int integer;
2470 char bytes[sizeof(unsigned int)];
2471 } seed;
2473 // Try using a real random source.
2474 cmsys::ifstream fin;
2475 fin.rdbuf()->pubsetbuf(nullptr, 0); // Unbuffered read.
2476 fin.open("/dev/urandom");
2477 if (fin.good() && fin.read(seed.bytes, sizeof(seed)) &&
2478 fin.gcount() == sizeof(seed)) {
2479 return seed.integer;
2482 // Fall back to the time and pid.
2483 struct timeval t;
2484 gettimeofday(&t, nullptr);
2485 unsigned int pid = static_cast<unsigned int>(getpid());
2486 unsigned int tv_sec = static_cast<unsigned int>(t.tv_sec);
2487 unsigned int tv_usec = static_cast<unsigned int>(t.tv_usec);
2488 // Since tv_usec never fills more than 11 bits we shift it to fill
2489 // in the slow-changing high-order bits of tv_sec.
2490 return tv_sec ^ (tv_usec << 21) ^ pid;
2491 #endif
2494 static std::string cmSystemToolsCMakeCommand;
2495 static std::string cmSystemToolsCTestCommand;
2496 static std::string cmSystemToolsCPackCommand;
2497 static std::string cmSystemToolsCMakeCursesCommand;
2498 static std::string cmSystemToolsCMakeGUICommand;
2499 static std::string cmSystemToolsCMClDepsCommand;
2500 static std::string cmSystemToolsCMakeRoot;
2501 static std::string cmSystemToolsHTMLDoc;
2502 void cmSystemTools::FindCMakeResources(const char* argv0)
2504 std::string exe_dir;
2505 #if defined(_WIN32) && !defined(__CYGWIN__)
2506 (void)argv0; // ignore this on windows
2507 wchar_t modulepath[_MAX_PATH];
2508 ::GetModuleFileNameW(nullptr, modulepath, sizeof(modulepath));
2509 std::string path = cmsys::Encoding::ToNarrow(modulepath);
2510 std::string realPath =
2511 cmSystemTools::GetRealPathResolvingWindowsSubst(path, nullptr);
2512 if (realPath.empty()) {
2513 realPath = path;
2515 exe_dir = cmSystemTools::GetFilenamePath(realPath);
2516 #elif defined(__APPLE__)
2517 (void)argv0; // ignore this on OS X
2518 # define CM_EXE_PATH_LOCAL_SIZE 16384
2519 char exe_path_local[CM_EXE_PATH_LOCAL_SIZE];
2520 # if defined(MAC_OS_X_VERSION_10_3) && !defined(MAC_OS_X_VERSION_10_4)
2521 unsigned long exe_path_size = CM_EXE_PATH_LOCAL_SIZE;
2522 # else
2523 uint32_t exe_path_size = CM_EXE_PATH_LOCAL_SIZE;
2524 # endif
2525 # undef CM_EXE_PATH_LOCAL_SIZE
2526 char* exe_path = exe_path_local;
2527 if (_NSGetExecutablePath(exe_path, &exe_path_size) < 0) {
2528 exe_path = static_cast<char*>(malloc(exe_path_size));
2529 _NSGetExecutablePath(exe_path, &exe_path_size);
2531 exe_dir =
2532 cmSystemTools::GetFilenamePath(cmSystemTools::GetRealPath(exe_path));
2533 if (exe_path != exe_path_local) {
2534 free(exe_path);
2536 if (cmSystemTools::GetFilenameName(exe_dir) == "MacOS") {
2537 // The executable is inside an application bundle.
2538 // Look for ..<CMAKE_BIN_DIR> (install tree) and then fall back to
2539 // ../../../bin (build tree).
2540 exe_dir = cmSystemTools::GetFilenamePath(exe_dir);
2541 if (cmSystemTools::FileExists(exe_dir + CMAKE_BIN_DIR "/cmake")) {
2542 exe_dir += CMAKE_BIN_DIR;
2543 } else {
2544 exe_dir = cmSystemTools::GetFilenamePath(exe_dir);
2545 exe_dir = cmSystemTools::GetFilenamePath(exe_dir);
2548 #else
2549 std::string errorMsg;
2550 std::string exe;
2551 if (cmSystemTools::FindProgramPath(argv0, exe, errorMsg)) {
2552 // remove symlinks
2553 exe = cmSystemTools::GetRealPath(exe);
2554 exe_dir = cmSystemTools::GetFilenamePath(exe);
2555 } else {
2556 // ???
2558 #endif
2559 exe_dir = cmSystemTools::GetActualCaseForPath(exe_dir);
2560 cmSystemToolsCMakeCommand =
2561 cmStrCat(exe_dir, "/cmake", cmSystemTools::GetExecutableExtension());
2562 #ifdef CMAKE_BOOTSTRAP
2563 // The bootstrap cmake does not provide the other tools,
2564 // so use the directory where they are about to be built.
2565 exe_dir = CMAKE_BOOTSTRAP_BINARY_DIR "/bin";
2566 #endif
2567 cmSystemToolsCTestCommand =
2568 cmStrCat(exe_dir, "/ctest", cmSystemTools::GetExecutableExtension());
2569 cmSystemToolsCPackCommand =
2570 cmStrCat(exe_dir, "/cpack", cmSystemTools::GetExecutableExtension());
2571 cmSystemToolsCMakeGUICommand =
2572 cmStrCat(exe_dir, "/cmake-gui", cmSystemTools::GetExecutableExtension());
2573 if (!cmSystemTools::FileExists(cmSystemToolsCMakeGUICommand)) {
2574 cmSystemToolsCMakeGUICommand.clear();
2576 cmSystemToolsCMakeCursesCommand =
2577 cmStrCat(exe_dir, "/ccmake", cmSystemTools::GetExecutableExtension());
2578 if (!cmSystemTools::FileExists(cmSystemToolsCMakeCursesCommand)) {
2579 cmSystemToolsCMakeCursesCommand.clear();
2581 cmSystemToolsCMClDepsCommand =
2582 cmStrCat(exe_dir, "/cmcldeps", cmSystemTools::GetExecutableExtension());
2583 if (!cmSystemTools::FileExists(cmSystemToolsCMClDepsCommand)) {
2584 cmSystemToolsCMClDepsCommand.clear();
2587 #ifndef CMAKE_BOOTSTRAP
2588 // Install tree has
2589 // - "<prefix><CMAKE_BIN_DIR>/cmake"
2590 // - "<prefix><CMAKE_DATA_DIR>"
2591 // - "<prefix><CMAKE_DOC_DIR>"
2592 if (cmHasLiteralSuffix(exe_dir, CMAKE_BIN_DIR)) {
2593 std::string const prefix =
2594 exe_dir.substr(0, exe_dir.size() - cmStrLen(CMAKE_BIN_DIR));
2595 cmSystemToolsCMakeRoot = cmStrCat(prefix, CMAKE_DATA_DIR);
2596 if (cmSystemTools::FileExists(
2597 cmStrCat(prefix, CMAKE_DOC_DIR "/html/index.html"))) {
2598 cmSystemToolsHTMLDoc = cmStrCat(prefix, CMAKE_DOC_DIR "/html");
2601 if (cmSystemToolsCMakeRoot.empty() ||
2602 !cmSystemTools::FileExists(
2603 cmStrCat(cmSystemToolsCMakeRoot, "/Modules/CMake.cmake"))) {
2604 // Build tree has "<build>/bin[/<config>]/cmake" and
2605 // "<build>/CMakeFiles/CMakeSourceDir.txt".
2606 std::string dir = cmSystemTools::GetFilenamePath(exe_dir);
2607 std::string src_dir_txt = cmStrCat(dir, "/CMakeFiles/CMakeSourceDir.txt");
2608 cmsys::ifstream fin(src_dir_txt.c_str());
2609 std::string src_dir;
2610 if (fin && cmSystemTools::GetLineFromStream(fin, src_dir) &&
2611 cmSystemTools::FileIsDirectory(src_dir)) {
2612 cmSystemToolsCMakeRoot = src_dir;
2613 } else {
2614 dir = cmSystemTools::GetFilenamePath(dir);
2615 src_dir_txt = cmStrCat(dir, "/CMakeFiles/CMakeSourceDir.txt");
2616 cmsys::ifstream fin2(src_dir_txt.c_str());
2617 if (fin2 && cmSystemTools::GetLineFromStream(fin2, src_dir) &&
2618 cmSystemTools::FileIsDirectory(src_dir)) {
2619 cmSystemToolsCMakeRoot = src_dir;
2622 if (!cmSystemToolsCMakeRoot.empty() && cmSystemToolsHTMLDoc.empty() &&
2623 cmSystemTools::FileExists(
2624 cmStrCat(dir, "/Utilities/Sphinx/html/index.html"))) {
2625 cmSystemToolsHTMLDoc = cmStrCat(dir, "/Utilities/Sphinx/html");
2628 #else
2629 // Bootstrap build knows its source.
2630 cmSystemToolsCMakeRoot = CMAKE_BOOTSTRAP_SOURCE_DIR;
2631 #endif
2634 std::string const& cmSystemTools::GetCMakeCommand()
2636 return cmSystemToolsCMakeCommand;
2639 std::string const& cmSystemTools::GetCTestCommand()
2641 return cmSystemToolsCTestCommand;
2644 std::string const& cmSystemTools::GetCPackCommand()
2646 return cmSystemToolsCPackCommand;
2649 std::string const& cmSystemTools::GetCMakeCursesCommand()
2651 return cmSystemToolsCMakeCursesCommand;
2654 std::string const& cmSystemTools::GetCMakeGUICommand()
2656 return cmSystemToolsCMakeGUICommand;
2659 std::string const& cmSystemTools::GetCMClDepsCommand()
2661 return cmSystemToolsCMClDepsCommand;
2664 std::string const& cmSystemTools::GetCMakeRoot()
2666 return cmSystemToolsCMakeRoot;
2669 std::string const& cmSystemTools::GetHTMLDoc()
2671 return cmSystemToolsHTMLDoc;
2674 std::string cmSystemTools::GetCurrentWorkingDirectory()
2676 return cmSystemTools::CollapseFullPath(
2677 cmsys::SystemTools::GetCurrentWorkingDirectory());
2680 void cmSystemTools::MakefileColorEcho(int color, const char* message,
2681 bool newline, bool enabled)
2683 // On some platforms (an MSYS prompt) cmsysTerminal may not be able
2684 // to determine whether the stream is displayed on a tty. In this
2685 // case it assumes no unless we tell it otherwise. Since we want
2686 // color messages to be displayed for users we will assume yes.
2687 // However, we can test for some situations when the answer is most
2688 // likely no.
2689 int assumeTTY = cmsysTerminal_Color_AssumeTTY;
2690 if (cmSystemTools::HasEnv("DART_TEST_FROM_DART") ||
2691 cmSystemTools::HasEnv("DASHBOARD_TEST_FROM_CTEST") ||
2692 cmSystemTools::HasEnv("CTEST_INTERACTIVE_DEBUG_MODE")) {
2693 // Avoid printing color escapes during dashboard builds.
2694 assumeTTY = 0;
2697 if (enabled && color != cmsysTerminal_Color_Normal) {
2698 // Print with color. Delay the newline until later so that
2699 // all color restore sequences appear before it.
2700 cmsysTerminal_cfprintf(color | assumeTTY, stdout, "%s", message);
2701 } else {
2702 // Color is disabled. Print without color.
2703 fprintf(stdout, "%s", message);
2706 if (newline) {
2707 fprintf(stdout, "\n");
2711 bool cmSystemTools::GuessLibrarySOName(std::string const& fullPath,
2712 std::string& soname)
2714 // For ELF shared libraries use a real parser to get the correct
2715 // soname.
2716 cmELF elf(fullPath.c_str());
2717 if (elf) {
2718 return elf.GetSOName(soname);
2721 // If the file is not a symlink we have no guess for its soname.
2722 if (!cmSystemTools::FileIsSymlink(fullPath)) {
2723 return false;
2725 if (!cmSystemTools::ReadSymlink(fullPath, soname)) {
2726 return false;
2729 // If the symlink has a path component we have no guess for the soname.
2730 if (!cmSystemTools::GetFilenamePath(soname).empty()) {
2731 return false;
2734 // If the symlink points at an extended version of the same name
2735 // assume it is the soname.
2736 std::string name = cmSystemTools::GetFilenameName(fullPath);
2737 return soname.length() > name.length() &&
2738 soname.compare(0, name.length(), name) == 0;
2741 bool cmSystemTools::GuessLibraryInstallName(std::string const& fullPath,
2742 std::string& soname)
2744 #if defined(CMake_USE_MACH_PARSER)
2745 cmMachO macho(fullPath.c_str());
2746 if (macho) {
2747 return macho.GetInstallName(soname);
2749 #else
2750 (void)fullPath;
2751 (void)soname;
2752 #endif
2754 return false;
2757 static std::string::size_type cmSystemToolsFindRPath(
2758 cm::string_view const& have, cm::string_view const& want)
2760 std::string::size_type pos = 0;
2761 while (pos < have.size()) {
2762 // Look for an occurrence of the string.
2763 std::string::size_type const beg = have.find(want, pos);
2764 if (beg == std::string::npos) {
2765 return std::string::npos;
2768 // Make sure it is separated from preceding entries.
2769 if (beg > 0 && have[beg - 1] != ':') {
2770 pos = beg + 1;
2771 continue;
2774 // Make sure it is separated from following entries.
2775 std::string::size_type const end = beg + want.size();
2776 if (end < have.size() && have[end] != ':') {
2777 pos = beg + 1;
2778 continue;
2781 // Return the position of the path portion.
2782 return beg;
2785 // The desired rpath was not found.
2786 return std::string::npos;
2789 namespace {
2790 struct cmSystemToolsRPathInfo
2792 unsigned long Position;
2793 unsigned long Size;
2794 std::string Name;
2795 std::string Value;
2798 using EmptyCallback = std::function<bool(std::string*, const cmELF&)>;
2799 using AdjustCallback = std::function<bool(
2800 cm::optional<std::string>&, const std::string&, const char*, std::string*)>;
2802 cm::optional<bool> AdjustRPathELF(std::string const& file,
2803 const EmptyCallback& emptyCallback,
2804 const AdjustCallback& adjustCallback,
2805 std::string* emsg, bool* changed)
2807 if (changed) {
2808 *changed = false;
2810 int rp_count = 0;
2811 bool remove_rpath = true;
2812 cmSystemToolsRPathInfo rp[2];
2814 // Parse the ELF binary.
2815 cmELF elf(file.c_str());
2816 if (!elf) {
2817 return cm::nullopt; // Not a valid ELF file.
2820 // Get the RPATH and RUNPATH entries from it.
2821 int se_count = 0;
2822 cmELF::StringEntry const* se[2] = { nullptr, nullptr };
2823 const char* se_name[2] = { nullptr, nullptr };
2824 if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
2825 se[se_count] = se_rpath;
2826 se_name[se_count] = "RPATH";
2827 ++se_count;
2829 if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
2830 se[se_count] = se_runpath;
2831 se_name[se_count] = "RUNPATH";
2832 ++se_count;
2834 if (se_count == 0) {
2835 return emptyCallback(emsg, elf);
2838 for (int i = 0; i < se_count; ++i) {
2839 // If both RPATH and RUNPATH refer to the same string literal it
2840 // needs to be changed only once.
2841 if (rp_count && rp[0].Position == se[i]->Position) {
2842 continue;
2845 // Store information about the entry in the file.
2846 rp[rp_count].Position = se[i]->Position;
2847 rp[rp_count].Size = se[i]->Size;
2848 rp[rp_count].Name = se_name[i];
2850 // Adjust the rpath.
2851 cm::optional<std::string> outRPath;
2852 if (!adjustCallback(outRPath, se[i]->Value, se_name[i], emsg)) {
2853 return false;
2856 if (outRPath) {
2857 if (!outRPath->empty()) {
2858 remove_rpath = false;
2861 // Make sure there is enough room to store the new rpath and at
2862 // least one null terminator.
2863 if (rp[rp_count].Size < outRPath->length() + 1) {
2864 if (emsg) {
2865 *emsg = cmStrCat("The replacement path is too long for the ",
2866 se_name[i], " entry.");
2868 return false;
2871 // This entry is ready for update.
2872 rp[rp_count].Value = std::move(*outRPath);
2873 ++rp_count;
2874 } else {
2875 remove_rpath = false;
2880 // If no runtime path needs to be changed, we are done.
2881 if (rp_count == 0) {
2882 return true;
2885 // If the resulting rpath is empty, just remove the entire entry instead.
2886 if (remove_rpath) {
2887 return cmSystemTools::RemoveRPath(file, emsg, changed);
2891 // Open the file for update.
2892 cmsys::ofstream f(file.c_str(),
2893 std::ios::in | std::ios::out | std::ios::binary);
2894 if (!f) {
2895 if (emsg) {
2896 *emsg = "Error opening file for update.";
2898 return false;
2901 // Store the new RPATH and RUNPATH strings.
2902 for (int i = 0; i < rp_count; ++i) {
2903 // Seek to the RPATH position.
2904 if (!f.seekp(rp[i].Position)) {
2905 if (emsg) {
2906 *emsg = cmStrCat("Error seeking to ", rp[i].Name, " position.");
2908 return false;
2911 // Write the new rpath. Follow it with enough null terminators to
2912 // fill the string table entry.
2913 f << rp[i].Value;
2914 for (unsigned long j = rp[i].Value.length(); j < rp[i].Size; ++j) {
2915 f << '\0';
2918 // Make sure it wrote correctly.
2919 if (!f) {
2920 if (emsg) {
2921 *emsg = cmStrCat("Error writing the new ", rp[i].Name,
2922 " string to the file.");
2924 return false;
2929 // Everything was updated successfully.
2930 if (changed) {
2931 *changed = true;
2933 return true;
2936 std::function<bool(std::string*, const cmELF&)> MakeEmptyCallback(
2937 const std::string& newRPath)
2939 return [newRPath](std::string* emsg, const cmELF& elf) -> bool {
2940 if (newRPath.empty()) {
2941 // The new rpath is empty and there is no rpath anyway so it is
2942 // okay.
2943 return true;
2945 if (emsg) {
2946 *emsg =
2947 cmStrCat("No valid ELF RPATH or RUNPATH entry exists in the file; ",
2948 elf.GetErrorMessage());
2950 return false;
2955 static cm::optional<bool> ChangeRPathELF(std::string const& file,
2956 std::string const& oldRPath,
2957 std::string const& newRPath,
2958 bool removeEnvironmentRPath,
2959 std::string* emsg, bool* changed)
2961 auto adjustCallback = [oldRPath, newRPath, removeEnvironmentRPath](
2962 cm::optional<std::string>& outRPath,
2963 const std::string& inRPath, const char* se_name,
2964 std::string* emsg2) -> bool {
2965 // Make sure the current rpath contains the old rpath.
2966 std::string::size_type pos = cmSystemToolsFindRPath(inRPath, oldRPath);
2967 if (pos == std::string::npos) {
2968 // If it contains the new rpath instead then it is okay.
2969 if (cmSystemToolsFindRPath(inRPath, newRPath) != std::string::npos) {
2970 return true;
2972 if (emsg2) {
2973 std::ostringstream e;
2974 /* clang-format off */
2975 e << "The current " << se_name << " is:\n"
2976 << " " << inRPath << "\n"
2977 << "which does not contain:\n"
2978 << " " << oldRPath << "\n"
2979 << "as was expected.";
2980 /* clang-format on */
2981 *emsg2 = e.str();
2983 return false;
2986 std::string::size_type prefix_len = pos;
2988 // If oldRPath was at the end of the file's RPath, and newRPath is empty,
2989 // we should remove the unnecessary ':' at the end.
2990 if (newRPath.empty() && pos > 0 && inRPath[pos - 1] == ':' &&
2991 pos + oldRPath.length() == inRPath.length()) {
2992 prefix_len--;
2995 // Construct the new value which preserves the part of the path
2996 // not being changed.
2997 outRPath.emplace();
2998 if (!removeEnvironmentRPath) {
2999 *outRPath += inRPath.substr(0, prefix_len);
3001 *outRPath += newRPath;
3002 *outRPath += inRPath.substr(pos + oldRPath.length());
3004 return true;
3007 return AdjustRPathELF(file, MakeEmptyCallback(newRPath), adjustCallback,
3008 emsg, changed);
3011 static cm::optional<bool> SetRPathELF(std::string const& file,
3012 std::string const& newRPath,
3013 std::string* emsg, bool* changed)
3015 auto adjustCallback = [newRPath](cm::optional<std::string>& outRPath,
3016 const std::string& inRPath,
3017 const char* /*se_name*/, std::string*
3018 /*emsg*/) -> bool {
3019 if (inRPath != newRPath) {
3020 outRPath = newRPath;
3022 return true;
3025 return AdjustRPathELF(file, MakeEmptyCallback(newRPath), adjustCallback,
3026 emsg, changed);
3028 static cm::optional<bool> ChangeRPathXCOFF(std::string const& file,
3029 std::string const& oldRPath,
3030 std::string const& newRPath,
3031 bool removeEnvironmentRPath,
3032 std::string* emsg, bool* changed)
3034 if (changed) {
3035 *changed = false;
3037 #if !defined(CMake_USE_XCOFF_PARSER)
3038 (void)file;
3039 (void)oldRPath;
3040 (void)newRPath;
3041 (void)removeEnvironmentRPath;
3042 (void)emsg;
3043 return cm::nullopt;
3044 #else
3045 bool chg = false;
3046 cmXCOFF xcoff(file.c_str(), cmXCOFF::Mode::ReadWrite);
3047 if (!xcoff) {
3048 return cm::nullopt; // Not a valid XCOFF file
3050 if (cm::optional<cm::string_view> maybeLibPath = xcoff.GetLibPath()) {
3051 cm::string_view libPath = *maybeLibPath;
3052 // Make sure the current rpath contains the old rpath.
3053 std::string::size_type pos = cmSystemToolsFindRPath(libPath, oldRPath);
3054 if (pos == std::string::npos) {
3055 // If it contains the new rpath instead then it is okay.
3056 if (cmSystemToolsFindRPath(libPath, newRPath) != std::string::npos) {
3057 return true;
3059 if (emsg) {
3060 std::ostringstream e;
3061 /* clang-format off */
3062 e << "The current RPATH is:\n"
3063 << " " << libPath << "\n"
3064 << "which does not contain:\n"
3065 << " " << oldRPath << "\n"
3066 << "as was expected.";
3067 /* clang-format on */
3068 *emsg = e.str();
3070 return false;
3073 // The prefix is either empty or ends in a ':'.
3074 cm::string_view prefix = libPath.substr(0, pos);
3075 if (newRPath.empty() && !prefix.empty()) {
3076 prefix.remove_suffix(1);
3079 // The suffix is either empty or starts in a ':'.
3080 cm::string_view suffix = libPath.substr(pos + oldRPath.length());
3082 // Construct the new value which preserves the part of the path
3083 // not being changed.
3084 std::string newLibPath;
3085 if (!removeEnvironmentRPath) {
3086 newLibPath = std::string(prefix);
3088 newLibPath += newRPath;
3089 newLibPath += suffix;
3091 chg = xcoff.SetLibPath(newLibPath);
3093 if (!xcoff) {
3094 if (emsg) {
3095 *emsg = xcoff.GetErrorMessage();
3097 return false;
3100 // Everything was updated successfully.
3101 if (changed) {
3102 *changed = chg;
3104 return true;
3105 #endif
3108 static cm::optional<bool> SetRPathXCOFF(std::string const& /*file*/,
3109 std::string const& /*newRPath*/,
3110 std::string* /*emsg*/,
3111 bool* /*changed*/)
3113 return cm::nullopt; // Not implemented.
3116 bool cmSystemTools::ChangeRPath(std::string const& file,
3117 std::string const& oldRPath,
3118 std::string const& newRPath,
3119 bool removeEnvironmentRPath, std::string* emsg,
3120 bool* changed)
3122 if (cm::optional<bool> result = ChangeRPathELF(
3123 file, oldRPath, newRPath, removeEnvironmentRPath, emsg, changed)) {
3124 return result.value();
3126 if (cm::optional<bool> result = ChangeRPathXCOFF(
3127 file, oldRPath, newRPath, removeEnvironmentRPath, emsg, changed)) {
3128 return result.value();
3130 // The file format is not recognized. Assume it has no RPATH.
3131 if (newRPath.empty()) {
3132 // The caller wanted no RPATH anyway.
3133 return true;
3135 if (emsg) {
3136 *emsg = "The file format is not recognized.";
3138 return false;
3141 bool cmSystemTools::SetRPath(std::string const& file,
3142 std::string const& newRPath, std::string* emsg,
3143 bool* changed)
3145 if (cm::optional<bool> result = SetRPathELF(file, newRPath, emsg, changed)) {
3146 return result.value();
3148 if (cm::optional<bool> result =
3149 SetRPathXCOFF(file, newRPath, emsg, changed)) {
3150 return result.value();
3152 // The file format is not recognized. Assume it has no RPATH.
3153 if (newRPath.empty()) {
3154 // The caller wanted no RPATH anyway.
3155 return true;
3157 if (emsg) {
3158 *emsg = "The file format is not recognized.";
3160 return false;
3163 namespace {
3164 bool VersionCompare(cmSystemTools::CompareOp op, const char* lhss,
3165 const char* rhss)
3167 const char* endl = lhss;
3168 const char* endr = rhss;
3170 while (((*endl >= '0') && (*endl <= '9')) ||
3171 ((*endr >= '0') && (*endr <= '9'))) {
3172 // Do component-wise comparison, ignoring leading zeros
3173 // (components are treated as integers, not as mantissas)
3174 while (*endl == '0') {
3175 endl++;
3177 while (*endr == '0') {
3178 endr++;
3181 const char* beginl = endl;
3182 const char* beginr = endr;
3184 // count significant digits
3185 while ((*endl >= '0') && (*endl <= '9')) {
3186 endl++;
3188 while ((*endr >= '0') && (*endr <= '9')) {
3189 endr++;
3192 // compare number of digits first
3193 ptrdiff_t r = ((endl - beginl) - (endr - beginr));
3194 if (r == 0) {
3195 // compare the digits if number of digits is equal
3196 r = strncmp(beginl, beginr, endl - beginl);
3199 if (r < 0) {
3200 // lhs < rhs, so true if operation is LESS
3201 return (op & cmSystemTools::OP_LESS) != 0;
3203 if (r > 0) {
3204 // lhs > rhs, so true if operation is GREATER
3205 return (op & cmSystemTools::OP_GREATER) != 0;
3208 if (*endr == '.') {
3209 endr++;
3212 if (*endl == '.') {
3213 endl++;
3216 // lhs == rhs, so true if operation is EQUAL
3217 return (op & cmSystemTools::OP_EQUAL) != 0;
3221 bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
3222 const std::string& lhs,
3223 const std::string& rhs)
3225 return ::VersionCompare(op, lhs.c_str(), rhs.c_str());
3227 bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
3228 const std::string& lhs, const char rhs[])
3230 return ::VersionCompare(op, lhs.c_str(), rhs);
3233 bool cmSystemTools::VersionCompareEqual(std::string const& lhs,
3234 std::string const& rhs)
3236 return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, lhs, rhs);
3239 bool cmSystemTools::VersionCompareGreater(std::string const& lhs,
3240 std::string const& rhs)
3242 return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER, lhs, rhs);
3245 bool cmSystemTools::VersionCompareGreaterEq(std::string const& lhs,
3246 std::string const& rhs)
3248 return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, lhs,
3249 rhs);
3252 static size_t cm_strverscmp_find_first_difference_or_end(const char* lhs,
3253 const char* rhs)
3255 size_t i = 0;
3256 /* Step forward until we find a difference or both strings end together.
3257 The difference may lie on the null-terminator of one string. */
3258 while (lhs[i] == rhs[i] && lhs[i] != 0) {
3259 ++i;
3261 return i;
3264 static size_t cm_strverscmp_find_digits_begin(const char* s, size_t i)
3266 /* Step back until we are not preceded by a digit. */
3267 while (i > 0 && isdigit(s[i - 1])) {
3268 --i;
3270 return i;
3273 static size_t cm_strverscmp_find_digits_end(const char* s, size_t i)
3275 /* Step forward over digits. */
3276 while (isdigit(s[i])) {
3277 ++i;
3279 return i;
3282 static size_t cm_strverscmp_count_leading_zeros(const char* s, size_t b)
3284 size_t i = b;
3285 /* Step forward over zeros that are followed by another digit. */
3286 while (s[i] == '0' && isdigit(s[i + 1])) {
3287 ++i;
3289 return i - b;
3292 static int cm_strverscmp(const char* lhs, const char* rhs)
3294 size_t const i = cm_strverscmp_find_first_difference_or_end(lhs, rhs);
3295 if (lhs[i] != rhs[i]) {
3296 /* The strings differ starting at 'i'. Check for a digit sequence. */
3297 size_t const b = cm_strverscmp_find_digits_begin(lhs, i);
3298 if (b != i || (isdigit(lhs[i]) && isdigit(rhs[i]))) {
3299 /* A digit sequence starts at 'b', preceding or at 'i'. */
3301 /* Look for leading zeros, implying a leading decimal point. */
3302 size_t const lhs_zeros = cm_strverscmp_count_leading_zeros(lhs, b);
3303 size_t const rhs_zeros = cm_strverscmp_count_leading_zeros(rhs, b);
3304 if (lhs_zeros != rhs_zeros) {
3305 /* The side with more leading zeros orders first. */
3306 return rhs_zeros > lhs_zeros ? 1 : -1;
3308 if (lhs_zeros == 0) {
3309 /* No leading zeros; compare digit sequence lengths. */
3310 size_t const lhs_end = cm_strverscmp_find_digits_end(lhs, i);
3311 size_t const rhs_end = cm_strverscmp_find_digits_end(rhs, i);
3312 if (lhs_end != rhs_end) {
3313 /* The side with fewer digits orders first. */
3314 return lhs_end > rhs_end ? 1 : -1;
3320 /* Ordering was not decided by digit sequence lengths; compare bytes. */
3321 return lhs[i] - rhs[i];
3324 int cmSystemTools::strverscmp(std::string const& lhs, std::string const& rhs)
3326 return cm_strverscmp(lhs.c_str(), rhs.c_str());
3329 static cm::optional<bool> RemoveRPathELF(std::string const& file,
3330 std::string* emsg, bool* removed)
3332 if (removed) {
3333 *removed = false;
3335 int zeroCount = 0;
3336 unsigned long zeroPosition[2] = { 0, 0 };
3337 unsigned long zeroSize[2] = { 0, 0 };
3338 unsigned long bytesBegin = 0;
3339 std::vector<char> bytes;
3341 // Parse the ELF binary.
3342 cmELF elf(file.c_str());
3343 if (!elf) {
3344 return cm::nullopt; // Not a valid ELF file.
3347 // Get the RPATH and RUNPATH entries from it and sort them by index
3348 // in the dynamic section header.
3349 int se_count = 0;
3350 cmELF::StringEntry const* se[2] = { nullptr, nullptr };
3351 if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
3352 se[se_count++] = se_rpath;
3354 if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
3355 se[se_count++] = se_runpath;
3357 if (se_count == 0) {
3358 // There is no RPATH or RUNPATH anyway.
3359 return true;
3361 if (se_count == 2 && se[1]->IndexInSection < se[0]->IndexInSection) {
3362 std::swap(se[0], se[1]);
3365 // Obtain a copy of the dynamic entries
3366 cmELF::DynamicEntryList dentries = elf.GetDynamicEntries();
3367 if (dentries.empty()) {
3368 // This should happen only for invalid ELF files where a DT_NULL
3369 // appears before the end of the table.
3370 if (emsg) {
3371 *emsg = "DYNAMIC section contains a DT_NULL before the end.";
3373 return false;
3376 // Save information about the string entries to be zeroed.
3377 zeroCount = se_count;
3378 for (int i = 0; i < se_count; ++i) {
3379 zeroPosition[i] = se[i]->Position;
3380 zeroSize[i] = se[i]->Size;
3383 // Get size of one DYNAMIC entry
3384 unsigned long const sizeof_dentry =
3385 elf.GetDynamicEntryPosition(1) - elf.GetDynamicEntryPosition(0);
3387 // Adjust the entry list as necessary to remove the run path
3388 unsigned long entriesErased = 0;
3389 for (auto it = dentries.begin(); it != dentries.end();) {
3390 if (it->first == cmELF::TagRPath || it->first == cmELF::TagRunPath) {
3391 it = dentries.erase(it);
3392 entriesErased++;
3393 continue;
3395 if (it->first == cmELF::TagMipsRldMapRel && elf.IsMIPS()) {
3396 // Background: debuggers need to know the "linker map" which contains
3397 // the addresses each dynamic object is loaded at. Most arches use
3398 // the DT_DEBUG tag which the dynamic linker writes to (directly) and
3399 // contain the location of the linker map, however on MIPS the
3400 // .dynamic section is always read-only so this is not possible. MIPS
3401 // objects instead contain a DT_MIPS_RLD_MAP tag which contains the
3402 // address where the dynamic linker will write to (an indirect
3403 // version of DT_DEBUG). Since this doesn't work when using PIE, a
3404 // relative equivalent was created - DT_MIPS_RLD_MAP_REL. Since this
3405 // version contains a relative offset, moving it changes the
3406 // calculated address. This may cause the dynamic linker to write
3407 // into memory it should not be changing.
3409 // To fix this, we adjust the value of DT_MIPS_RLD_MAP_REL here. If
3410 // we move it up by n bytes, we add n bytes to the value of this tag.
3411 it->second += entriesErased * sizeof_dentry;
3414 it++;
3417 // Encode new entries list
3418 bytes = elf.EncodeDynamicEntries(dentries);
3419 bytesBegin = elf.GetDynamicEntryPosition(0);
3422 // Open the file for update.
3423 cmsys::ofstream f(file.c_str(),
3424 std::ios::in | std::ios::out | std::ios::binary);
3425 if (!f) {
3426 if (emsg) {
3427 *emsg = "Error opening file for update.";
3429 return false;
3432 // Write the new DYNAMIC table header.
3433 if (!f.seekp(bytesBegin)) {
3434 if (emsg) {
3435 *emsg = "Error seeking to DYNAMIC table header for RPATH.";
3437 return false;
3439 if (!f.write(bytes.data(), bytes.size())) {
3440 if (emsg) {
3441 *emsg = "Error replacing DYNAMIC table header.";
3443 return false;
3446 // Fill the RPATH and RUNPATH strings with zero bytes.
3447 for (int i = 0; i < zeroCount; ++i) {
3448 if (!f.seekp(zeroPosition[i])) {
3449 if (emsg) {
3450 *emsg = "Error seeking to RPATH position.";
3452 return false;
3454 for (unsigned long j = 0; j < zeroSize[i]; ++j) {
3455 f << '\0';
3457 if (!f) {
3458 if (emsg) {
3459 *emsg = "Error writing the empty rpath string to the file.";
3461 return false;
3465 // Everything was updated successfully.
3466 if (removed) {
3467 *removed = true;
3469 return true;
3472 static cm::optional<bool> RemoveRPathXCOFF(std::string const& file,
3473 std::string* emsg, bool* removed)
3475 if (removed) {
3476 *removed = false;
3478 #if !defined(CMake_USE_XCOFF_PARSER)
3479 (void)file;
3480 (void)emsg;
3481 return cm::nullopt; // Cannot handle XCOFF files.
3482 #else
3483 cmXCOFF xcoff(file.c_str(), cmXCOFF::Mode::ReadWrite);
3484 if (!xcoff) {
3485 return cm::nullopt; // Not a valid XCOFF file.
3487 bool rm = xcoff.RemoveLibPath();
3488 if (!xcoff) {
3489 if (emsg) {
3490 *emsg = xcoff.GetErrorMessage();
3492 return false;
3495 if (removed) {
3496 *removed = rm;
3498 return true;
3499 #endif
3501 bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg,
3502 bool* removed)
3504 if (cm::optional<bool> result = RemoveRPathELF(file, emsg, removed)) {
3505 return result.value();
3507 if (cm::optional<bool> result = RemoveRPathXCOFF(file, emsg, removed)) {
3508 return result.value();
3510 // The file format is not recognized. Assume it has no RPATH.
3511 return true;
3514 bool cmSystemTools::CheckRPath(std::string const& file,
3515 std::string const& newRPath)
3517 // Parse the ELF binary.
3518 cmELF elf(file.c_str());
3519 if (elf) {
3520 // Get the RPATH or RUNPATH entry from it.
3521 cmELF::StringEntry const* se = elf.GetRPath();
3522 if (!se) {
3523 se = elf.GetRunPath();
3526 // Make sure the current rpath contains the new rpath.
3527 if (newRPath.empty()) {
3528 if (!se) {
3529 return true;
3531 } else {
3532 if (se &&
3533 cmSystemToolsFindRPath(se->Value, newRPath) != std::string::npos) {
3534 return true;
3537 return false;
3539 #if defined(CMake_USE_XCOFF_PARSER)
3540 // Parse the XCOFF binary.
3541 cmXCOFF xcoff(file.c_str());
3542 if (xcoff) {
3543 if (cm::optional<cm::string_view> libPath = xcoff.GetLibPath()) {
3544 if (cmSystemToolsFindRPath(*libPath, newRPath) != std::string::npos) {
3545 return true;
3548 return false;
3550 #endif
3551 // The file format is not recognized. Assume it has no RPATH.
3552 // Therefore we succeed if the new rpath is empty anyway.
3553 return newRPath.empty();
3556 bool cmSystemTools::RepeatedRemoveDirectory(const std::string& dir)
3558 #ifdef _WIN32
3559 // Windows sometimes locks files temporarily so try a few times.
3560 WindowsFileRetry retry = cmSystemTools::GetWindowsFileRetry();
3562 for (unsigned int i = 0; i < retry.Count; ++i) {
3563 if (cmSystemTools::RemoveADirectory(dir)) {
3564 return true;
3566 cmSystemTools::Delay(retry.Delay);
3568 return false;
3569 #else
3570 return static_cast<bool>(cmSystemTools::RemoveADirectory(dir));
3571 #endif
3574 std::string cmSystemTools::EncodeURL(std::string const& in, bool escapeSlashes)
3576 std::string out;
3577 for (char c : in) {
3578 char hexCh[4] = { 0, 0, 0, 0 };
3579 hexCh[0] = c;
3580 switch (c) {
3581 case '+':
3582 case '?':
3583 case '\\':
3584 case '&':
3585 case ' ':
3586 case '=':
3587 case '%':
3588 snprintf(hexCh, sizeof(hexCh), "%%%02X", static_cast<int>(c));
3589 break;
3590 case '/':
3591 if (escapeSlashes) {
3592 strcpy(hexCh, "%2F");
3594 break;
3595 default:
3596 break;
3598 out.append(hexCh);
3600 return out;
3603 cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName,
3604 std::string const& newName)
3606 cmsys::Status status =
3607 cmSystemTools::CreateSymlinkQuietly(origName, newName);
3608 if (!status) {
3609 cmSystemTools::Error(cmStrCat("failed to create symbolic link '", newName,
3610 "': ", status.GetString()));
3612 return status;
3615 cmsys::Status cmSystemTools::CreateSymlinkQuietly(std::string const& origName,
3616 std::string const& newName)
3618 uv_fs_t req;
3619 int flags = 0;
3620 #if defined(_WIN32)
3621 if (cmsys::SystemTools::FileIsDirectory(origName)) {
3622 flags |= UV_FS_SYMLINK_DIR;
3624 #endif
3625 int err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(),
3626 flags, nullptr);
3627 cmsys::Status status;
3628 if (err) {
3629 #if defined(_WIN32)
3630 status = cmsys::Status::Windows(uv_fs_get_system_error(&req));
3631 #elif UV_VERSION_MAJOR > 1 || (UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR >= 38)
3632 status = cmsys::Status::POSIX(uv_fs_get_system_error(&req));
3633 #else
3634 status = cmsys::Status::POSIX(-err);
3635 #endif
3637 return status;
3640 cmsys::Status cmSystemTools::CreateLink(std::string const& origName,
3641 std::string const& newName)
3643 cmsys::Status status = cmSystemTools::CreateLinkQuietly(origName, newName);
3644 if (!status) {
3645 cmSystemTools::Error(
3646 cmStrCat("failed to create link '", newName, "': ", status.GetString()));
3648 return status;
3651 cmsys::Status cmSystemTools::CreateLinkQuietly(std::string const& origName,
3652 std::string const& newName)
3654 uv_fs_t req;
3655 int err =
3656 uv_fs_link(nullptr, &req, origName.c_str(), newName.c_str(), nullptr);
3657 cmsys::Status status;
3658 if (err) {
3659 #if defined(_WIN32)
3660 status = cmsys::Status::Windows(uv_fs_get_system_error(&req));
3661 #elif UV_VERSION_MAJOR > 1 || (UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR >= 38)
3662 status = cmsys::Status::POSIX(uv_fs_get_system_error(&req));
3663 #else
3664 status = cmsys::Status::POSIX(-err);
3665 #endif
3667 return status;
3670 cm::string_view cmSystemTools::GetSystemName()
3672 #if defined(_WIN32)
3673 return "Windows";
3674 #elif defined(__MSYS__)
3675 return "MSYS";
3676 #elif defined(__CYGWIN__)
3677 return "CYGWIN";
3678 #elif defined(__ANDROID__)
3679 return "Android";
3680 #else
3681 static struct utsname uts_name;
3682 static bool initialized = false;
3683 static cm::string_view systemName;
3684 if (initialized) {
3685 return systemName;
3687 if (uname(&uts_name) >= 0) {
3688 initialized = true;
3689 systemName = uts_name.sysname;
3691 if (cmIsOff(systemName)) {
3692 systemName = "UnknownOS";
3695 // fix for BSD/OS, remove the /
3696 static const cmsys::RegularExpression bsdOsRegex("BSD.OS");
3697 cmsys::RegularExpressionMatch match;
3698 if (bsdOsRegex.find(uts_name.sysname, match)) {
3699 systemName = "BSDOS";
3702 // fix for GNU/kFreeBSD, remove the GNU/
3703 if (systemName.find("kFreeBSD") != cm::string_view::npos) {
3704 systemName = "kFreeBSD";
3706 return systemName;
3708 return "";
3709 #endif
3712 char cmSystemTools::GetSystemPathlistSeparator()
3714 #if defined(_WIN32)
3715 return ';';
3716 #else
3717 return ':';
3718 #endif