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
9 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__QNX__)
11 // NOLINTNEXTLINE(bugprone-reserved-identifier)
12 # define _XOPEN_SOURCE 700
15 #include "cmSystemTools.h"
17 #include <cm/optional>
18 #include <cmext/algorithm>
22 #include "cmDuration.h"
24 #include "cmMessageMetadata.h"
25 #include "cmProcessOutput.h"
27 #include "cmStringAlgorithms.h"
30 #if !defined(CMAKE_BOOTSTRAP)
31 # include <cm3p/archive.h>
32 # include <cm3p/archive_entry.h>
34 # include "cmArchiveWrite.h"
35 # include "cmLocale.h"
37 # define __LA_INT64_T la_int64_t
40 # define __LA_SSIZE_T la_ssize_t
44 #if !defined(CMAKE_BOOTSTRAP)
48 # include "cmCryptoHash.h"
51 #if defined(CMake_USE_MACH_PARSER)
55 #if defined(CMake_USE_XCOFF_PARSER)
75 #include "cmsys/Directory.hxx"
76 #include "cmsys/Encoding.hxx"
77 #include "cmsys/FStream.hxx"
78 #include "cmsys/RegularExpression.hxx"
79 #include "cmsys/System.h"
80 #include "cmsys/Terminal.h"
84 // include wincrypt.h after windows.h
85 # include <wincrypt.h>
89 # include <sys/time.h>
90 # include <sys/types.h>
93 #if defined(_WIN32) && \
94 (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__MINGW32__))
98 #if defined(__APPLE__)
99 # include <mach-o/dyld.h>
103 # include <malloc.h> /* for malloc/free on QNX */
106 #if !defined(_WIN32) && !defined(__ANDROID__)
107 # include <sys/utsname.h>
110 #if defined(_MSC_VER) && _MSC_VER >= 1800
111 # define CM_WINDOWS_DEPRECATED_GetVersionEx
116 cmSystemTools::InterruptCallback s_InterruptCallback;
117 cmSystemTools::MessageCallback s_MessageCallback;
118 cmSystemTools::OutputCallback s_StderrCallback;
119 cmSystemTools::OutputCallback s_StdoutCallback;
123 #if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE)
124 // For GetEnvironmentVariables
126 extern __declspec(dllimport) char** environ;
128 extern char** environ;
132 #if !defined(CMAKE_BOOTSTRAP)
133 static std::string cm_archive_entry_pathname(struct archive_entry* entry)
135 # if cmsys_STL_HAS_WSTRING
136 return cmsys::Encoding::ToNarrow(archive_entry_pathname_w(entry));
138 return archive_entry_pathname(entry);
142 static int cm_archive_read_open_file(struct archive* a, const char* file,
145 # if cmsys_STL_HAS_WSTRING
146 std::wstring wfile = cmsys::Encoding::ToWide(file);
147 return archive_read_open_filename_w(a, wfile.c_str(), block_size);
149 return archive_read_open_filename(a, file, block_size);
155 #elif defined(__APPLE__)
156 # include <crt_externs.h>
158 # define environ (*_NSGetEnviron())
161 bool cmSystemTools::s_RunCommandHideConsole = false;
162 bool cmSystemTools::s_DisableRunCommandOutput = false;
163 bool cmSystemTools::s_ErrorOccurred = false;
164 bool cmSystemTools::s_FatalErrorOccurred = false;
165 bool cmSystemTools::s_ForceUnixPaths = false;
167 // replace replace with with as many times as it shows up in source.
168 // write the result into source.
169 #if defined(_WIN32) && !defined(__CYGWIN__)
170 void cmSystemTools::ExpandRegistryValues(std::string& source, KeyWOW64 view)
172 // Regular expression to match anything inside [...] that begins in HKEY.
173 // Note that there is a special rule for regular expressions to match a
174 // close square-bracket inside a list delimited by square brackets.
175 // The "[^]]" part of this expression will match any character except
176 // a close square-bracket. The ']' character must be the first in the
177 // list of characters inside the [^...] block of the expression.
178 cmsys::RegularExpression regEntry("\\[(HKEY[^]]*)\\]");
180 // check for black line or comment
181 while (regEntry.find(source)) {
182 // the arguments are the second match
183 std::string key = regEntry.match(1);
185 if (ReadRegistryValue(key.c_str(), val, view)) {
186 std::string reg = cmStrCat('[', key, ']');
187 cmSystemTools::ReplaceString(source, reg.c_str(), val.c_str());
189 std::string reg = cmStrCat('[', key, ']');
190 cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
195 void cmSystemTools::ExpandRegistryValues(std::string& source,
198 cmsys::RegularExpression regEntry("\\[(HKEY[^]]*)\\]");
199 while (regEntry.find(source)) {
200 // the arguments are the second match
201 std::string key = regEntry.match(1);
202 std::string reg = cmStrCat('[', key, ']');
203 cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
208 std::string cmSystemTools::HelpFileName(cm::string_view str)
210 std::string name(str);
211 cmSystemTools::ReplaceString(name, "<", "");
212 cmSystemTools::ReplaceString(name, ">", "");
216 void cmSystemTools::Error(const std::string& m)
218 std::string message = "CMake Error: " + m;
219 cmSystemTools::s_ErrorOccurred = true;
220 cmSystemTools::Message(message, "Error");
223 void cmSystemTools::SetInterruptCallback(InterruptCallback f)
225 s_InterruptCallback = std::move(f);
228 bool cmSystemTools::GetInterruptFlag()
230 if (s_InterruptCallback) {
231 return s_InterruptCallback();
236 void cmSystemTools::SetMessageCallback(MessageCallback f)
238 s_MessageCallback = std::move(f);
241 void cmSystemTools::SetStdoutCallback(OutputCallback f)
243 s_StdoutCallback = std::move(f);
246 void cmSystemTools::SetStderrCallback(OutputCallback f)
248 s_StderrCallback = std::move(f);
251 void cmSystemTools::Stderr(const std::string& s)
253 if (s_StderrCallback) {
256 std::cerr << s << std::flush;
260 void cmSystemTools::Stdout(const std::string& s)
262 if (s_StdoutCallback) {
265 std::cout << s << std::flush;
269 void cmSystemTools::Message(const std::string& m, const char* title)
271 cmMessageMetadata md;
276 void cmSystemTools::Message(const std::string& m, const cmMessageMetadata& md)
278 if (s_MessageCallback) {
279 s_MessageCallback(m, md);
281 std::cerr << m << std::endl;
285 void cmSystemTools::ReportLastSystemError(const char* msg)
288 cmStrCat(msg, ": System Error: ", Superclass::GetLastSystemError());
289 cmSystemTools::Error(m);
292 void cmSystemTools::ParseWindowsCommandLine(const char* command,
293 std::vector<std::string>& args)
295 // See the MSDN document "Parsing C Command-Line Arguments" at
296 // http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx for rules
297 // of parsing the windows command line.
299 bool in_argument = false;
300 bool in_quotes = false;
303 for (const char* c = command; *c; ++c) {
307 } else if (*c == '"') {
308 int backslash_pairs = backslashes >> 1;
309 int backslash_escaped = backslashes & 1;
310 arg.append(backslash_pairs, '\\');
312 if (backslash_escaped) {
313 /* An odd number of backslashes precede this quote.
317 /* An even number of backslashes precede this quote.
318 It is not escaped. */
319 in_quotes = !in_quotes;
323 arg.append(backslashes, '\\');
328 } else if (in_argument) {
339 arg.append(backslashes, '\\');
345 class cmSystemToolsArgV
350 cmSystemToolsArgV(char** argv)
356 for (char** arg = this->ArgV; arg && *arg; ++arg) {
361 cmSystemToolsArgV(const cmSystemToolsArgV&) = delete;
362 cmSystemToolsArgV& operator=(const cmSystemToolsArgV&) = delete;
363 void Store(std::vector<std::string>& args) const
365 for (char** arg = this->ArgV; arg && *arg; ++arg) {
366 args.emplace_back(*arg);
371 void cmSystemTools::ParseUnixCommandLine(const char* command,
372 std::vector<std::string>& args)
374 // Invoke the underlying parser.
375 cmSystemToolsArgV argv(cmsysSystem_Parse_CommandForUnix(command, 0));
379 std::vector<std::string> cmSystemTools::HandleResponseFile(
380 std::vector<std::string>::const_iterator argBeg,
381 std::vector<std::string>::const_iterator argEnd)
383 std::vector<std::string> arg_full;
384 for (std::string const& arg : cmMakeRange(argBeg, argEnd)) {
385 if (cmHasLiteralPrefix(arg, "@")) {
386 cmsys::ifstream responseFile(arg.substr(1).c_str(), std::ios::in);
388 std::string error = cmStrCat("failed to open for reading (",
389 cmSystemTools::GetLastSystemError(),
390 "):\n ", cm::string_view(arg).substr(1));
391 cmSystemTools::Error(error);
394 cmSystemTools::GetLineFromStream(responseFile, line);
395 std::vector<std::string> args2;
397 cmSystemTools::ParseWindowsCommandLine(line.c_str(), args2);
399 cmSystemTools::ParseUnixCommandLine(line.c_str(), args2);
401 cm::append(arg_full, args2);
404 arg_full.push_back(arg);
410 std::vector<std::string> cmSystemTools::ParseArguments(const std::string& cmd)
412 std::vector<std::string> args;
415 bool win_path = false;
417 const char* command = cmd.c_str();
418 if (command[0] && command[1] &&
419 ((command[0] != '/' && command[1] == ':' && command[2] == '\\') ||
420 (command[0] == '\"' && command[1] != '/' && command[2] == ':' &&
421 command[3] == '\\') ||
422 (command[0] == '\'' && command[1] != '/' && command[2] == ':' &&
423 command[3] == '\\') ||
424 (command[0] == '\\' && command[1] == '\\'))) {
427 // Split the command into an argv array.
428 for (const char* c = command; *c;) {
429 // Skip over whitespace.
430 while (*c == ' ' || *c == '\t') {
435 // Parse a quoted argument.
437 while (*c && *c != '"') {
445 } else if (*c == '\'') {
446 // Parse a quoted argument.
448 while (*c && *c != '\'') {
457 // Parse an unquoted argument.
458 while (*c && *c != ' ' && *c != '\t') {
459 if (*c == '\\' && !win_path) {
477 bool cmSystemTools::SplitProgramFromArgs(std::string const& command,
478 std::string& program,
481 const char* c = command.c_str();
483 // Skip leading whitespace.
484 while (isspace(static_cast<unsigned char>(*c))) {
488 // Parse one command-line element up to an unquoted space.
489 bool in_escape = false;
490 bool in_double = false;
491 bool in_single = false;
499 } else if (in_escape) {
502 } else if (*c == '\\') {
504 } else if (in_double) {
510 } else if (*c == '"') {
512 } else if (*c == '\'') {
514 } else if (isspace(static_cast<unsigned char>(*c))) {
521 // The remainder of the command line holds unparsed arguments.
524 return !in_single && !in_escape && !in_double;
527 size_t cmSystemTools::CalculateCommandLineLengthLimit()
531 // There's a maximum of 65536 bytes and thus 32768 WCHARs on Windows
532 // However, cmd.exe itself can only handle 8191 WCHARs and Ninja for
533 // example uses it to spawn processes.
535 #elif defined(__linux)
536 // MAX_ARG_STRLEN is the maximum length of a string permissible for
537 // the execve() syscall on Linux. It's defined as (PAGE_SIZE * 32)
538 // in Linux's binfmts.h
539 static_cast<size_t>(sysconf(_SC_PAGESIZE) * 32);
544 #if defined(_SC_ARG_MAX)
545 // ARG_MAX is the maximum size of the command and environment
546 // that can be passed to the exec functions on UNIX.
547 // The value in climits does not need to be present and may
548 // depend upon runtime memory constraints, hence sysconf()
549 // should be used to query it.
550 long szArgMax = sysconf(_SC_ARG_MAX);
551 // A return value of -1 signifies an undetermined limit, but
552 // it does not imply an infinite limit, and thus is ignored.
553 if (szArgMax != -1) {
554 // We estimate the size of the environment block to be 1000.
555 // This isn't accurate at all, but leaves some headroom.
556 szArgMax = szArgMax < 1000 ? 0 : szArgMax - 1000;
557 # if defined(_WIN32) || defined(__linux)
558 sz = std::min(sz, static_cast<size_t>(szArgMax));
560 sz = static_cast<size_t>(szArgMax);
567 bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
568 std::string* captureStdOut,
569 std::string* captureStdErr, int* retVal,
570 const char* dir, OutputOption outputflag,
571 cmDuration timeout, Encoding encoding)
573 std::vector<const char*> argv;
574 argv.reserve(command.size() + 1);
575 for (std::string const& cmd : command) {
576 argv.push_back(cmd.c_str());
578 argv.push_back(nullptr);
580 cmsysProcess* cp = cmsysProcess_New();
581 cmsysProcess_SetCommand(cp, argv.data());
582 cmsysProcess_SetWorkingDirectory(cp, dir);
583 if (cmSystemTools::GetRunCommandHideConsole()) {
584 cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
587 if (outputflag == OUTPUT_PASSTHROUGH) {
588 cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
589 cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
590 captureStdOut = nullptr;
591 captureStdErr = nullptr;
592 } else if (outputflag == OUTPUT_MERGE ||
593 (captureStdErr && captureStdErr == captureStdOut)) {
594 cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
595 captureStdErr = nullptr;
597 assert(!captureStdErr || captureStdErr != captureStdOut);
599 cmsysProcess_SetTimeout(cp, timeout.count());
600 cmsysProcess_Execute(cp);
602 std::vector<char> tempStdOut;
603 std::vector<char> tempStdErr;
607 cmProcessOutput processOutput(encoding);
609 if (outputflag != OUTPUT_PASSTHROUGH &&
610 (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) {
611 while ((pipe = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) >
613 // Translate NULL characters in the output into valid text.
614 for (int i = 0; i < length; ++i) {
615 if (data[i] == '\0') {
620 if (pipe == cmsysProcess_Pipe_STDOUT) {
621 if (outputflag != OUTPUT_NONE) {
622 processOutput.DecodeText(data, length, strdata, 1);
623 cmSystemTools::Stdout(strdata);
626 cm::append(tempStdOut, data, data + length);
628 } else if (pipe == cmsysProcess_Pipe_STDERR) {
629 if (outputflag != OUTPUT_NONE) {
630 processOutput.DecodeText(data, length, strdata, 2);
631 cmSystemTools::Stderr(strdata);
634 cm::append(tempStdErr, data, data + length);
639 if (outputflag != OUTPUT_NONE) {
640 processOutput.DecodeText(std::string(), strdata, 1);
641 if (!strdata.empty()) {
642 cmSystemTools::Stdout(strdata);
644 processOutput.DecodeText(std::string(), strdata, 2);
645 if (!strdata.empty()) {
646 cmSystemTools::Stderr(strdata);
651 cmsysProcess_WaitForExit(cp, nullptr);
654 captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
655 processOutput.DecodeText(*captureStdOut, *captureStdOut);
658 captureStdErr->assign(tempStdErr.begin(), tempStdErr.end());
659 processOutput.DecodeText(*captureStdErr, *captureStdErr);
663 if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
665 *retVal = cmsysProcess_GetExitValue(cp);
667 if (cmsysProcess_GetExitValue(cp) != 0) {
671 } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
672 const char* exception_str = cmsysProcess_GetExceptionString(cp);
673 if (outputflag != OUTPUT_NONE) {
674 std::cerr << exception_str << std::endl;
677 captureStdErr->append(exception_str, strlen(exception_str));
678 } else if (captureStdOut) {
679 captureStdOut->append(exception_str, strlen(exception_str));
682 } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
683 const char* error_str = cmsysProcess_GetErrorString(cp);
684 if (outputflag != OUTPUT_NONE) {
685 std::cerr << error_str << std::endl;
688 captureStdErr->append(error_str, strlen(error_str));
689 } else if (captureStdOut) {
690 captureStdOut->append(error_str, strlen(error_str));
693 } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
694 const char* error_str = "Process terminated due to timeout\n";
695 if (outputflag != OUTPUT_NONE) {
696 std::cerr << error_str << std::endl;
699 captureStdErr->append(error_str, strlen(error_str));
704 cmsysProcess_Delete(cp);
708 bool cmSystemTools::RunSingleCommand(const std::string& command,
709 std::string* captureStdOut,
710 std::string* captureStdErr, int* retVal,
711 const char* dir, OutputOption outputflag,
714 if (s_DisableRunCommandOutput) {
715 outputflag = OUTPUT_NONE;
718 std::vector<std::string> args = cmSystemTools::ParseArguments(command);
723 return cmSystemTools::RunSingleCommand(args, captureStdOut, captureStdErr,
724 retVal, dir, outputflag, timeout);
727 std::string cmSystemTools::PrintSingleCommand(
728 std::vector<std::string> const& command)
730 if (command.empty()) {
731 return std::string();
734 return cmWrap('"', command, '"', " ");
737 bool cmSystemTools::DoesFileExistWithExtensions(
738 const std::string& name, const std::vector<std::string>& headerExts)
742 for (std::string const& headerExt : headerExts) {
743 hname = cmStrCat(name, '.', headerExt);
744 if (cmSystemTools::FileExists(hname)) {
751 std::string cmSystemTools::FileExistsInParentDirectories(
752 const std::string& fname, const std::string& directory,
753 const std::string& toplevel)
755 std::string file = fname;
756 cmSystemTools::ConvertToUnixSlashes(file);
757 std::string dir = directory;
758 cmSystemTools::ConvertToUnixSlashes(dir);
760 while (dir != prevDir) {
761 std::string path = cmStrCat(dir, "/", file);
762 if (cmSystemTools::FileExists(path)) {
765 if (dir.size() < toplevel.size()) {
769 dir = cmSystemTools::GetParentDirectory(dir);
777 /* Helper class to save and restore the specified file (or directory)
778 attribute bits. Instantiate this class as an automatic variable on the
779 stack. Its constructor saves a copy of the file attributes, and then its
780 destructor restores the original attribute settings. */
781 class SaveRestoreFileAttributes
784 SaveRestoreFileAttributes(std::wstring const& path,
785 uint32_t file_attrs_to_set);
786 ~SaveRestoreFileAttributes();
788 SaveRestoreFileAttributes(SaveRestoreFileAttributes const&) = delete;
789 SaveRestoreFileAttributes& operator=(SaveRestoreFileAttributes const&) =
792 void SetPath(std::wstring const& path) { path_ = path; }
796 uint32_t original_attr_bits_;
799 SaveRestoreFileAttributes::SaveRestoreFileAttributes(
800 std::wstring const& path, uint32_t file_attrs_to_set)
802 , original_attr_bits_(0)
804 // Set the specified attributes for the source file/directory.
805 original_attr_bits_ = GetFileAttributesW(path_.c_str());
806 if ((INVALID_FILE_ATTRIBUTES != original_attr_bits_) &&
807 ((file_attrs_to_set & original_attr_bits_) != file_attrs_to_set)) {
808 SetFileAttributesW(path_.c_str(), original_attr_bits_ | file_attrs_to_set);
812 // We set attribute bits. Now we need to restore their original state.
813 SaveRestoreFileAttributes::~SaveRestoreFileAttributes()
815 DWORD last_error = GetLastError();
816 // Verify or restore the original attributes.
817 const DWORD source_attr_bits = GetFileAttributesW(path_.c_str());
818 if (INVALID_FILE_ATTRIBUTES != source_attr_bits) {
819 if (original_attr_bits_ != source_attr_bits) {
820 // The file still exists, and its attributes aren't our saved values.
821 // Time to restore them.
822 SetFileAttributesW(path_.c_str(), original_attr_bits_);
825 SetLastError(last_error);
828 struct WindowsFileRetryInit
830 cmSystemTools::WindowsFileRetry Retry;
834 WindowsFileRetryInit InitWindowsFileRetry(wchar_t const* const values[2],
835 unsigned int const defaults[2])
837 unsigned int data[2] = { 0, 0 };
838 HKEY const keys[2] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE };
839 for (int k = 0; k < 2; ++k) {
841 if (RegOpenKeyExW(keys[k], L"Software\\Kitware\\CMake\\Config", 0,
842 KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
843 for (int v = 0; v < 2; ++v) {
844 DWORD dwData, dwType, dwSize = 4;
846 RegQueryValueExW(hKey, values[v], 0, &dwType, (BYTE*)&dwData,
847 &dwSize) == ERROR_SUCCESS &&
848 dwType == REG_DWORD && dwSize == 4) {
849 data[v] = static_cast<unsigned int>(dwData);
855 WindowsFileRetryInit init;
856 init.Explicit = data[0] || data[1];
857 init.Retry.Count = data[0] ? data[0] : defaults[0];
858 init.Retry.Delay = data[1] ? data[1] : defaults[1];
862 WindowsFileRetryInit InitWindowsFileRetry()
864 static wchar_t const* const values[2] = { L"FilesystemRetryCount",
865 L"FilesystemRetryDelay" };
866 static unsigned int const defaults[2] = { 5, 500 };
867 return InitWindowsFileRetry(values, defaults);
870 WindowsFileRetryInit InitWindowsDirectoryRetry()
872 static wchar_t const* const values[2] = { L"FilesystemDirectoryRetryCount",
873 L"FilesystemDirectoryRetryDelay" };
874 static unsigned int const defaults[2] = { 120, 500 };
875 WindowsFileRetryInit dirInit = InitWindowsFileRetry(values, defaults);
876 if (dirInit.Explicit) {
879 WindowsFileRetryInit fileInit = InitWindowsFileRetry();
880 if (fileInit.Explicit) {
886 cmSystemTools::WindowsFileRetry GetWindowsRetry(std::wstring const& path)
888 // If we are performing a directory operation, then try and get the
889 // appropriate timing info.
890 DWORD const attrs = GetFileAttributesW(path.c_str());
891 if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
892 return cmSystemTools::GetWindowsDirectoryRetry();
894 return cmSystemTools::GetWindowsFileRetry();
897 } // end of anonymous namespace
899 cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsFileRetry()
901 static WindowsFileRetry retry = InitWindowsFileRetry().Retry;
905 cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsDirectoryRetry()
907 static cmSystemTools::WindowsFileRetry retry =
908 InitWindowsDirectoryRetry().Retry;
912 cmSystemTools::WindowsVersion cmSystemTools::GetWindowsVersion()
914 /* Windows version number data. */
915 OSVERSIONINFOEXW osviex;
916 ZeroMemory(&osviex, sizeof(osviex));
917 osviex.dwOSVersionInfoSize = sizeof(osviex);
919 # ifdef CM_WINDOWS_DEPRECATED_GetVersionEx
920 # pragma warning(push)
921 # ifdef __INTEL_COMPILER
922 # pragma warning(disable : 1478)
923 # elif defined __clang__
924 # pragma clang diagnostic push
925 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
927 # pragma warning(disable : 4996)
930 GetVersionExW((OSVERSIONINFOW*)&osviex);
931 # ifdef CM_WINDOWS_DEPRECATED_GetVersionEx
933 # pragma clang diagnostic pop
935 # pragma warning(pop)
939 WindowsVersion result;
940 result.dwMajorVersion = osviex.dwMajorVersion;
941 result.dwMinorVersion = osviex.dwMinorVersion;
942 result.dwBuildNumber = osviex.dwBuildNumber;
947 std::string cmSystemTools::GetRealPathResolvingWindowsSubst(
948 const std::string& path, std::string* errorMessage)
951 // uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found
952 std::string resolved_path;
954 int err = uv_fs_realpath(NULL, &req, path.c_str(), NULL);
956 resolved_path = std::string((char*)req.ptr);
957 cmSystemTools::ConvertToUnixSlashes(resolved_path);
958 // Normalize to upper-case drive letter as GetActualCaseForPath does.
959 if (resolved_path.size() > 1 && resolved_path[1] == ':') {
960 resolved_path[0] = toupper(resolved_path[0]);
962 } else if (err == UV_ENOSYS) {
963 resolved_path = cmsys::SystemTools::GetRealPath(path, errorMessage);
964 } else if (errorMessage) {
965 LPSTR message = NULL;
966 DWORD size = FormatMessageA(
967 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
968 FORMAT_MESSAGE_IGNORE_INSERTS,
969 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&message, 0,
971 *errorMessage = std::string(message, size);
976 resolved_path = path;
978 return resolved_path;
980 return cmsys::SystemTools::GetRealPath(path, errorMessage);
984 void cmSystemTools::InitializeLibUV()
987 // Perform libuv one-time initialization now, and then un-do its
988 // global _fmode setting so that using libuv does not change the
989 // default file text/binary mode. See libuv issue 840.
990 if (uv_loop_t* loop = uv_default_loop()) {
998 // Replace libuv's report handler with our own to suppress popups.
999 cmSystemTools::EnableMSVCDebugHook();
1005 bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname,
1006 cmSystemTools::Replace replace)
1008 // Not only ignore any previous error, but clear any memory of it.
1012 if (replace == cmSystemTools::Replace::Yes) {
1013 // Use MOVEFILE_REPLACE_EXISTING to replace an existing destination file.
1014 flags = flags | MOVEFILE_REPLACE_EXISTING;
1017 return MoveFileExW(oldname.c_str(), newname.c_str(), flags);
1022 bool cmSystemTools::CopySingleFile(const std::string& oldname,
1023 const std::string& newname)
1025 return cmSystemTools::CopySingleFile(oldname, newname, CopyWhen::Always) ==
1026 CopyResult::Success;
1029 cmSystemTools::CopyResult cmSystemTools::CopySingleFile(
1030 std::string const& oldname, std::string const& newname, CopyWhen when,
1034 case CopyWhen::Always:
1036 case CopyWhen::OnlyIfDifferent:
1037 if (!FilesDiffer(oldname, newname)) {
1038 return CopyResult::Success;
1044 cmsys::Status perms = SystemTools::GetPermissions(oldname, perm);
1046 // If files are the same do not copy
1047 if (SystemTools::SameFile(oldname, newname)) {
1048 return CopyResult::Success;
1051 cmsys::Status status;
1052 status = cmsys::SystemTools::CloneFileContent(oldname, newname);
1054 // if cloning did not succeed, fall back to blockwise copy
1055 status = cmsys::SystemTools::CopyFileContentBlockwise(oldname, newname);
1059 *err = status.GetString();
1061 return CopyResult::Failure;
1064 status = SystemTools::SetPermissions(newname, perm);
1067 *err = status.GetString();
1069 return CopyResult::Failure;
1072 return CopyResult::Success;
1075 bool cmSystemTools::RenameFile(const std::string& oldname,
1076 const std::string& newname)
1078 return cmSystemTools::RenameFile(oldname, newname, Replace::Yes) ==
1079 RenameResult::Success;
1082 cmSystemTools::RenameResult cmSystemTools::RenameFile(
1083 std::string const& oldname, std::string const& newname, Replace replace,
1087 # ifndef INVALID_FILE_ATTRIBUTES
1088 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
1090 std::wstring const oldname_wstr =
1091 SystemTools::ConvertToWindowsExtendedPath(oldname);
1092 std::wstring const newname_wstr =
1093 SystemTools::ConvertToWindowsExtendedPath(newname);
1095 /* Windows MoveFileEx may not replace read-only or in-use files. If it
1096 fails then remove the read-only attribute from any existing destination.
1097 Try multiple times since we may be racing against another process
1098 creating/opening the destination file just before our MoveFileEx. */
1099 WindowsFileRetry retry = GetWindowsRetry(oldname_wstr);
1101 // Use RAII to set the attribute bit blocking Microsoft Search Indexing,
1102 // and restore the previous value upon return.
1103 SaveRestoreFileAttributes save_restore_file_attributes(
1104 oldname_wstr, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
1106 DWORD move_last_error = 0;
1107 while (!cmMoveFile(oldname_wstr, newname_wstr, replace) && --retry.Count) {
1108 move_last_error = GetLastError();
1110 // There was no error ==> the operation is not yet complete.
1111 if (move_last_error == NO_ERROR) {
1115 // Try again only if failure was due to access/sharing permissions.
1116 // Most often ERROR_ACCESS_DENIED (a.k.a. I/O error) for a directory, and
1117 // ERROR_SHARING_VIOLATION for a file, are caused by one of the following:
1118 // 1) Anti-Virus Software
1119 // 2) Windows Search Indexer
1120 // 3) Windows Explorer has an associated directory already opened.
1121 if (move_last_error != ERROR_ACCESS_DENIED &&
1122 move_last_error != ERROR_SHARING_VIOLATION) {
1123 if (replace == Replace::No && move_last_error == ERROR_ALREADY_EXISTS) {
1124 return RenameResult::NoReplace;
1127 *err = cmsys::Status::Windows(move_last_error).GetString();
1129 return RenameResult::Failure;
1132 DWORD const attrs = GetFileAttributesW(newname_wstr.c_str());
1133 if ((attrs != INVALID_FILE_ATTRIBUTES) &&
1134 (attrs & FILE_ATTRIBUTE_READONLY) &&
1135 // FILE_ATTRIBUTE_READONLY is not honored on directories.
1136 !(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
1137 // Remove the read-only attribute from the destination file.
1138 SetFileAttributesW(newname_wstr.c_str(),
1139 attrs & ~FILE_ATTRIBUTE_READONLY);
1141 // The file may be temporarily in use so wait a bit.
1142 cmSystemTools::Delay(retry.Delay);
1146 // If we were successful, then there was no error.
1147 if (retry.Count > 0) {
1148 move_last_error = 0;
1149 // Restore the attributes on the new name.
1150 save_restore_file_attributes.SetPath(newname_wstr);
1152 SetLastError(move_last_error);
1153 if (retry.Count > 0) {
1154 return RenameResult::Success;
1156 if (replace == Replace::No && GetLastError() == ERROR_ALREADY_EXISTS) {
1157 return RenameResult::NoReplace;
1160 *err = cmsys::Status::Windows_GetLastError().GetString();
1162 return RenameResult::Failure;
1164 // On UNIX we have OS-provided calls to create 'newname' atomically.
1165 if (replace == Replace::No) {
1166 if (link(oldname.c_str(), newname.c_str()) == 0) {
1167 return RenameResult::Success;
1169 if (errno == EEXIST) {
1170 return RenameResult::NoReplace;
1173 *err = cmsys::Status::POSIX_errno().GetString();
1175 return RenameResult::Failure;
1177 if (rename(oldname.c_str(), newname.c_str()) == 0) {
1178 return RenameResult::Success;
1181 *err = cmsys::Status::POSIX_errno().GetString();
1183 return RenameResult::Failure;
1187 void cmSystemTools::MoveFileIfDifferent(const std::string& source,
1188 const std::string& destination)
1190 if (FilesDiffer(source, destination)) {
1191 if (RenameFile(source, destination)) {
1194 CopyFileAlways(source, destination);
1199 #ifndef CMAKE_BOOTSTRAP
1200 std::string cmSystemTools::ComputeFileHash(const std::string& source,
1201 cmCryptoHash::Algo algo)
1203 cmCryptoHash hash(algo);
1204 return hash.HashFile(source);
1207 std::string cmSystemTools::ComputeStringMD5(const std::string& input)
1209 cmCryptoHash md5(cmCryptoHash::AlgoMD5);
1210 return md5.HashString(input);
1214 std::string cmSystemTools::ComputeCertificateThumbprint(
1215 const std::string& source)
1217 std::string thumbprint;
1219 CRYPT_INTEGER_BLOB cryptBlob;
1220 HCERTSTORE certStore = NULL;
1221 PCCERT_CONTEXT certContext = NULL;
1223 HANDLE certFile = CreateFileW(
1224 cmsys::Encoding::ToWide(source.c_str()).c_str(), GENERIC_READ,
1225 FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1227 if (certFile != INVALID_HANDLE_VALUE && certFile != NULL) {
1228 DWORD fileSize = GetFileSize(certFile, NULL);
1229 if (fileSize != INVALID_FILE_SIZE) {
1230 auto certData = cm::make_unique<BYTE[]>(fileSize);
1231 if (certData != NULL) {
1233 if (ReadFile(certFile, certData.get(), fileSize, &dwRead, NULL)) {
1234 cryptBlob.cbData = fileSize;
1235 cryptBlob.pbData = certData.get();
1237 // Verify that this is a valid cert
1238 if (PFXIsPFXBlob(&cryptBlob)) {
1239 // Open the certificate as a store
1240 certStore = PFXImportCertStore(&cryptBlob, NULL, CRYPT_EXPORTABLE);
1241 if (certStore != NULL) {
1242 // There should only be 1 cert.
1244 CertEnumCertificatesInStore(certStore, certContext);
1245 if (certContext != NULL) {
1246 // The hash is 20 bytes
1248 DWORD hashLength = 20;
1250 // Buffer to print the hash. Each byte takes 2 chars +
1251 // terminating character
1253 char* pHashPrint = hashPrint;
1254 // Get the hash property from the certificate
1255 if (CertGetCertificateContextProperty(
1256 certContext, CERT_HASH_PROP_ID, hashData, &hashLength)) {
1257 for (DWORD i = 0; i < hashLength; i++) {
1258 // Convert each byte to hexadecimal
1259 sprintf(pHashPrint, "%02X", hashData[i]);
1263 thumbprint = hashPrint;
1265 CertFreeCertificateContext(certContext);
1267 CertCloseStore(certStore, 0);
1273 CloseHandle(certFile);
1281 void cmSystemTools::Glob(const std::string& directory,
1282 const std::string& regexp,
1283 std::vector<std::string>& files)
1286 cmsys::RegularExpression reg(regexp.c_str());
1288 if (d.Load(directory)) {
1291 numf = d.GetNumberOfFiles();
1292 for (i = 0; i < numf; i++) {
1293 std::string fname = d.GetFile(i);
1294 if (reg.find(fname)) {
1295 files.push_back(std::move(fname));
1301 void cmSystemTools::GlobDirs(const std::string& path,
1302 std::vector<std::string>& files)
1304 std::string::size_type pos = path.find("/*");
1305 if (pos == std::string::npos) {
1306 files.push_back(path);
1309 std::string startPath = path.substr(0, pos);
1310 std::string finishPath = path.substr(pos + 2);
1313 if (d.Load(startPath)) {
1314 for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) {
1315 if ((std::string(d.GetFile(i)) != ".") &&
1316 (std::string(d.GetFile(i)) != "..")) {
1317 std::string fname = cmStrCat(startPath, '/', d.GetFile(i));
1318 if (cmSystemTools::FileIsDirectory(fname)) {
1319 fname += finishPath;
1320 cmSystemTools::GlobDirs(fname, files);
1327 bool cmSystemTools::SimpleGlob(const std::string& glob,
1328 std::vector<std::string>& files,
1332 if (glob.back() != '*') {
1335 std::string path = cmSystemTools::GetFilenamePath(glob);
1336 std::string ppath = cmSystemTools::GetFilenameName(glob);
1337 ppath = ppath.substr(0, ppath.size() - 1);
1345 for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) {
1346 if ((std::string(d.GetFile(i)) != ".") &&
1347 (std::string(d.GetFile(i)) != "..")) {
1348 std::string fname = path;
1349 if (path.back() != '/') {
1352 fname += d.GetFile(i);
1353 std::string sfname = d.GetFile(i);
1354 if (type > 0 && cmSystemTools::FileIsDirectory(fname)) {
1357 if (type < 0 && !cmSystemTools::FileIsDirectory(fname)) {
1360 if (cmHasPrefix(sfname, ppath)) {
1361 files.push_back(fname);
1370 std::string cmSystemTools::ConvertToOutputPath(std::string const& path)
1372 #if defined(_WIN32) && !defined(__CYGWIN__)
1373 if (s_ForceUnixPaths) {
1374 return cmSystemTools::ConvertToUnixOutputPath(path);
1376 return cmSystemTools::ConvertToWindowsOutputPath(path);
1378 return cmSystemTools::ConvertToUnixOutputPath(path);
1382 void cmSystemTools::ConvertToOutputSlashes(std::string& path)
1384 #if defined(_WIN32) && !defined(__CYGWIN__)
1385 if (!s_ForceUnixPaths) {
1386 // Convert to windows slashes.
1387 std::string::size_type pos = 0;
1388 while ((pos = path.find('/', pos)) != std::string::npos) {
1393 static_cast<void>(path);
1397 void cmSystemTools::ConvertToLongPath(std::string& path)
1399 #if defined(_WIN32) && !defined(__CYGWIN__)
1400 // Try to convert path to a long path only if the path contains character '~'
1401 if (path.find('~') == std::string::npos) {
1405 std::wstring wPath = cmsys::Encoding::ToWide(path);
1406 DWORD ret = GetLongPathNameW(wPath.c_str(), nullptr, 0);
1407 std::vector<wchar_t> buffer(ret);
1409 ret = GetLongPathNameW(wPath.c_str(), buffer.data(),
1410 static_cast<DWORD>(buffer.size()));
1414 path = cmsys::Encoding::ToNarrow(buffer.data());
1417 static_cast<void>(path);
1421 std::string cmSystemTools::ConvertToRunCommandPath(const std::string& path)
1423 #if defined(_WIN32) && !defined(__CYGWIN__)
1424 return cmSystemTools::ConvertToWindowsOutputPath(path);
1426 return cmSystemTools::ConvertToUnixOutputPath(path);
1430 // compute the relative path from here to there
1431 std::string cmSystemTools::RelativePath(std::string const& local,
1432 std::string const& remote)
1434 if (!cmSystemTools::FileIsFullPath(local)) {
1435 cmSystemTools::Error("RelativePath must be passed a full path to local: " +
1438 if (!cmSystemTools::FileIsFullPath(remote)) {
1439 cmSystemTools::Error(
1440 "RelativePath must be passed a full path to remote: " + remote);
1442 return cmsys::SystemTools::RelativePath(local, remote);
1445 std::string cmSystemTools::ForceToRelativePath(std::string const& local_path,
1446 std::string const& remote_path)
1448 // The paths should never be quoted.
1449 assert(local_path.front() != '\"');
1450 assert(remote_path.front() != '\"');
1452 // The local path should never have a trailing slash except if it is just the
1453 // bare root directory
1454 assert(local_path.empty() || local_path.back() != '/' ||
1455 local_path.size() == 1 ||
1456 (local_path.size() == 3 && local_path[1] == ':' &&
1457 ((local_path[0] >= 'A' && local_path[0] <= 'Z') ||
1458 (local_path[0] >= 'a' && local_path[0] <= 'z'))));
1460 // If the path is already relative then just return the path.
1461 if (!cmSystemTools::FileIsFullPath(remote_path)) {
1465 // Identify the longest shared path component between the remote
1466 // path and the local path.
1467 std::vector<std::string> local;
1468 cmSystemTools::SplitPath(local_path, local);
1469 std::vector<std::string> remote;
1470 cmSystemTools::SplitPath(remote_path, remote);
1471 unsigned int common = 0;
1472 while (common < remote.size() && common < local.size() &&
1473 cmSystemTools::ComparePath(remote[common], local[common])) {
1477 // If no part of the path is in common then return the full path.
1482 // If the entire path is in common then just return a ".".
1483 if (common == remote.size() && common == local.size()) {
1487 // If the entire path is in common except for a trailing slash then
1488 // just return a "./".
1489 if (common + 1 == remote.size() && remote[common].empty() &&
1490 common == local.size()) {
1494 // Construct the relative path.
1495 std::string relative;
1497 // First add enough ../ to get up to the level of the shared portion
1498 // of the path. Leave off the trailing slash. Note that the last
1499 // component of local will never be empty because local should never
1500 // have a trailing slash.
1501 for (unsigned int i = common; i < local.size(); ++i) {
1503 if (i < local.size() - 1) {
1508 // Now add the portion of the destination path that is not included
1509 // in the shared portion of the path. Add a slash the first time
1510 // only if there was already something in the path. If there was a
1511 // trailing slash in the input then the last iteration of the loop
1512 // will add a slash followed by an empty string which will preserve
1513 // the trailing slash in the output.
1515 if (!relative.empty() && !remote.empty()) {
1518 relative += cmJoin(cmMakeRange(remote).advance(common), "/");
1520 // Finally return the path.
1524 std::string cmSystemTools::RelativeIfUnder(std::string const& top,
1525 std::string const& in)
1530 } else if (cmSystemTools::IsSubDirectory(in, top)) {
1531 out = in.substr(top.size() + 1);
1538 #ifndef CMAKE_BOOTSTRAP
1539 bool cmSystemTools::UnsetEnv(const char* value)
1541 # if !defined(HAVE_UNSETENV)
1542 std::string var = cmStrCat(value, '=');
1543 return cmSystemTools::PutEnv(var);
1550 std::vector<std::string> cmSystemTools::GetEnvironmentVariables()
1552 std::vector<std::string> env;
1554 for (cc = 0; environ[cc]; ++cc) {
1555 env.emplace_back(environ[cc]);
1560 void cmSystemTools::AppendEnv(std::vector<std::string> const& env)
1562 for (std::string const& eit : env) {
1563 cmSystemTools::PutEnv(eit);
1567 cmSystemTools::SaveRestoreEnvironment::SaveRestoreEnvironment()
1569 this->Env = cmSystemTools::GetEnvironmentVariables();
1572 cmSystemTools::SaveRestoreEnvironment::~SaveRestoreEnvironment()
1574 // First clear everything in the current environment:
1575 std::vector<std::string> currentEnv = GetEnvironmentVariables();
1576 for (std::string var : currentEnv) {
1577 std::string::size_type pos = var.find('=');
1578 if (pos != std::string::npos) {
1579 var = var.substr(0, pos);
1582 cmSystemTools::UnsetEnv(var.c_str());
1585 // Then put back each entry from the original environment:
1586 cmSystemTools::AppendEnv(this->Env);
1590 void cmSystemTools::EnableVSConsoleOutput()
1593 // Visual Studio tools like devenv may not
1594 // display output to the console unless this environment variable is
1595 // set. We need it to capture the output of these build tools.
1596 // Note for future work that one could pass "/out \\.\pipe\NAME" to
1597 // either of these executables where NAME is created with
1598 // CreateNamedPipe. This would bypass the internal buffering of the
1599 // output and allow it to be captured on the fly.
1600 cmSystemTools::PutEnv("vsconsoleoutput=1");
1602 # ifndef CMAKE_BOOTSTRAP
1603 // VS sets an environment variable to tell MS tools like "cl" to report
1604 // output through a backdoor pipe instead of stdout/stderr. Unset the
1605 // environment variable to close this backdoor for any path of process
1606 // invocations that passes through CMake so we can capture the output.
1607 cmSystemTools::UnsetEnv("VS_UNICODE_OUTPUT");
1612 bool cmSystemTools::IsPathToFramework(const std::string& path)
1614 return (cmSystemTools::FileIsFullPath(path) &&
1615 cmHasLiteralSuffix(path, ".framework"));
1618 bool cmSystemTools::IsPathToMacOSSharedLibrary(const std::string& path)
1620 return (cmSystemTools::FileIsFullPath(path) &&
1621 cmHasLiteralSuffix(path, ".dylib"));
1624 bool cmSystemTools::CreateTar(const std::string& outFileName,
1625 const std::vector<std::string>& files,
1626 cmTarCompression compressType, bool verbose,
1627 std::string const& mtime,
1628 std::string const& format, int compressionLevel)
1630 #if !defined(CMAKE_BOOTSTRAP)
1631 std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
1632 cmsys::ofstream fout(outFileName.c_str(), std::ios::out | std::ios::binary);
1634 std::string e = cmStrCat("Cannot open output file \"", outFileName,
1635 "\": ", cmSystemTools::GetLastSystemError());
1636 cmSystemTools::Error(e);
1639 cmArchiveWrite::Compress compress = cmArchiveWrite::CompressNone;
1640 switch (compressType) {
1641 case TarCompressGZip:
1642 compress = cmArchiveWrite::CompressGZip;
1644 case TarCompressBZip2:
1645 compress = cmArchiveWrite::CompressBZip2;
1648 compress = cmArchiveWrite::CompressXZ;
1650 case TarCompressZstd:
1651 compress = cmArchiveWrite::CompressZstd;
1653 case TarCompressNone:
1654 compress = cmArchiveWrite::CompressNone;
1658 cmArchiveWrite a(fout, compress, format.empty() ? "paxr" : format,
1662 cmSystemTools::Error(a.GetError());
1666 a.SetVerbose(verbose);
1667 bool tarCreatedSuccessfully = true;
1668 for (auto path : files) {
1669 if (cmSystemTools::FileIsFullPath(path)) {
1670 // Get the relative path to the file.
1671 path = cmSystemTools::RelativePath(cwd, path);
1674 cmSystemTools::Error(a.GetError());
1675 tarCreatedSuccessfully = false;
1678 return tarCreatedSuccessfully;
1687 #if !defined(CMAKE_BOOTSTRAP)
1689 # define BSDTAR_FILESIZE_PRINTF "%lu"
1690 # define BSDTAR_FILESIZE_TYPE unsigned long
1691 void list_item_verbose(FILE* out, struct archive_entry* entry)
1700 size_t gs_width = 13;
1703 * We avoid collecting the entire list in memory at once by
1704 * listing things as we see them. However, that also means we can't
1705 * just pre-compute the field widths. Instead, we start with guesses
1706 * and just widen them as necessary. These numbers are completely
1712 fprintf(out, "%s %d ", archive_entry_strmode(entry),
1713 archive_entry_nlink(entry));
1715 /* Use uname if it's present, else uid. */
1716 p = archive_entry_uname(entry);
1717 if ((p == nullptr) || (*p == '\0')) {
1718 snprintf(tmp, sizeof(tmp), "%lu ",
1719 static_cast<unsigned long>(archive_entry_uid(entry)));
1726 fprintf(out, "%-*s ", static_cast<int>(u_width), p);
1727 /* Use gname if it's present, else gid. */
1728 p = archive_entry_gname(entry);
1729 if (p != nullptr && p[0] != '\0') {
1730 fprintf(out, "%s", p);
1733 snprintf(tmp, sizeof(tmp), "%lu",
1734 static_cast<unsigned long>(archive_entry_gid(entry)));
1736 fprintf(out, "%s", tmp);
1740 * Print device number or file size, right-aligned so as to make
1741 * total width of group and devnum/filesize fields be gs_width.
1742 * If gs_width is too small, grow it.
1744 if (archive_entry_filetype(entry) == AE_IFCHR ||
1745 archive_entry_filetype(entry) == AE_IFBLK) {
1746 unsigned long rdevmajor = archive_entry_rdevmajor(entry);
1747 unsigned long rdevminor = archive_entry_rdevminor(entry);
1748 snprintf(tmp, sizeof(tmp), "%lu,%lu", rdevmajor, rdevminor);
1751 * Note the use of platform-dependent macros to format
1752 * the filesize here. We need the format string and the
1753 * corresponding type for the cast.
1755 snprintf(tmp, sizeof(tmp), BSDTAR_FILESIZE_PRINTF,
1756 static_cast<BSDTAR_FILESIZE_TYPE>(archive_entry_size(entry)));
1758 if (w + strlen(tmp) >= gs_width) {
1759 gs_width = w + strlen(tmp) + 1;
1761 fprintf(out, "%*s", static_cast<int>(gs_width - w), tmp);
1763 /* Format the time using 'ls -l' conventions. */
1764 tim = archive_entry_mtime(entry);
1765 # define HALF_YEAR ((time_t)365 * 86400 / 2)
1766 # if defined(_WIN32) && !defined(__CYGWIN__)
1767 /* Windows' strftime function does not support %e format. */
1768 # define DAY_FMT "%d"
1770 # define DAY_FMT "%e" /* Day number without leading zeros */
1772 if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) {
1773 fmt = DAY_FMT " %b %Y";
1775 fmt = DAY_FMT " %b %H:%M";
1777 strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
1778 fprintf(out, " %s ", tmp);
1779 fprintf(out, "%s", cm_archive_entry_pathname(entry).c_str());
1781 /* Extra information for links. */
1782 if (archive_entry_hardlink(entry)) /* Hard link */
1784 fprintf(out, " link to %s", archive_entry_hardlink(entry));
1785 } else if (archive_entry_symlink(entry)) /* Symbolic link */
1787 fprintf(out, " -> %s", archive_entry_symlink(entry));
1792 void ArchiveError(const char* m1, struct archive* a)
1794 std::string message(m1);
1795 const char* m2 = archive_error_string(a);
1799 cmSystemTools::Error(message);
1802 bool la_diagnostic(struct archive* ar, __LA_SSIZE_T r)
1804 // See archive.h definition of ARCHIVE_OK for return values.
1806 if (r >= ARCHIVE_OK) {
1810 if (r >= ARCHIVE_WARN) {
1811 const char* warn = archive_error_string(ar);
1813 warn = "unknown warning";
1815 std::cerr << "cmake -E tar: warning: " << warn << '\n';
1820 const char* err = archive_error_string(ar);
1822 err = "unknown error";
1824 std::cerr << "cmake -E tar: error: " << err << '\n';
1828 // Return 'true' on success
1829 bool copy_data(struct archive* ar, struct archive* aw)
1834 # if defined(ARCHIVE_VERSION_NUMBER) && ARCHIVE_VERSION_NUMBER >= 3000000
1835 __LA_INT64_T offset;
1841 // See archive.h definition of ARCHIVE_OK for return values.
1842 r = archive_read_data_block(ar, &buff, &size, &offset);
1843 if (r == ARCHIVE_EOF) {
1846 if (!la_diagnostic(ar, r)) {
1849 // See archive.h definition of ARCHIVE_OK for return values.
1850 __LA_SSIZE_T const w = archive_write_data_block(aw, buff, size, offset);
1851 if (!la_diagnostic(ar, w)) {
1855 # if !defined(__clang__) && !defined(__NVCOMPILER) && !defined(__HP_aCC)
1856 return false; /* this should not happen but it quiets some compilers */
1860 bool extract_tar(const std::string& outFileName,
1861 const std::vector<std::string>& files, bool verbose,
1862 cmSystemTools::cmTarExtractTimestamps extractTimestamps,
1865 cmLocaleRAII localeRAII;
1866 static_cast<void>(localeRAII);
1867 struct archive* a = archive_read_new();
1868 struct archive* ext = archive_write_disk_new();
1869 archive_read_support_filter_all(a);
1870 archive_read_support_format_all(a);
1871 struct archive_entry* entry;
1873 struct archive* matching = archive_match_new();
1874 if (matching == nullptr) {
1875 cmSystemTools::Error("Out of memory");
1879 for (const auto& filename : files) {
1880 if (archive_match_include_pattern(matching, filename.c_str()) !=
1882 cmSystemTools::Error("Failed to add to inclusion list: " + filename);
1887 int r = cm_archive_read_open_file(a, outFileName.c_str(), 10240);
1889 ArchiveError("Problem with archive_read_open_file(): ", a);
1890 archive_write_free(ext);
1891 archive_read_close(a);
1895 r = archive_read_next_header(a, &entry);
1896 if (r == ARCHIVE_EOF) {
1899 if (r != ARCHIVE_OK) {
1900 ArchiveError("Problem with archive_read_next_header(): ", a);
1904 if (archive_match_excluded(matching, entry)) {
1910 cmSystemTools::Stdout("x ");
1911 cmSystemTools::Stdout(cm_archive_entry_pathname(entry));
1913 list_item_verbose(stdout, entry);
1915 cmSystemTools::Stdout("\n");
1916 } else if (!extract) {
1917 cmSystemTools::Stdout(cm_archive_entry_pathname(entry));
1918 cmSystemTools::Stdout("\n");
1921 if (extractTimestamps == cmSystemTools::cmTarExtractTimestamps::Yes) {
1922 r = archive_write_disk_set_options(ext, ARCHIVE_EXTRACT_TIME);
1923 if (r != ARCHIVE_OK) {
1924 ArchiveError("Problem with archive_write_disk_set_options(): ", ext);
1929 r = archive_write_header(ext, entry);
1930 if (r == ARCHIVE_OK) {
1931 if (!copy_data(a, ext)) {
1934 r = archive_write_finish_entry(ext);
1935 if (r != ARCHIVE_OK) {
1936 ArchiveError("Problem with archive_write_finish_entry(): ", ext);
1941 else if (const char* linktext = archive_entry_symlink(entry)) {
1942 std::cerr << "cmake -E tar: warning: skipping symbolic link \""
1943 << cm_archive_entry_pathname(entry) << "\" -> \"" << linktext
1944 << "\"." << std::endl;
1948 ArchiveError("Problem with archive_write_header(): ", ext);
1949 cmSystemTools::Error("Current file: " +
1950 cm_archive_entry_pathname(entry));
1956 bool error_occured = false;
1957 if (matching != nullptr) {
1961 while ((ar = archive_match_path_unmatched_inclusions_next(matching, &p)) ==
1963 cmSystemTools::Error("tar: " + std::string(p) +
1964 ": Not found in archive");
1965 error_occured = true;
1967 if (error_occured) {
1970 if (ar == ARCHIVE_FATAL) {
1971 cmSystemTools::Error("tar: Out of memory");
1975 archive_match_free(matching);
1976 archive_write_free(ext);
1977 archive_read_close(a);
1978 archive_read_free(a);
1979 return r == ARCHIVE_EOF || r == ARCHIVE_OK;
1984 bool cmSystemTools::ExtractTar(const std::string& outFileName,
1985 const std::vector<std::string>& files,
1986 cmTarExtractTimestamps extractTimestamps,
1989 #if !defined(CMAKE_BOOTSTRAP)
1990 return extract_tar(outFileName, files, verbose, extractTimestamps, true);
1994 (void)extractTimestamps;
2000 bool cmSystemTools::ListTar(const std::string& outFileName,
2001 const std::vector<std::string>& files,
2004 #if !defined(CMAKE_BOOTSTRAP)
2005 return extract_tar(outFileName, files, verbose, cmTarExtractTimestamps::Yes,
2015 int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
2016 cmDuration timeout, std::vector<char>& out,
2017 std::vector<char>& err)
2020 auto outiter = out.begin();
2021 auto erriter = err.begin();
2022 cmProcessOutput processOutput;
2023 std::string strdata;
2025 // Check for a newline in stdout.
2026 for (; outiter != out.end(); ++outiter) {
2027 if ((*outiter == '\r') && ((outiter + 1) == out.end())) {
2030 if (*outiter == '\n' || *outiter == '\0') {
2031 std::vector<char>::size_type length = outiter - out.begin();
2032 if (length > 1 && *(outiter - 1) == '\r') {
2036 line.append(out.data(), length);
2038 out.erase(out.begin(), outiter + 1);
2039 return cmsysProcess_Pipe_STDOUT;
2043 // Check for a newline in stderr.
2044 for (; erriter != err.end(); ++erriter) {
2045 if ((*erriter == '\r') && ((erriter + 1) == err.end())) {
2048 if (*erriter == '\n' || *erriter == '\0') {
2049 std::vector<char>::size_type length = erriter - err.begin();
2050 if (length > 1 && *(erriter - 1) == '\r') {
2054 line.append(err.data(), length);
2056 err.erase(err.begin(), erriter + 1);
2057 return cmsysProcess_Pipe_STDERR;
2061 // No newlines found. Wait for more data from the process.
2064 double timeoutAsDbl = timeout.count();
2066 cmsysProcess_WaitForData(process, &data, &length, &timeoutAsDbl);
2067 if (pipe == cmsysProcess_Pipe_Timeout) {
2068 // Timeout has been exceeded.
2071 if (pipe == cmsysProcess_Pipe_STDOUT) {
2072 processOutput.DecodeText(data, length, strdata, 1);
2073 // Append to the stdout buffer.
2074 std::vector<char>::size_type size = out.size();
2075 cm::append(out, strdata);
2076 outiter = out.begin() + size;
2077 } else if (pipe == cmsysProcess_Pipe_STDERR) {
2078 processOutput.DecodeText(data, length, strdata, 2);
2079 // Append to the stderr buffer.
2080 std::vector<char>::size_type size = err.size();
2081 cm::append(err, strdata);
2082 erriter = err.begin() + size;
2083 } else if (pipe == cmsysProcess_Pipe_None) {
2084 // Both stdout and stderr pipes have broken. Return leftover data.
2085 processOutput.DecodeText(std::string(), strdata, 1);
2086 if (!strdata.empty()) {
2087 std::vector<char>::size_type size = out.size();
2088 cm::append(out, strdata);
2089 outiter = out.begin() + size;
2091 processOutput.DecodeText(std::string(), strdata, 2);
2092 if (!strdata.empty()) {
2093 std::vector<char>::size_type size = err.size();
2094 cm::append(err, strdata);
2095 erriter = err.begin() + size;
2098 line.append(out.data(), outiter - out.begin());
2099 out.erase(out.begin(), out.end());
2100 return cmsysProcess_Pipe_STDOUT;
2103 line.append(err.data(), erriter - err.begin());
2104 err.erase(err.begin(), err.end());
2105 return cmsysProcess_Pipe_STDERR;
2107 return cmsysProcess_Pipe_None;
2113 static void EnsureStdPipe(DWORD fd)
2115 if (GetStdHandle(fd) != INVALID_HANDLE_VALUE) {
2118 SECURITY_ATTRIBUTES sa;
2119 sa.nLength = sizeof(sa);
2120 sa.lpSecurityDescriptor = NULL;
2121 sa.bInheritHandle = TRUE;
2123 HANDLE h = CreateFileW(
2125 fd == STD_INPUT_HANDLE ? FILE_GENERIC_READ
2126 : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES,
2127 FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, NULL);
2129 if (h == INVALID_HANDLE_VALUE) {
2130 LPSTR message = NULL;
2131 DWORD size = FormatMessageA(
2132 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
2133 FORMAT_MESSAGE_IGNORE_INSERTS,
2134 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2135 (LPSTR)&message, 0, NULL);
2136 std::string msg = std::string(message, size);
2138 std::cerr << "failed to open NUL for missing stdio pipe: " << msg;
2142 SetStdHandle(fd, h);
2145 void cmSystemTools::EnsureStdPipes()
2147 EnsureStdPipe(STD_INPUT_HANDLE);
2148 EnsureStdPipe(STD_OUTPUT_HANDLE);
2149 EnsureStdPipe(STD_ERROR_HANDLE);
2152 static void EnsureStdPipe(int fd)
2154 if (fcntl(fd, F_GETFD) != -1 || errno != EBADF) {
2158 int f = open("/dev/null", fd == STDIN_FILENO ? O_RDONLY : O_WRONLY);
2160 perror("failed to open /dev/null for missing stdio pipe");
2169 void cmSystemTools::EnsureStdPipes()
2171 EnsureStdPipe(STDIN_FILENO);
2172 EnsureStdPipe(STDOUT_FILENO);
2173 EnsureStdPipe(STDERR_FILENO);
2177 void cmSystemTools::DoNotInheritStdPipes()
2180 // Check to see if we are attached to a console
2181 // if so, then do not stop the inherited pipes
2182 // or stdout and stderr will not show up in dos
2184 CONSOLE_SCREEN_BUFFER_INFO hOutInfo;
2185 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
2186 if (GetConsoleScreenBufferInfo(hOut, &hOutInfo)) {
2190 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
2191 DuplicateHandle(GetCurrentProcess(), out, GetCurrentProcess(), &out, 0,
2192 FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
2193 SetStdHandle(STD_OUTPUT_HANDLE, out);
2196 HANDLE out = GetStdHandle(STD_ERROR_HANDLE);
2197 DuplicateHandle(GetCurrentProcess(), out, GetCurrentProcess(), &out, 0,
2198 FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
2199 SetStdHandle(STD_ERROR_HANDLE, out);
2205 # ifndef CRYPT_SILENT
2206 # define CRYPT_SILENT 0x40 /* Not defined by VS 6 version of header. */
2208 static int WinCryptRandom(void* data, size_t size)
2211 HCRYPTPROV hProvider = 0;
2212 if (CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL,
2213 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
2214 result = CryptGenRandom(hProvider, (DWORD)size, (BYTE*)data) ? 1 : 0;
2215 CryptReleaseContext(hProvider, 0);
2221 unsigned int cmSystemTools::RandomSeed()
2223 #if defined(_WIN32) && !defined(__CYGWIN__)
2224 unsigned int seed = 0;
2226 // Try using a real random source.
2227 if (WinCryptRandom(&seed, sizeof(seed))) {
2231 // Fall back to the time and pid.
2233 GetSystemTimeAsFileTime(&ft);
2234 unsigned int t1 = static_cast<unsigned int>(ft.dwHighDateTime);
2235 unsigned int t2 = static_cast<unsigned int>(ft.dwLowDateTime);
2236 unsigned int pid = static_cast<unsigned int>(GetCurrentProcessId());
2237 return t1 ^ t2 ^ pid;
2241 unsigned int integer;
2242 char bytes[sizeof(unsigned int)];
2245 // Try using a real random source.
2246 cmsys::ifstream fin;
2247 fin.rdbuf()->pubsetbuf(nullptr, 0); // Unbuffered read.
2248 fin.open("/dev/urandom");
2249 if (fin.good() && fin.read(seed.bytes, sizeof(seed)) &&
2250 fin.gcount() == sizeof(seed)) {
2251 return seed.integer;
2254 // Fall back to the time and pid.
2256 gettimeofday(&t, nullptr);
2257 unsigned int pid = static_cast<unsigned int>(getpid());
2258 unsigned int tv_sec = static_cast<unsigned int>(t.tv_sec);
2259 unsigned int tv_usec = static_cast<unsigned int>(t.tv_usec);
2260 // Since tv_usec never fills more than 11 bits we shift it to fill
2261 // in the slow-changing high-order bits of tv_sec.
2262 return tv_sec ^ (tv_usec << 21) ^ pid;
2266 static std::string cmSystemToolsCMakeCommand;
2267 static std::string cmSystemToolsCTestCommand;
2268 static std::string cmSystemToolsCPackCommand;
2269 static std::string cmSystemToolsCMakeCursesCommand;
2270 static std::string cmSystemToolsCMakeGUICommand;
2271 static std::string cmSystemToolsCMClDepsCommand;
2272 static std::string cmSystemToolsCMakeRoot;
2273 static std::string cmSystemToolsHTMLDoc;
2274 void cmSystemTools::FindCMakeResources(const char* argv0)
2276 std::string exe_dir;
2277 #if defined(_WIN32) && !defined(__CYGWIN__)
2278 (void)argv0; // ignore this on windows
2279 wchar_t modulepath[_MAX_PATH];
2280 ::GetModuleFileNameW(NULL, modulepath, sizeof(modulepath));
2281 std::string path = cmsys::Encoding::ToNarrow(modulepath);
2282 std::string realPath =
2283 cmSystemTools::GetRealPathResolvingWindowsSubst(path, NULL);
2284 if (realPath.empty()) {
2287 exe_dir = cmSystemTools::GetFilenamePath(realPath);
2288 #elif defined(__APPLE__)
2289 (void)argv0; // ignore this on OS X
2290 # define CM_EXE_PATH_LOCAL_SIZE 16384
2291 char exe_path_local[CM_EXE_PATH_LOCAL_SIZE];
2292 # if defined(MAC_OS_X_VERSION_10_3) && !defined(MAC_OS_X_VERSION_10_4)
2293 unsigned long exe_path_size = CM_EXE_PATH_LOCAL_SIZE;
2295 uint32_t exe_path_size = CM_EXE_PATH_LOCAL_SIZE;
2297 # undef CM_EXE_PATH_LOCAL_SIZE
2298 char* exe_path = exe_path_local;
2299 if (_NSGetExecutablePath(exe_path, &exe_path_size) < 0) {
2300 exe_path = static_cast<char*>(malloc(exe_path_size));
2301 _NSGetExecutablePath(exe_path, &exe_path_size);
2304 cmSystemTools::GetFilenamePath(cmSystemTools::GetRealPath(exe_path));
2305 if (exe_path != exe_path_local) {
2308 if (cmSystemTools::GetFilenameName(exe_dir) == "MacOS") {
2309 // The executable is inside an application bundle.
2310 // Look for ..<CMAKE_BIN_DIR> (install tree) and then fall back to
2311 // ../../../bin (build tree).
2312 exe_dir = cmSystemTools::GetFilenamePath(exe_dir);
2313 if (cmSystemTools::FileExists(exe_dir + CMAKE_BIN_DIR "/cmake")) {
2314 exe_dir += CMAKE_BIN_DIR;
2316 exe_dir = cmSystemTools::GetFilenamePath(exe_dir);
2317 exe_dir = cmSystemTools::GetFilenamePath(exe_dir);
2321 std::string errorMsg;
2323 if (cmSystemTools::FindProgramPath(argv0, exe, errorMsg)) {
2325 exe = cmSystemTools::GetRealPath(exe);
2326 exe_dir = cmSystemTools::GetFilenamePath(exe);
2331 exe_dir = cmSystemTools::GetActualCaseForPath(exe_dir);
2332 cmSystemToolsCMakeCommand =
2333 cmStrCat(exe_dir, "/cmake", cmSystemTools::GetExecutableExtension());
2334 #ifdef CMAKE_BOOTSTRAP
2335 // The bootstrap cmake does not provide the other tools,
2336 // so use the directory where they are about to be built.
2337 exe_dir = CMAKE_BOOTSTRAP_BINARY_DIR "/bin";
2339 cmSystemToolsCTestCommand =
2340 cmStrCat(exe_dir, "/ctest", cmSystemTools::GetExecutableExtension());
2341 cmSystemToolsCPackCommand =
2342 cmStrCat(exe_dir, "/cpack", cmSystemTools::GetExecutableExtension());
2343 cmSystemToolsCMakeGUICommand =
2344 cmStrCat(exe_dir, "/cmake-gui", cmSystemTools::GetExecutableExtension());
2345 if (!cmSystemTools::FileExists(cmSystemToolsCMakeGUICommand)) {
2346 cmSystemToolsCMakeGUICommand.clear();
2348 cmSystemToolsCMakeCursesCommand =
2349 cmStrCat(exe_dir, "/ccmake", cmSystemTools::GetExecutableExtension());
2350 if (!cmSystemTools::FileExists(cmSystemToolsCMakeCursesCommand)) {
2351 cmSystemToolsCMakeCursesCommand.clear();
2353 cmSystemToolsCMClDepsCommand =
2354 cmStrCat(exe_dir, "/cmcldeps", cmSystemTools::GetExecutableExtension());
2355 if (!cmSystemTools::FileExists(cmSystemToolsCMClDepsCommand)) {
2356 cmSystemToolsCMClDepsCommand.clear();
2359 #ifndef CMAKE_BOOTSTRAP
2361 // - "<prefix><CMAKE_BIN_DIR>/cmake"
2362 // - "<prefix><CMAKE_DATA_DIR>"
2363 // - "<prefix><CMAKE_DOC_DIR>"
2364 if (cmHasLiteralSuffix(exe_dir, CMAKE_BIN_DIR)) {
2365 std::string const prefix =
2366 exe_dir.substr(0, exe_dir.size() - cmStrLen(CMAKE_BIN_DIR));
2367 cmSystemToolsCMakeRoot = cmStrCat(prefix, CMAKE_DATA_DIR);
2368 if (cmSystemTools::FileExists(
2369 cmStrCat(prefix, CMAKE_DOC_DIR "/html/index.html"))) {
2370 cmSystemToolsHTMLDoc = cmStrCat(prefix, CMAKE_DOC_DIR "/html");
2373 if (cmSystemToolsCMakeRoot.empty() ||
2374 !cmSystemTools::FileExists(
2375 cmStrCat(cmSystemToolsCMakeRoot, "/Modules/CMake.cmake"))) {
2376 // Build tree has "<build>/bin[/<config>]/cmake" and
2377 // "<build>/CMakeFiles/CMakeSourceDir.txt".
2378 std::string dir = cmSystemTools::GetFilenamePath(exe_dir);
2379 std::string src_dir_txt = cmStrCat(dir, "/CMakeFiles/CMakeSourceDir.txt");
2380 cmsys::ifstream fin(src_dir_txt.c_str());
2381 std::string src_dir;
2382 if (fin && cmSystemTools::GetLineFromStream(fin, src_dir) &&
2383 cmSystemTools::FileIsDirectory(src_dir)) {
2384 cmSystemToolsCMakeRoot = src_dir;
2386 dir = cmSystemTools::GetFilenamePath(dir);
2387 src_dir_txt = cmStrCat(dir, "/CMakeFiles/CMakeSourceDir.txt");
2388 cmsys::ifstream fin2(src_dir_txt.c_str());
2389 if (fin2 && cmSystemTools::GetLineFromStream(fin2, src_dir) &&
2390 cmSystemTools::FileIsDirectory(src_dir)) {
2391 cmSystemToolsCMakeRoot = src_dir;
2394 if (!cmSystemToolsCMakeRoot.empty() && cmSystemToolsHTMLDoc.empty() &&
2395 cmSystemTools::FileExists(
2396 cmStrCat(dir, "/Utilities/Sphinx/html/index.html"))) {
2397 cmSystemToolsHTMLDoc = cmStrCat(dir, "/Utilities/Sphinx/html");
2401 // Bootstrap build knows its source.
2402 cmSystemToolsCMakeRoot = CMAKE_BOOTSTRAP_SOURCE_DIR;
2406 std::string const& cmSystemTools::GetCMakeCommand()
2408 return cmSystemToolsCMakeCommand;
2411 std::string const& cmSystemTools::GetCTestCommand()
2413 return cmSystemToolsCTestCommand;
2416 std::string const& cmSystemTools::GetCPackCommand()
2418 return cmSystemToolsCPackCommand;
2421 std::string const& cmSystemTools::GetCMakeCursesCommand()
2423 return cmSystemToolsCMakeCursesCommand;
2426 std::string const& cmSystemTools::GetCMakeGUICommand()
2428 return cmSystemToolsCMakeGUICommand;
2431 std::string const& cmSystemTools::GetCMClDepsCommand()
2433 return cmSystemToolsCMClDepsCommand;
2436 std::string const& cmSystemTools::GetCMakeRoot()
2438 return cmSystemToolsCMakeRoot;
2441 std::string const& cmSystemTools::GetHTMLDoc()
2443 return cmSystemToolsHTMLDoc;
2446 std::string cmSystemTools::GetCurrentWorkingDirectory()
2448 return cmSystemTools::CollapseFullPath(
2449 cmsys::SystemTools::GetCurrentWorkingDirectory());
2452 void cmSystemTools::MakefileColorEcho(int color, const char* message,
2453 bool newline, bool enabled)
2455 // On some platforms (an MSYS prompt) cmsysTerminal may not be able
2456 // to determine whether the stream is displayed on a tty. In this
2457 // case it assumes no unless we tell it otherwise. Since we want
2458 // color messages to be displayed for users we will assume yes.
2459 // However, we can test for some situations when the answer is most
2461 int assumeTTY = cmsysTerminal_Color_AssumeTTY;
2462 if (cmSystemTools::HasEnv("DART_TEST_FROM_DART") ||
2463 cmSystemTools::HasEnv("DASHBOARD_TEST_FROM_CTEST") ||
2464 cmSystemTools::HasEnv("CTEST_INTERACTIVE_DEBUG_MODE")) {
2465 // Avoid printing color escapes during dashboard builds.
2469 if (enabled && color != cmsysTerminal_Color_Normal) {
2470 // Print with color. Delay the newline until later so that
2471 // all color restore sequences appear before it.
2472 cmsysTerminal_cfprintf(color | assumeTTY, stdout, "%s", message);
2474 // Color is disabled. Print without color.
2475 fprintf(stdout, "%s", message);
2479 fprintf(stdout, "\n");
2483 bool cmSystemTools::GuessLibrarySOName(std::string const& fullPath,
2484 std::string& soname)
2486 // For ELF shared libraries use a real parser to get the correct
2488 cmELF elf(fullPath.c_str());
2490 return elf.GetSOName(soname);
2493 // If the file is not a symlink we have no guess for its soname.
2494 if (!cmSystemTools::FileIsSymlink(fullPath)) {
2497 if (!cmSystemTools::ReadSymlink(fullPath, soname)) {
2501 // If the symlink has a path component we have no guess for the soname.
2502 if (!cmSystemTools::GetFilenamePath(soname).empty()) {
2506 // If the symlink points at an extended version of the same name
2507 // assume it is the soname.
2508 std::string name = cmSystemTools::GetFilenameName(fullPath);
2509 return soname.length() > name.length() &&
2510 soname.compare(0, name.length(), name) == 0;
2513 bool cmSystemTools::GuessLibraryInstallName(std::string const& fullPath,
2514 std::string& soname)
2516 #if defined(CMake_USE_MACH_PARSER)
2517 cmMachO macho(fullPath.c_str());
2519 return macho.GetInstallName(soname);
2529 static std::string::size_type cmSystemToolsFindRPath(
2530 cm::string_view const& have, cm::string_view const& want)
2532 std::string::size_type pos = 0;
2533 while (pos < have.size()) {
2534 // Look for an occurrence of the string.
2535 std::string::size_type const beg = have.find(want, pos);
2536 if (beg == std::string::npos) {
2537 return std::string::npos;
2540 // Make sure it is separated from preceding entries.
2541 if (beg > 0 && have[beg - 1] != ':') {
2546 // Make sure it is separated from following entries.
2547 std::string::size_type const end = beg + want.size();
2548 if (end < have.size() && have[end] != ':') {
2553 // Return the position of the path portion.
2557 // The desired rpath was not found.
2558 return std::string::npos;
2562 struct cmSystemToolsRPathInfo
2564 unsigned long Position;
2570 using EmptyCallback = std::function<bool(std::string*, const cmELF&)>;
2571 using AdjustCallback = std::function<bool(
2572 cm::optional<std::string>&, const std::string&, const char*, std::string*)>;
2574 cm::optional<bool> AdjustRPathELF(std::string const& file,
2575 const EmptyCallback& emptyCallback,
2576 const AdjustCallback& adjustCallback,
2577 std::string* emsg, bool* changed)
2583 bool remove_rpath = true;
2584 cmSystemToolsRPathInfo rp[2];
2586 // Parse the ELF binary.
2587 cmELF elf(file.c_str());
2589 return cm::nullopt; // Not a valid ELF file.
2592 // Get the RPATH and RUNPATH entries from it.
2594 cmELF::StringEntry const* se[2] = { nullptr, nullptr };
2595 const char* se_name[2] = { nullptr, nullptr };
2596 if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
2597 se[se_count] = se_rpath;
2598 se_name[se_count] = "RPATH";
2601 if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
2602 se[se_count] = se_runpath;
2603 se_name[se_count] = "RUNPATH";
2606 if (se_count == 0) {
2607 return emptyCallback(emsg, elf);
2610 for (int i = 0; i < se_count; ++i) {
2611 // If both RPATH and RUNPATH refer to the same string literal it
2612 // needs to be changed only once.
2613 if (rp_count && rp[0].Position == se[i]->Position) {
2617 // Store information about the entry in the file.
2618 rp[rp_count].Position = se[i]->Position;
2619 rp[rp_count].Size = se[i]->Size;
2620 rp[rp_count].Name = se_name[i];
2622 // Adjust the rpath.
2623 cm::optional<std::string> outRPath;
2624 if (!adjustCallback(outRPath, se[i]->Value, se_name[i], emsg)) {
2629 if (!outRPath->empty()) {
2630 remove_rpath = false;
2633 // Make sure there is enough room to store the new rpath and at
2634 // least one null terminator.
2635 if (rp[rp_count].Size < outRPath->length() + 1) {
2637 *emsg = cmStrCat("The replacement path is too long for the ",
2638 se_name[i], " entry.");
2643 // This entry is ready for update.
2644 rp[rp_count].Value = std::move(*outRPath);
2647 remove_rpath = false;
2652 // If no runtime path needs to be changed, we are done.
2653 if (rp_count == 0) {
2657 // If the resulting rpath is empty, just remove the entire entry instead.
2659 return cmSystemTools::RemoveRPath(file, emsg, changed);
2663 // Open the file for update.
2664 cmsys::ofstream f(file.c_str(),
2665 std::ios::in | std::ios::out | std::ios::binary);
2668 *emsg = "Error opening file for update.";
2673 // Store the new RPATH and RUNPATH strings.
2674 for (int i = 0; i < rp_count; ++i) {
2675 // Seek to the RPATH position.
2676 if (!f.seekp(rp[i].Position)) {
2678 *emsg = cmStrCat("Error seeking to ", rp[i].Name, " position.");
2683 // Write the new rpath. Follow it with enough null terminators to
2684 // fill the string table entry.
2686 for (unsigned long j = rp[i].Value.length(); j < rp[i].Size; ++j) {
2690 // Make sure it wrote correctly.
2693 *emsg = cmStrCat("Error writing the new ", rp[i].Name,
2694 " string to the file.");
2701 // Everything was updated successfully.
2708 std::function<bool(std::string*, const cmELF&)> MakeEmptyCallback(
2709 const std::string& newRPath)
2711 return [newRPath](std::string* emsg, const cmELF& elf) -> bool {
2712 if (newRPath.empty()) {
2713 // The new rpath is empty and there is no rpath anyway so it is
2719 cmStrCat("No valid ELF RPATH or RUNPATH entry exists in the file; ",
2720 elf.GetErrorMessage());
2727 static cm::optional<bool> ChangeRPathELF(std::string const& file,
2728 std::string const& oldRPath,
2729 std::string const& newRPath,
2730 bool removeEnvironmentRPath,
2731 std::string* emsg, bool* changed)
2733 auto adjustCallback = [oldRPath, newRPath, removeEnvironmentRPath](
2734 cm::optional<std::string>& outRPath,
2735 const std::string& inRPath, const char* se_name,
2736 std::string* emsg2) -> bool {
2737 // Make sure the current rpath contains the old rpath.
2738 std::string::size_type pos = cmSystemToolsFindRPath(inRPath, oldRPath);
2739 if (pos == std::string::npos) {
2740 // If it contains the new rpath instead then it is okay.
2741 if (cmSystemToolsFindRPath(inRPath, newRPath) != std::string::npos) {
2745 std::ostringstream e;
2746 /* clang-format off */
2747 e << "The current " << se_name << " is:\n"
2748 << " " << inRPath << "\n"
2749 << "which does not contain:\n"
2750 << " " << oldRPath << "\n"
2751 << "as was expected.";
2752 /* clang-format on */
2758 std::string::size_type prefix_len = pos;
2760 // If oldRPath was at the end of the file's RPath, and newRPath is empty,
2761 // we should remove the unnecessary ':' at the end.
2762 if (newRPath.empty() && pos > 0 && inRPath[pos - 1] == ':' &&
2763 pos + oldRPath.length() == inRPath.length()) {
2767 // Construct the new value which preserves the part of the path
2768 // not being changed.
2770 if (!removeEnvironmentRPath) {
2771 *outRPath += inRPath.substr(0, prefix_len);
2773 *outRPath += newRPath;
2774 *outRPath += inRPath.substr(pos + oldRPath.length());
2779 return AdjustRPathELF(file, MakeEmptyCallback(newRPath), adjustCallback,
2783 static cm::optional<bool> SetRPathELF(std::string const& file,
2784 std::string const& newRPath,
2785 std::string* emsg, bool* changed)
2787 auto adjustCallback = [newRPath](cm::optional<std::string>& outRPath,
2788 const std::string& inRPath,
2789 const char* /*se_name*/, std::string *
2791 if (inRPath != newRPath) {
2792 outRPath = newRPath;
2797 return AdjustRPathELF(file, MakeEmptyCallback(newRPath), adjustCallback,
2800 static cm::optional<bool> ChangeRPathXCOFF(std::string const& file,
2801 std::string const& oldRPath,
2802 std::string const& newRPath,
2803 bool removeEnvironmentRPath,
2804 std::string* emsg, bool* changed)
2809 #if !defined(CMake_USE_XCOFF_PARSER)
2813 (void)removeEnvironmentRPath;
2818 cmXCOFF xcoff(file.c_str(), cmXCOFF::Mode::ReadWrite);
2820 return cm::nullopt; // Not a valid XCOFF file
2822 if (cm::optional<cm::string_view> maybeLibPath = xcoff.GetLibPath()) {
2823 cm::string_view libPath = *maybeLibPath;
2824 // Make sure the current rpath contains the old rpath.
2825 std::string::size_type pos = cmSystemToolsFindRPath(libPath, oldRPath);
2826 if (pos == std::string::npos) {
2827 // If it contains the new rpath instead then it is okay.
2828 if (cmSystemToolsFindRPath(libPath, newRPath) != std::string::npos) {
2832 std::ostringstream e;
2833 /* clang-format off */
2834 e << "The current RPATH is:\n"
2835 << " " << libPath << "\n"
2836 << "which does not contain:\n"
2837 << " " << oldRPath << "\n"
2838 << "as was expected.";
2839 /* clang-format on */
2845 // The prefix is either empty or ends in a ':'.
2846 cm::string_view prefix = libPath.substr(0, pos);
2847 if (newRPath.empty() && !prefix.empty()) {
2848 prefix.remove_suffix(1);
2851 // The suffix is either empty or starts in a ':'.
2852 cm::string_view suffix = libPath.substr(pos + oldRPath.length());
2854 // Construct the new value which preserves the part of the path
2855 // not being changed.
2856 std::string newLibPath;
2857 if (!removeEnvironmentRPath) {
2858 newLibPath = std::string(prefix);
2860 newLibPath += newRPath;
2861 newLibPath += suffix;
2863 chg = xcoff.SetLibPath(newLibPath);
2867 *emsg = xcoff.GetErrorMessage();
2872 // Everything was updated successfully.
2880 static cm::optional<bool> SetRPathXCOFF(std::string const& /*file*/,
2881 std::string const& /*newRPath*/,
2882 std::string* /*emsg*/,
2885 return cm::nullopt; // Not implemented.
2888 bool cmSystemTools::ChangeRPath(std::string const& file,
2889 std::string const& oldRPath,
2890 std::string const& newRPath,
2891 bool removeEnvironmentRPath, std::string* emsg,
2894 if (cm::optional<bool> result = ChangeRPathELF(
2895 file, oldRPath, newRPath, removeEnvironmentRPath, emsg, changed)) {
2896 return result.value();
2898 if (cm::optional<bool> result = ChangeRPathXCOFF(
2899 file, oldRPath, newRPath, removeEnvironmentRPath, emsg, changed)) {
2900 return result.value();
2902 // The file format is not recognized. Assume it has no RPATH.
2903 if (newRPath.empty()) {
2904 // The caller wanted no RPATH anyway.
2908 *emsg = "The file format is not recognized.";
2913 bool cmSystemTools::SetRPath(std::string const& file,
2914 std::string const& newRPath, std::string* emsg,
2917 if (cm::optional<bool> result = SetRPathELF(file, newRPath, emsg, changed)) {
2918 return result.value();
2920 if (cm::optional<bool> result =
2921 SetRPathXCOFF(file, newRPath, emsg, changed)) {
2922 return result.value();
2924 // The file format is not recognized. Assume it has no RPATH.
2925 if (newRPath.empty()) {
2926 // The caller wanted no RPATH anyway.
2930 *emsg = "The file format is not recognized.";
2936 bool VersionCompare(cmSystemTools::CompareOp op, const char* lhss,
2939 const char* endl = lhss;
2940 const char* endr = rhss;
2944 while (((*endl >= '0') && (*endl <= '9')) ||
2945 ((*endr >= '0') && (*endr <= '9'))) {
2946 // Do component-wise comparison.
2947 lhs = strtoul(endl, const_cast<char**>(&endl), 10);
2948 rhs = strtoul(endr, const_cast<char**>(&endr), 10);
2951 // lhs < rhs, so true if operation is LESS
2952 return (op & cmSystemTools::OP_LESS) != 0;
2955 // lhs > rhs, so true if operation is GREATER
2956 return (op & cmSystemTools::OP_GREATER) != 0;
2967 // lhs == rhs, so true if operation is EQUAL
2968 return (op & cmSystemTools::OP_EQUAL) != 0;
2972 bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
2973 const std::string& lhs,
2974 const std::string& rhs)
2976 return ::VersionCompare(op, lhs.c_str(), rhs.c_str());
2978 bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
2979 const std::string& lhs, const char rhs[])
2981 return ::VersionCompare(op, lhs.c_str(), rhs);
2984 bool cmSystemTools::VersionCompareEqual(std::string const& lhs,
2985 std::string const& rhs)
2987 return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, lhs, rhs);
2990 bool cmSystemTools::VersionCompareGreater(std::string const& lhs,
2991 std::string const& rhs)
2993 return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER, lhs, rhs);
2996 bool cmSystemTools::VersionCompareGreaterEq(std::string const& lhs,
2997 std::string const& rhs)
2999 return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, lhs,
3003 static size_t cm_strverscmp_find_first_difference_or_end(const char* lhs,
3007 /* Step forward until we find a difference or both strings end together.
3008 The difference may lie on the null-terminator of one string. */
3009 while (lhs[i] == rhs[i] && lhs[i] != 0) {
3015 static size_t cm_strverscmp_find_digits_begin(const char* s, size_t i)
3017 /* Step back until we are not preceded by a digit. */
3018 while (i > 0 && isdigit(s[i - 1])) {
3024 static size_t cm_strverscmp_find_digits_end(const char* s, size_t i)
3026 /* Step forward over digits. */
3027 while (isdigit(s[i])) {
3033 static size_t cm_strverscmp_count_leading_zeros(const char* s, size_t b)
3036 /* Step forward over zeros that are followed by another digit. */
3037 while (s[i] == '0' && isdigit(s[i + 1])) {
3043 static int cm_strverscmp(const char* lhs, const char* rhs)
3045 size_t const i = cm_strverscmp_find_first_difference_or_end(lhs, rhs);
3046 if (lhs[i] != rhs[i]) {
3047 /* The strings differ starting at 'i'. Check for a digit sequence. */
3048 size_t const b = cm_strverscmp_find_digits_begin(lhs, i);
3049 if (b != i || (isdigit(lhs[i]) && isdigit(rhs[i]))) {
3050 /* A digit sequence starts at 'b', preceding or at 'i'. */
3052 /* Look for leading zeros, implying a leading decimal point. */
3053 size_t const lhs_zeros = cm_strverscmp_count_leading_zeros(lhs, b);
3054 size_t const rhs_zeros = cm_strverscmp_count_leading_zeros(rhs, b);
3055 if (lhs_zeros != rhs_zeros) {
3056 /* The side with more leading zeros orders first. */
3057 return rhs_zeros > lhs_zeros ? 1 : -1;
3059 if (lhs_zeros == 0) {
3060 /* No leading zeros; compare digit sequence lengths. */
3061 size_t const lhs_end = cm_strverscmp_find_digits_end(lhs, i);
3062 size_t const rhs_end = cm_strverscmp_find_digits_end(rhs, i);
3063 if (lhs_end != rhs_end) {
3064 /* The side with fewer digits orders first. */
3065 return lhs_end > rhs_end ? 1 : -1;
3071 /* Ordering was not decided by digit sequence lengths; compare bytes. */
3072 return lhs[i] - rhs[i];
3075 int cmSystemTools::strverscmp(std::string const& lhs, std::string const& rhs)
3077 return cm_strverscmp(lhs.c_str(), rhs.c_str());
3080 static cm::optional<bool> RemoveRPathELF(std::string const& file,
3081 std::string* emsg, bool* removed)
3087 unsigned long zeroPosition[2] = { 0, 0 };
3088 unsigned long zeroSize[2] = { 0, 0 };
3089 unsigned long bytesBegin = 0;
3090 std::vector<char> bytes;
3092 // Parse the ELF binary.
3093 cmELF elf(file.c_str());
3095 return cm::nullopt; // Not a valid ELF file.
3098 // Get the RPATH and RUNPATH entries from it and sort them by index
3099 // in the dynamic section header.
3101 cmELF::StringEntry const* se[2] = { nullptr, nullptr };
3102 if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
3103 se[se_count++] = se_rpath;
3105 if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
3106 se[se_count++] = se_runpath;
3108 if (se_count == 0) {
3109 // There is no RPATH or RUNPATH anyway.
3112 if (se_count == 2 && se[1]->IndexInSection < se[0]->IndexInSection) {
3113 std::swap(se[0], se[1]);
3116 // Obtain a copy of the dynamic entries
3117 cmELF::DynamicEntryList dentries = elf.GetDynamicEntries();
3118 if (dentries.empty()) {
3119 // This should happen only for invalid ELF files where a DT_NULL
3120 // appears before the end of the table.
3122 *emsg = "DYNAMIC section contains a DT_NULL before the end.";
3127 // Save information about the string entries to be zeroed.
3128 zeroCount = se_count;
3129 for (int i = 0; i < se_count; ++i) {
3130 zeroPosition[i] = se[i]->Position;
3131 zeroSize[i] = se[i]->Size;
3134 // Get size of one DYNAMIC entry
3135 unsigned long const sizeof_dentry =
3136 elf.GetDynamicEntryPosition(1) - elf.GetDynamicEntryPosition(0);
3138 // Adjust the entry list as necessary to remove the run path
3139 unsigned long entriesErased = 0;
3140 for (auto it = dentries.begin(); it != dentries.end();) {
3141 if (it->first == cmELF::TagRPath || it->first == cmELF::TagRunPath) {
3142 it = dentries.erase(it);
3146 if (it->first == cmELF::TagMipsRldMapRel && elf.IsMIPS()) {
3147 // Background: debuggers need to know the "linker map" which contains
3148 // the addresses each dynamic object is loaded at. Most arches use
3149 // the DT_DEBUG tag which the dynamic linker writes to (directly) and
3150 // contain the location of the linker map, however on MIPS the
3151 // .dynamic section is always read-only so this is not possible. MIPS
3152 // objects instead contain a DT_MIPS_RLD_MAP tag which contains the
3153 // address where the dynamic linker will write to (an indirect
3154 // version of DT_DEBUG). Since this doesn't work when using PIE, a
3155 // relative equivalent was created - DT_MIPS_RLD_MAP_REL. Since this
3156 // version contains a relative offset, moving it changes the
3157 // calculated address. This may cause the dynamic linker to write
3158 // into memory it should not be changing.
3160 // To fix this, we adjust the value of DT_MIPS_RLD_MAP_REL here. If
3161 // we move it up by n bytes, we add n bytes to the value of this tag.
3162 it->second += entriesErased * sizeof_dentry;
3168 // Encode new entries list
3169 bytes = elf.EncodeDynamicEntries(dentries);
3170 bytesBegin = elf.GetDynamicEntryPosition(0);
3173 // Open the file for update.
3174 cmsys::ofstream f(file.c_str(),
3175 std::ios::in | std::ios::out | std::ios::binary);
3178 *emsg = "Error opening file for update.";
3183 // Write the new DYNAMIC table header.
3184 if (!f.seekp(bytesBegin)) {
3186 *emsg = "Error seeking to DYNAMIC table header for RPATH.";
3190 if (!f.write(bytes.data(), bytes.size())) {
3192 *emsg = "Error replacing DYNAMIC table header.";
3197 // Fill the RPATH and RUNPATH strings with zero bytes.
3198 for (int i = 0; i < zeroCount; ++i) {
3199 if (!f.seekp(zeroPosition[i])) {
3201 *emsg = "Error seeking to RPATH position.";
3205 for (unsigned long j = 0; j < zeroSize[i]; ++j) {
3210 *emsg = "Error writing the empty rpath string to the file.";
3216 // Everything was updated successfully.
3223 static cm::optional<bool> RemoveRPathXCOFF(std::string const& file,
3224 std::string* emsg, bool* removed)
3229 #if !defined(CMake_USE_XCOFF_PARSER)
3232 return cm::nullopt; // Cannot handle XCOFF files.
3234 cmXCOFF xcoff(file.c_str(), cmXCOFF::Mode::ReadWrite);
3236 return cm::nullopt; // Not a valid XCOFF file.
3238 bool rm = xcoff.RemoveLibPath();
3241 *emsg = xcoff.GetErrorMessage();
3252 bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg,
3255 if (cm::optional<bool> result = RemoveRPathELF(file, emsg, removed)) {
3256 return result.value();
3258 if (cm::optional<bool> result = RemoveRPathXCOFF(file, emsg, removed)) {
3259 return result.value();
3261 // The file format is not recognized. Assume it has no RPATH.
3265 bool cmSystemTools::CheckRPath(std::string const& file,
3266 std::string const& newRPath)
3268 // Parse the ELF binary.
3269 cmELF elf(file.c_str());
3271 // Get the RPATH or RUNPATH entry from it.
3272 cmELF::StringEntry const* se = elf.GetRPath();
3274 se = elf.GetRunPath();
3277 // Make sure the current rpath contains the new rpath.
3278 if (newRPath.empty()) {
3284 cmSystemToolsFindRPath(se->Value, newRPath) != std::string::npos) {
3290 #if defined(CMake_USE_XCOFF_PARSER)
3291 // Parse the XCOFF binary.
3292 cmXCOFF xcoff(file.c_str());
3294 if (cm::optional<cm::string_view> libPath = xcoff.GetLibPath()) {
3295 if (cmSystemToolsFindRPath(*libPath, newRPath) != std::string::npos) {
3302 // The file format is not recognized. Assume it has no RPATH.
3303 // Therefore we succeed if the new rpath is empty anyway.
3304 return newRPath.empty();
3307 bool cmSystemTools::RepeatedRemoveDirectory(const std::string& dir)
3310 // Windows sometimes locks files temporarily so try a few times.
3311 WindowsFileRetry retry = cmSystemTools::GetWindowsFileRetry();
3313 for (unsigned int i = 0; i < retry.Count; ++i) {
3314 if (cmSystemTools::RemoveADirectory(dir)) {
3317 cmSystemTools::Delay(retry.Delay);
3321 return static_cast<bool>(cmSystemTools::RemoveADirectory(dir));
3325 std::string cmSystemTools::EncodeURL(std::string const& in, bool escapeSlashes)
3329 char hexCh[4] = { 0, 0, 0, 0 };
3339 snprintf(hexCh, sizeof(hexCh), "%%%02X", static_cast<int>(c));
3342 if (escapeSlashes) {
3343 strcpy(hexCh, "%2F");
3354 cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName,
3355 std::string const& newName,
3356 std::string* errorMessage)
3361 if (cmsys::SystemTools::FileIsDirectory(origName)) {
3362 flags |= UV_FS_SYMLINK_DIR;
3365 int err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(),
3367 cmsys::Status status;
3370 status = cmsys::Status::Windows(uv_fs_get_system_error(&req));
3371 #elif UV_VERSION_MAJOR > 1 || (UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR >= 38)
3372 status = cmsys::Status::POSIX(uv_fs_get_system_error(&req));
3374 status = cmsys::Status::POSIX(-err);
3376 std::string e = cmStrCat("failed to create symbolic link '", newName,
3377 "': ", status.GetString());
3379 *errorMessage = std::move(e);
3381 cmSystemTools::Error(e);
3387 cmsys::Status cmSystemTools::CreateLink(std::string const& origName,
3388 std::string const& newName,
3389 std::string* errorMessage)
3393 uv_fs_link(nullptr, &req, origName.c_str(), newName.c_str(), nullptr);
3394 cmsys::Status status;
3397 status = cmsys::Status::Windows(uv_fs_get_system_error(&req));
3398 #elif UV_VERSION_MAJOR > 1 || (UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR >= 38)
3399 status = cmsys::Status::POSIX(uv_fs_get_system_error(&req));
3401 status = cmsys::Status::POSIX(-err);
3404 cmStrCat("failed to create link '", newName, "': ", status.GetString());
3406 *errorMessage = std::move(e);
3408 cmSystemTools::Error(e);
3414 cm::string_view cmSystemTools::GetSystemName()
3418 #elif defined(__ANDROID__)
3421 static struct utsname uts_name;
3422 static bool initialized = false;
3423 static cm::string_view systemName;
3427 if (uname(&uts_name) >= 0) {
3429 systemName = uts_name.sysname;
3431 if (cmIsOff(systemName)) {
3432 systemName = "UnknownOS";
3435 // fix for BSD/OS, remove the /
3436 static const cmsys::RegularExpression bsdOsRegex("BSD.OS");
3437 cmsys::RegularExpressionMatch match;
3438 if (bsdOsRegex.find(uts_name.sysname, match)) {
3439 systemName = "BSDOS";
3442 // fix for GNU/kFreeBSD, remove the GNU/
3443 if (systemName.find("kFreeBSD") != cm::string_view::npos) {
3444 systemName = "kFreeBSD";
3447 // fix for CYGWIN and MSYS which have windows version in them
3448 if (systemName.find("CYGWIN") != cm::string_view::npos) {
3449 systemName = "CYGWIN";
3452 if (systemName.find("MSYS") != cm::string_view::npos) {
3453 systemName = "MSYS";
3461 char cmSystemTools::GetSystemPathlistSeparator()