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(__DragonFly__) || defined(__NetBSD__) || \
12 // NOLINTNEXTLINE(bugprone-reserved-identifier)
13 # define _XOPEN_SOURCE 700
15 #if defined(__APPLE__)
16 // Restore Darwin APIs removed by _POSIX_C_SOURCE.
17 // NOLINTNEXTLINE(bugprone-reserved-identifier)
18 # define _DARWIN_C_SOURCE
21 #include "cmSystemTools.h"
23 #include <cm/optional>
24 #include <cmext/algorithm>
25 #include <cmext/string_view>
29 #include "cmDuration.h"
31 #include "cmMessageMetadata.h"
32 #include "cmProcessOutput.h"
34 #include "cmStringAlgorithms.h"
35 #include "cmUVHandlePtr.h"
36 #include "cmUVProcessChain.h"
37 #include "cmUVStream.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"
47 # define __LA_INT64_T la_int64_t
50 # define __LA_SSIZE_T la_ssize_t
54 #if defined(CMake_USE_MACH_PARSER)
58 #if defined(CMake_USE_XCOFF_PARSER)
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"
89 // include wincrypt.h after windows.h
90 # include <wincrypt.h>
94 # include <sys/time.h>
97 #if defined(_WIN32) && \
98 (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__MINGW32__))
102 #if defined(__APPLE__)
103 # include <mach-o/dyld.h>
107 # include <malloc.h> /* for malloc/free on QNX */
110 #if !defined(_WIN32) && !defined(__ANDROID__)
111 # include <sys/utsname.h>
114 #if defined(_MSC_VER) && _MSC_VER >= 1800
115 # define CM_WINDOWS_DEPRECATED_GetVersionEx
120 cmSystemTools::InterruptCallback s_InterruptCallback
;
121 cmSystemTools::MessageCallback s_MessageCallback
;
122 cmSystemTools::OutputCallback s_StderrCallback
;
123 cmSystemTools::OutputCallback s_StdoutCallback
;
127 #if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE)
128 // For GetEnvironmentVariables
130 extern __declspec(dllimport
) char** environ
;
132 extern char** environ
;
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
));
142 return archive_entry_pathname(entry
);
146 static int cm_archive_read_open_file(struct archive
* a
, const char* file
,
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
);
153 return archive_read_open_filename(a
, file
, block_size
);
159 #elif defined(__APPLE__)
160 # include <crt_externs.h>
162 # define environ (*_NSGetEnviron())
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);
189 if (ReadRegistryValue(key
.c_str(), val
, view
)) {
190 std::string reg
= cmStrCat('[', key
, ']');
191 cmSystemTools::ReplaceString(source
, reg
.c_str(), val
.c_str());
193 std::string reg
= cmStrCat('[', key
, ']');
194 cmSystemTools::ReplaceString(source
, reg
.c_str(), "/registry");
199 void cmSystemTools::ExpandRegistryValues(std::string
& source
,
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");
212 std::string
cmSystemTools::HelpFileName(cm::string_view str
)
214 std::string
name(str
);
215 cmSystemTools::ReplaceString(name
, "<", "");
216 cmSystemTools::ReplaceString(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();
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
) {
260 std::cerr
<< s
<< std::flush
;
264 void cmSystemTools::Stdout(const std::string
& s
)
266 if (s_StdoutCallback
) {
269 std::cout
<< s
<< std::flush
;
273 void cmSystemTools::Message(const std::string
& m
, const char* title
)
275 cmMessageMetadata md
;
280 void cmSystemTools::Message(const std::string
& m
, const cmMessageMetadata
& md
)
282 if (s_MessageCallback
) {
283 s_MessageCallback(m
, md
);
285 std::cerr
<< m
<< std::endl
;
289 void cmSystemTools::ReportLastSystemError(const char* msg
)
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;
307 for (const char* c
= command
; *c
; ++c
) {
311 } else if (*c
== '"') {
312 int backslash_pairs
= backslashes
>> 1;
313 int backslash_escaped
= backslashes
& 1;
314 arg
.append(backslash_pairs
, '\\');
316 if (backslash_escaped
) {
317 /* An odd number of backslashes precede this quote.
321 /* An even number of backslashes precede this quote.
322 It is not escaped. */
323 in_quotes
= !in_quotes
;
327 arg
.append(backslashes
, '\\');
332 } else if (in_argument
) {
343 arg
.append(backslashes
, '\\');
349 class cmSystemToolsArgV
354 cmSystemToolsArgV(char** argv
)
360 for (char** arg
= this->ArgV
; arg
&& *arg
; ++arg
) {
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));
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
);
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
);
398 cmSystemTools::GetLineFromStream(responseFile
, line
);
399 std::vector
<std::string
> args2
;
401 cmSystemTools::ParseWindowsCommandLine(line
.c_str(), args2
);
403 cmSystemTools::ParseUnixCommandLine(line
.c_str(), args2
);
405 cm::append(arg_full
, args2
);
408 arg_full
.push_back(arg
);
414 std::vector
<std::string
> cmSystemTools::ParseArguments(const std::string
& cmd
)
416 std::vector
<std::string
> args
;
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] == '\\'))) {
431 // Split the command into an argv array.
432 for (const char* c
= command
; *c
;) {
433 // Skip over whitespace.
434 while (*c
== ' ' || *c
== '\t') {
439 // Parse a quoted argument.
441 while (*c
&& *c
!= '"') {
449 } else if (*c
== '\'') {
450 // Parse a quoted argument.
452 while (*c
&& *c
!= '\'') {
461 // Parse an unquoted argument.
462 while (*c
&& *c
!= ' ' && *c
!= '\t') {
463 if (*c
== '\\' && !win_path
) {
481 bool cmSystemTools::SplitProgramFromArgs(std::string
const& command
,
482 std::string
& program
,
485 const char* c
= command
.c_str();
487 // Skip leading whitespace.
488 while (cmIsSpace(*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;
503 } else if (in_escape
) {
506 } else if (*c
== '\\') {
508 } else if (in_double
) {
514 } else if (*c
== '"') {
516 } else if (*c
== '\'') {
518 } else if (cmIsSpace(*c
)) {
525 // The remainder of the command line holds unparsed arguments.
528 return !in_single
&& !in_escape
&& !in_double
;
531 std::size_t cmSystemTools::CalculateCommandLineLengthLimit()
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.
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);
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
));
564 sz
= static_cast<size_t>(szArgMax
);
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
);
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;
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
);
606 auto* timedOutPtr
= static_cast<bool*>(t
->data
);
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
)) {
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
> {
633 pipe
.init(chain
.GetLoop(), 0);
634 uv_pipe_open(pipe
, stream
);
637 return cmUVStreamRead(
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
) {
648 if (outputflag
!= OUTPUT_NONE
) {
650 processOutput
.DecodeText(data
.data(), data
.size(), strdata
, id
);
654 cm::append(tempStd
, data
.data(), data
.data() + data
.size());
657 [&finished
, outputflag
, &processOutput
, id
, outputFunc
]() {
659 if (outputflag
!= OUTPUT_NONE
) {
661 processOutput
.DecodeText(std::string(), strdata
, id
);
662 if (!strdata
.empty()) {
670 startRead(outStream
, chain
.OutputStream(), captureStdOut
, tempStdOut
, 1,
671 cmSystemTools::Stdout
, outFinished
);
672 if (chain
.OutputStream() != chain
.ErrorStream()) {
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
);
684 captureStdOut
->assign(tempStdOut
.begin(), tempStdOut
.end());
685 processOutput
.DecodeText(*captureStdOut
, *captureStdOut
);
688 captureStdErr
->assign(tempStdErr
.begin(), tempStdErr
.end());
689 processOutput
.DecodeText(*captureStdErr
, *captureStdErr
);
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
));
703 auto const& status
= chain
.GetStatus(0);
704 auto exception
= status
.GetException();
706 switch (exception
.first
) {
707 case cmUVProcessChain::ExceptionCode::None
:
709 *retVal
= static_cast<int>(status
.ExitStatus
);
711 if (status
.ExitStatus
!= 0) {
717 if (outputflag
!= OUTPUT_NONE
) {
718 std::cerr
<< exception
.second
<< std::endl
;
721 captureStdErr
->append(exception
.second
);
722 } else if (captureStdOut
) {
723 captureStdOut
->append(exception
.second
);
733 bool cmSystemTools::RunSingleCommand(const std::string
& command
,
734 std::string
* captureStdOut
,
735 std::string
* captureStdErr
, int* retVal
,
736 const char* dir
, OutputOption outputflag
,
739 if (s_DisableRunCommandOutput
) {
740 outputflag
= OUTPUT_NONE
;
743 std::vector
<std::string
> args
= cmSystemTools::ParseArguments(command
);
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
)
767 for (std::string
const& headerExt
: headerExts
) {
768 hname
= cmStrCat(name
, '.', headerExt
);
769 if (cmSystemTools::FileExists(hname
)) {
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
);
785 while (dir
!= prevDir
) {
786 std::string path
= cmStrCat(dir
, "/", file
);
787 if (cmSystemTools::FileExists(path
)) {
790 if (dir
.size() < toplevel
.size()) {
794 dir
= cmSystemTools::GetParentDirectory(dir
);
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
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&) =
817 void SetPath(std::wstring
const& path
) { path_
= path
; }
821 uint32_t original_attr_bits_
;
824 SaveRestoreFileAttributes::SaveRestoreFileAttributes(
825 std::wstring
const& path
, uint32_t file_attrs_to_set
)
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
;
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
) {
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;
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
);
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];
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
) {
904 WindowsFileRetryInit fileInit
= InitWindowsFileRetry();
905 if (fileInit
.Explicit
) {
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
;
930 cmSystemTools::WindowsFileRetry
cmSystemTools::GetWindowsDirectoryRetry()
932 static cmSystemTools::WindowsFileRetry retry
=
933 InitWindowsDirectoryRetry().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"
952 # pragma warning(disable : 4996)
955 GetVersionExW((OSVERSIONINFOW
*)&osviex
);
956 # ifdef CM_WINDOWS_DEPRECATED_GetVersionEx
958 # pragma clang diagnostic pop
960 # pragma warning(pop)
964 WindowsVersion result
;
965 result
.dwMajorVersion
= osviex
.dwMajorVersion
;
966 result
.dwMinorVersion
= osviex
.dwMinorVersion
;
967 result
.dwBuildNumber
= osviex
.dwBuildNumber
;
972 std::string
cmSystemTools::GetRealPathResolvingWindowsSubst(
973 const std::string
& path
, std::string
* errorMessage
)
976 // uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found
977 std::string resolved_path
;
979 int err
= uv_fs_realpath(nullptr, &req
, path
.c_str(), nullptr);
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
,
996 *errorMessage
= std::string(message
, size
);
1001 resolved_path
= path
;
1003 return resolved_path
;
1005 return cmsys::SystemTools::GetRealPath(path
, errorMessage
);
1009 void cmSystemTools::InitializeLibUV()
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
);
1019 _set_fmode(_O_TEXT
);
1023 // Replace libuv's report handler with our own to suppress popups.
1024 cmSystemTools::EnableMSVCDebugHook();
1031 # include <wctype.h>
1033 using mode_t
= cmSystemTools::SystemTools::mode_t
;
1036 # include <sys/stat.h>
1039 inline int Mkdir(const char* dir
, const mode_t
* mode
)
1042 int ret
= _wmkdir(cmSystemTools::ConvertToWindowsExtendedPath(dir
).c_str());
1043 if (ret
== 0 && mode
)
1044 cmSystemTools::SystemTools::SetPermissions(dir
, *mode
);
1047 return mkdir(dir
, mode
? *mode
: 0777);
1051 cmsys::Status
cmSystemTools::MakeTempDirectory(std::string
& path
,
1055 return cmsys::Status::POSIX(EINVAL
);
1057 return cmSystemTools::MakeTempDirectory(&path
.front(), mode
);
1060 cmsys::Status
cmSystemTools::MakeTempDirectory(char* path
, const mode_t
* mode
)
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
1074 while ((sep
= strchr(sep
, '/'))) {
1075 // all underlying functions use C strings,
1076 // so temporarily end the string here
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
);
1104 if (mkdtemp(path
)) {
1109 return cmsys::Status::POSIX_errno();
1111 return cmsys::Status::Success();
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.
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
);
1134 cmSystemTools::CopyResult
cmSystemTools::CopySingleFile(
1135 std::string
const& oldname
, std::string
const& newname
, CopyWhen when
,
1136 CopyInputRecent inputRecent
, std::string
* err
)
1139 case CopyWhen::Always
:
1141 case CopyWhen::OnlyIfDifferent
:
1142 if (!FilesDiffer(oldname
, newname
)) {
1143 return CopyResult::Success
;
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
);
1159 // if cloning did not succeed, fall back to blockwise copy
1161 if (inputRecent
== CopyInputRecent::Yes
) {
1162 // Windows sometimes locks a file immediately after creation.
1163 // Retry a few times.
1164 WindowsFileRetry retry
= cmSystemTools::GetWindowsFileRetry();
1166 cmsys::SystemTools::CopyFileContentBlockwise(oldname
, newname
),
1167 status
.Path
== cmsys::SystemTools::CopyStatus::SourcePath
&&
1168 status
.GetPOSIX() == EACCES
&& --retry
.Count
)) {
1169 cmSystemTools::Delay(retry
.Delay
);
1172 status
= cmsys::SystemTools::CopyFileContentBlockwise(oldname
, newname
);
1175 static_cast<void>(inputRecent
);
1176 status
= cmsys::SystemTools::CopyFileContentBlockwise(oldname
, newname
);
1181 *err
= status
.GetString();
1182 switch (status
.Path
) {
1183 case cmsys::SystemTools::CopyStatus::SourcePath
:
1184 *err
= cmStrCat(*err
, " (input)");
1186 case cmsys::SystemTools::CopyStatus::DestPath
:
1187 *err
= cmStrCat(*err
, " (output)");
1193 return CopyResult::Failure
;
1196 perms
= SystemTools::SetPermissions(newname
, perm
);
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
,
1219 # ifndef INVALID_FILE_ATTRIBUTES
1220 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
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
) {
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
;
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
);
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
;
1292 *err
= cmsys::Status::Windows_GetLastError().GetString();
1294 return RenameResult::Failure
;
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
;
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
;
1313 *err
= cmsys::Status::POSIX_errno().GetString();
1315 return RenameResult::Failure
;
1319 void cmSystemTools::MoveFileIfDifferent(const std::string
& source
,
1320 const std::string
& destination
)
1322 if (FilesDiffer(source
, destination
)) {
1323 if (RenameFile(source
, destination
)) {
1326 CopyFileAlways(source
, destination
);
1331 void cmSystemTools::Glob(const std::string
& directory
,
1332 const std::string
& regexp
,
1333 std::vector
<std::string
>& files
)
1336 cmsys::RegularExpression
reg(regexp
.c_str());
1338 if (d
.Load(directory
)) {
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
);
1359 std::string startPath
= path
.substr(0, pos
);
1360 std::string finishPath
= path
.substr(pos
+ 2);
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
,
1382 if (glob
.back() != '*') {
1385 std::string path
= cmSystemTools::GetFilenamePath(glob
);
1386 std::string ppath
= cmSystemTools::GetFilenameName(glob
);
1387 ppath
= ppath
.substr(0, ppath
.size() - 1);
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() != '/') {
1402 fname
+= d
.GetFile(i
);
1403 std::string sfname
= d
.GetFile(i
);
1404 if (type
> 0 && cmSystemTools::FileIsDirectory(fname
)) {
1407 if (type
< 0 && !cmSystemTools::FileIsDirectory(fname
)) {
1410 if (cmHasPrefix(sfname
, ppath
)) {
1411 files
.push_back(fname
);
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
);
1428 return cmSystemTools::ConvertToUnixOutputPath(path
);
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
) {
1443 static_cast<void>(path
);
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
) {
1455 std::wstring wPath
= cmsys::Encoding::ToWide(path
);
1456 DWORD ret
= GetLongPathNameW(wPath
.c_str(), nullptr, 0);
1457 std::vector
<wchar_t> buffer(ret
);
1459 ret
= GetLongPathNameW(wPath
.c_str(), buffer
.data(),
1460 static_cast<DWORD
>(buffer
.size()));
1464 path
= cmsys::Encoding::ToNarrow(buffer
.data());
1467 static_cast<void>(path
);
1471 std::string
cmSystemTools::ConvertToRunCommandPath(const std::string
& path
)
1473 #if defined(_WIN32) && !defined(__CYGWIN__)
1474 return cmSystemTools::ConvertToWindowsOutputPath(path
);
1476 return cmSystemTools::ConvertToUnixOutputPath(path
);
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: " +
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
)) {
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
])) {
1527 // If no part of the path is in common then return the full path.
1532 // If the entire path is in common then just return a ".".
1533 if (common
== remote
.size() && common
== local
.size()) {
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()) {
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
) {
1553 if (i
< local
.size() - 1) {
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()) {
1568 relative
+= cmJoin(cmMakeRange(remote
).advance(common
), "/");
1570 // Finally return the path.
1574 std::string
cmSystemTools::RelativeIfUnder(std::string
const& top
,
1575 std::string
const& in
)
1580 } else if (cmSystemTools::IsSubDirectory(in
, top
)) {
1581 out
= in
.substr(top
.size() + 1);
1588 cm::optional
<std::string
> cmSystemTools::GetEnvVar(std::string
const& var
)
1590 cm::optional
<std::string
> result
;
1593 if (cmSystemTools::GetEnv(var
, value
)) {
1594 result
= std::move(value
);
1600 std::vector
<std::string
> cmSystemTools::SplitEnvPath(std::string
const& value
)
1602 #if defined(_WIN32) && !defined(__CYGWIN__)
1603 static cm::string_view sep
= ";"_s
;
1605 static cm::string_view sep
= ":"_s
;
1607 std::vector
<std::string
> paths
= cmTokenize(value
, sep
);
1608 for (std::string
& p
: paths
) {
1609 SystemTools::ConvertToUnixSlashes(p
);
1614 #ifndef CMAKE_BOOTSTRAP
1615 bool cmSystemTools::UnsetEnv(const char* value
)
1617 # if !defined(HAVE_UNSETENV)
1618 return cmSystemTools::UnPutEnv(value
);
1625 std::vector
<std::string
> cmSystemTools::GetEnvironmentVariables()
1627 std::vector
<std::string
> env
;
1630 // if program starts with main, _wenviron is initially NULL, call to
1631 // _wgetenv and create wide-character string environment
1633 for (cc
= 0; _wenviron
[cc
]; ++cc
) {
1634 env
.emplace_back(cmsys::Encoding::ToNarrow(_wenviron
[cc
]));
1637 for (cc
= 0; environ
[cc
]; ++cc
) {
1638 env
.emplace_back(environ
[cc
]);
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
) {
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);
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
];
1683 output
= *old_value
;
1685 const char* curval
= cmSystemTools::GetEnv(name
);
1691 diff
[name
] = output
;
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'));
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'));
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()) {
1723 } else if (op
== "set"_s
) {
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
) {
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()) {
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()) {
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
);
1761 cmSystemTools::Error(cmStrCat(
1762 "Error: Unrecognized environment manipulation argument: ", op
, '\n'));
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
);
1777 *measurement
<< env_update
<< std::endl
;
1780 cmSystemTools::UnsetEnv(env_apply
.first
.c_str());
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
);
1812 void cmSystemTools::EnableVSConsoleOutput()
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");
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
);
1862 std::string e
= cmStrCat("Cannot open output file \"", outFileName
,
1863 "\": ", cmSystemTools::GetLastSystemError());
1864 cmSystemTools::Error(e
);
1867 cmArchiveWrite::Compress compress
= cmArchiveWrite::CompressNone
;
1868 switch (compressType
) {
1869 case TarCompressGZip
:
1870 compress
= cmArchiveWrite::CompressGZip
;
1872 case TarCompressBZip2
:
1873 compress
= cmArchiveWrite::CompressBZip2
;
1876 compress
= cmArchiveWrite::CompressXZ
;
1878 case TarCompressZstd
:
1879 compress
= cmArchiveWrite::CompressZstd
;
1881 case TarCompressNone
:
1882 compress
= cmArchiveWrite::CompressNone
;
1886 cmArchiveWrite
a(fout
, compress
, format
.empty() ? "paxr" : format
,
1890 cmSystemTools::Error(a
.GetError());
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
);
1902 cmSystemTools::Error(a
.GetError());
1903 tarCreatedSuccessfully
= false;
1906 return tarCreatedSuccessfully
;
1915 #if !defined(CMAKE_BOOTSTRAP)
1917 # define BSDTAR_FILESIZE_PRINTF "%lu"
1918 # define BSDTAR_FILESIZE_TYPE unsigned long
1919 void list_item_verbose(FILE* out
, struct archive_entry
* entry
)
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
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
)));
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
);
1961 snprintf(tmp
, sizeof(tmp
), "%lu",
1962 static_cast<unsigned long>(archive_entry_gid(entry
)));
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
);
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"
1998 # define DAY_FMT "%e" /* Day number without leading zeros */
2000 if (tim
< now
- HALF_YEAR
|| tim
> now
+ HALF_YEAR
) {
2001 fmt
= DAY_FMT
" %b %Y";
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
));
2020 void ArchiveError(const char* m1
, struct archive
* a
)
2022 std::string
message(m1
);
2023 const char* m2
= archive_error_string(a
);
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
) {
2038 if (r
>= ARCHIVE_WARN
) {
2039 const char* warn
= archive_error_string(ar
);
2041 warn
= "unknown warning";
2043 std::cerr
<< "cmake -E tar: warning: " << warn
<< '\n';
2048 const char* err
= archive_error_string(ar
);
2050 err
= "unknown error";
2052 std::cerr
<< "cmake -E tar: error: " << err
<< '\n';
2056 // Return 'true' on success
2057 bool copy_data(struct archive
* ar
, struct archive
* aw
)
2062 # if defined(ARCHIVE_VERSION_NUMBER) && ARCHIVE_VERSION_NUMBER >= 3000000
2063 __LA_INT64_T offset
;
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
) {
2074 if (!la_diagnostic(ar
, r
)) {
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
)) {
2083 # if !defined(__clang__) && !defined(__NVCOMPILER) && !defined(__HP_aCC)
2084 return false; /* this should not happen but it quiets some compilers */
2088 bool extract_tar(const std::string
& outFileName
,
2089 const std::vector
<std::string
>& files
, bool verbose
,
2090 cmSystemTools::cmTarExtractTimestamps extractTimestamps
,
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");
2107 for (const auto& filename
: files
) {
2108 if (archive_match_include_pattern(matching
, filename
.c_str()) !=
2110 cmSystemTools::Error("Failed to add to inclusion list: " + filename
);
2115 int r
= cm_archive_read_open_file(a
, outFileName
.c_str(), 10240);
2117 ArchiveError("Problem with archive_read_open_file(): ", a
);
2118 archive_write_free(ext
);
2119 archive_read_close(a
);
2123 r
= archive_read_next_header(a
, &entry
);
2124 if (r
== ARCHIVE_EOF
) {
2127 if (r
!= ARCHIVE_OK
) {
2128 ArchiveError("Problem with archive_read_next_header(): ", a
);
2132 if (archive_match_excluded(matching
, entry
)) {
2138 cmSystemTools::Stdout("x ");
2139 cmSystemTools::Stdout(cm_archive_entry_pathname(entry
));
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");
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
);
2157 r
= archive_write_header(ext
, entry
);
2158 if (r
== ARCHIVE_OK
) {
2159 if (!copy_data(a
, ext
)) {
2162 r
= archive_write_finish_entry(ext
);
2163 if (r
!= ARCHIVE_OK
) {
2164 ArchiveError("Problem with archive_write_finish_entry(): ", ext
);
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
;
2176 ArchiveError("Problem with archive_write_header(): ", ext
);
2177 cmSystemTools::Error("Current file: " +
2178 cm_archive_entry_pathname(entry
));
2184 bool error_occured
= false;
2185 if (matching
!= nullptr) {
2189 while ((ar
= archive_match_path_unmatched_inclusions_next(matching
, &p
)) ==
2191 cmSystemTools::Error("tar: " + std::string(p
) +
2192 ": Not found in archive");
2193 error_occured
= true;
2195 if (error_occured
) {
2198 if (ar
== ARCHIVE_FATAL
) {
2199 cmSystemTools::Error("tar: Out of memory");
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
;
2212 bool cmSystemTools::ExtractTar(const std::string
& outFileName
,
2213 const std::vector
<std::string
>& files
,
2214 cmTarExtractTimestamps extractTimestamps
,
2217 #if !defined(CMAKE_BOOTSTRAP)
2218 return extract_tar(outFileName
, files
, verbose
, extractTimestamps
, true);
2222 (void)extractTimestamps
;
2228 bool cmSystemTools::ListTar(const std::string
& outFileName
,
2229 const std::vector
<std::string
>& files
,
2232 #if !defined(CMAKE_BOOTSTRAP)
2233 return extract_tar(outFileName
, files
, verbose
, cmTarExtractTimestamps::Yes
,
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
)
2249 auto outiter
= out
.begin();
2250 auto erriter
= err
.begin();
2251 cmProcessOutput processOutput
;
2252 std::string strdata
;
2254 // Check for a newline in stdout.
2255 for (; outiter
!= out
.end(); ++outiter
) {
2256 if ((*outiter
== '\r') && ((outiter
+ 1) == out
.end())) {
2259 if (*outiter
== '\n' || *outiter
== '\0') {
2260 std::vector
<char>::size_type length
= outiter
- out
.begin();
2261 if (length
> 1 && *(outiter
- 1) == '\r') {
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())) {
2277 if (*erriter
== '\n' || *erriter
== '\0') {
2278 std::vector
<char>::size_type length
= erriter
- err
.begin();
2279 if (length
> 1 && *(erriter
- 1) == '\r') {
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.
2293 uv_stream_t
* Stream
;
2294 std::vector
<char> Buffer
;
2296 bool Finished
= false;
2299 [](uv_stream_t
* stream
,
2300 ReadData
& data
) -> std::unique_ptr
<cmUVStreamReadHandle
> {
2301 data
.Stream
= stream
;
2302 return cmUVStreamRead(
2304 [&data
](std::vector
<char> buf
) {
2305 data
.Buffer
= std::move(buf
);
2307 uv_read_stop(data
.Stream
);
2309 [&data
]() { data
.Finished
= true; });
2312 auto outHandle
= startRead(outPipe
, outData
);
2314 auto errHandle
= startRead(errPipe
, errData
);
2316 cm::uv_timer_ptr timer
;
2317 bool timedOut
= false;
2318 timer
.init(*loop
, &timedOut
);
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
);
2328 // Timeout has been exceeded.
2329 return WaitForLineResult::Timeout
;
2332 processOutput
.DecodeText(outData
.Buffer
.data(), outData
.Buffer
.size(),
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(),
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
;
2360 line
.append(out
.data(), outiter
- out
.begin());
2361 out
.erase(out
.begin(), out
.end());
2362 return WaitForLineResult::STDOUT
;
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
);
2381 static void EnsureStdPipe(int stdFd
, DWORD nStdHandle
, FILE* stream
,
2382 const wchar_t* mode
)
2384 if (fileno(stream
) >= 0) {
2388 _wfreopen(L
"NUL", mode
, stream
);
2389 int fd
= fileno(stream
);
2391 perror("failed to open NUL for missing stdio pipe");
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");
2407 static void EnsureStdPipe(int fd
)
2409 if (fcntl(fd
, F_GETFD
) != -1 || errno
!= EBADF
) {
2413 int f
= open("/dev/null", fd
== STDIN_FILENO
? O_RDONLY
: O_WRONLY
);
2415 perror("failed to open /dev/null for missing stdio pipe");
2424 void cmSystemTools::EnsureStdPipes()
2426 EnsureStdPipe(STDIN_FILENO
);
2427 EnsureStdPipe(STDOUT_FILENO
);
2428 EnsureStdPipe(STDERR_FILENO
);
2433 # ifndef CRYPT_SILENT
2434 # define CRYPT_SILENT 0x40 /* Not defined by VS 6 version of header. */
2436 static int WinCryptRandom(void* data
, size_t size
)
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);
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
))) {
2459 // Fall back to the time and pid.
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
;
2469 unsigned int integer
;
2470 char bytes
[sizeof(unsigned int)];
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.
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
;
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()) {
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
;
2523 uint32_t exe_path_size
= CM_EXE_PATH_LOCAL_SIZE
;
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
);
2532 cmSystemTools::GetFilenamePath(cmSystemTools::GetRealPath(exe_path
));
2533 if (exe_path
!= exe_path_local
) {
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
;
2544 exe_dir
= cmSystemTools::GetFilenamePath(exe_dir
);
2545 exe_dir
= cmSystemTools::GetFilenamePath(exe_dir
);
2549 std::string errorMsg
;
2551 if (cmSystemTools::FindProgramPath(argv0
, exe
, errorMsg
)) {
2553 exe
= cmSystemTools::GetRealPath(exe
);
2554 exe_dir
= cmSystemTools::GetFilenamePath(exe
);
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";
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
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
;
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");
2629 // Bootstrap build knows its source.
2630 cmSystemToolsCMakeRoot
= CMAKE_BOOTSTRAP_SOURCE_DIR
;
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
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.
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
);
2702 // Color is disabled. Print without color.
2703 fprintf(stdout
, "%s", message
);
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
2716 cmELF
elf(fullPath
.c_str());
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
)) {
2725 if (!cmSystemTools::ReadSymlink(fullPath
, soname
)) {
2729 // If the symlink has a path component we have no guess for the soname.
2730 if (!cmSystemTools::GetFilenamePath(soname
).empty()) {
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());
2747 return macho
.GetInstallName(soname
);
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] != ':') {
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
] != ':') {
2781 // Return the position of the path portion.
2785 // The desired rpath was not found.
2786 return std::string::npos
;
2790 struct cmSystemToolsRPathInfo
2792 unsigned long Position
;
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
)
2811 bool remove_rpath
= true;
2812 cmSystemToolsRPathInfo rp
[2];
2814 // Parse the ELF binary.
2815 cmELF
elf(file
.c_str());
2817 return cm::nullopt
; // Not a valid ELF file.
2820 // Get the RPATH and RUNPATH entries from it.
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";
2829 if (cmELF::StringEntry
const* se_runpath
= elf
.GetRunPath()) {
2830 se
[se_count
] = se_runpath
;
2831 se_name
[se_count
] = "RUNPATH";
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
) {
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
)) {
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) {
2865 *emsg
= cmStrCat("The replacement path is too long for the ",
2866 se_name
[i
], " entry.");
2871 // This entry is ready for update.
2872 rp
[rp_count
].Value
= std::move(*outRPath
);
2875 remove_rpath
= false;
2880 // If no runtime path needs to be changed, we are done.
2881 if (rp_count
== 0) {
2885 // If the resulting rpath is empty, just remove the entire entry instead.
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
);
2896 *emsg
= "Error opening file for update.";
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
)) {
2906 *emsg
= cmStrCat("Error seeking to ", rp
[i
].Name
, " position.");
2911 // Write the new rpath. Follow it with enough null terminators to
2912 // fill the string table entry.
2914 for (unsigned long j
= rp
[i
].Value
.length(); j
< rp
[i
].Size
; ++j
) {
2918 // Make sure it wrote correctly.
2921 *emsg
= cmStrCat("Error writing the new ", rp
[i
].Name
,
2922 " string to the file.");
2929 // Everything was updated successfully.
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
2947 cmStrCat("No valid ELF RPATH or RUNPATH entry exists in the file; ",
2948 elf
.GetErrorMessage());
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
) {
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 */
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()) {
2995 // Construct the new value which preserves the part of the path
2996 // not being changed.
2998 if (!removeEnvironmentRPath
) {
2999 *outRPath
+= inRPath
.substr(0, prefix_len
);
3001 *outRPath
+= newRPath
;
3002 *outRPath
+= inRPath
.substr(pos
+ oldRPath
.length());
3007 return AdjustRPathELF(file
, MakeEmptyCallback(newRPath
), adjustCallback
,
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
*
3019 if (inRPath
!= newRPath
) {
3020 outRPath
= newRPath
;
3025 return AdjustRPathELF(file
, MakeEmptyCallback(newRPath
), adjustCallback
,
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
)
3037 #if !defined(CMake_USE_XCOFF_PARSER)
3041 (void)removeEnvironmentRPath
;
3046 cmXCOFF
xcoff(file
.c_str(), cmXCOFF::Mode::ReadWrite
);
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
) {
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 */
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
);
3095 *emsg
= xcoff
.GetErrorMessage();
3100 // Everything was updated successfully.
3108 static cm::optional
<bool> SetRPathXCOFF(std::string
const& /*file*/,
3109 std::string
const& /*newRPath*/,
3110 std::string
* /*emsg*/,
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
,
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.
3136 *emsg
= "The file format is not recognized.";
3141 bool cmSystemTools::SetRPath(std::string
const& file
,
3142 std::string
const& newRPath
, std::string
* emsg
,
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.
3158 *emsg
= "The file format is not recognized.";
3164 bool VersionCompare(cmSystemTools::CompareOp op
, const char* lhss
,
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') {
3177 while (*endr
== '0') {
3181 const char* beginl
= endl
;
3182 const char* beginr
= endr
;
3184 // count significant digits
3185 while ((*endl
>= '0') && (*endl
<= '9')) {
3188 while ((*endr
>= '0') && (*endr
<= '9')) {
3192 // compare number of digits first
3193 ptrdiff_t r
= ((endl
- beginl
) - (endr
- beginr
));
3195 // compare the digits if number of digits is equal
3196 r
= strncmp(beginl
, beginr
, endl
- beginl
);
3200 // lhs < rhs, so true if operation is LESS
3201 return (op
& cmSystemTools::OP_LESS
) != 0;
3204 // lhs > rhs, so true if operation is GREATER
3205 return (op
& cmSystemTools::OP_GREATER
) != 0;
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
,
3252 static size_t cm_strverscmp_find_first_difference_or_end(const char* lhs
,
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) {
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])) {
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
])) {
3282 static size_t cm_strverscmp_count_leading_zeros(const char* s
, size_t b
)
3285 /* Step forward over zeros that are followed by another digit. */
3286 while (s
[i
] == '0' && isdigit(s
[i
+ 1])) {
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
)
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());
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.
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.
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.
3371 *emsg
= "DYNAMIC section contains a DT_NULL before the end.";
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
);
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
;
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
);
3427 *emsg
= "Error opening file for update.";
3432 // Write the new DYNAMIC table header.
3433 if (!f
.seekp(bytesBegin
)) {
3435 *emsg
= "Error seeking to DYNAMIC table header for RPATH.";
3439 if (!f
.write(bytes
.data(), bytes
.size())) {
3441 *emsg
= "Error replacing DYNAMIC table header.";
3446 // Fill the RPATH and RUNPATH strings with zero bytes.
3447 for (int i
= 0; i
< zeroCount
; ++i
) {
3448 if (!f
.seekp(zeroPosition
[i
])) {
3450 *emsg
= "Error seeking to RPATH position.";
3454 for (unsigned long j
= 0; j
< zeroSize
[i
]; ++j
) {
3459 *emsg
= "Error writing the empty rpath string to the file.";
3465 // Everything was updated successfully.
3472 static cm::optional
<bool> RemoveRPathXCOFF(std::string
const& file
,
3473 std::string
* emsg
, bool* removed
)
3478 #if !defined(CMake_USE_XCOFF_PARSER)
3481 return cm::nullopt
; // Cannot handle XCOFF files.
3483 cmXCOFF
xcoff(file
.c_str(), cmXCOFF::Mode::ReadWrite
);
3485 return cm::nullopt
; // Not a valid XCOFF file.
3487 bool rm
= xcoff
.RemoveLibPath();
3490 *emsg
= xcoff
.GetErrorMessage();
3501 bool cmSystemTools::RemoveRPath(std::string
const& file
, std::string
* emsg
,
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.
3514 bool cmSystemTools::CheckRPath(std::string
const& file
,
3515 std::string
const& newRPath
)
3517 // Parse the ELF binary.
3518 cmELF
elf(file
.c_str());
3520 // Get the RPATH or RUNPATH entry from it.
3521 cmELF::StringEntry
const* se
= elf
.GetRPath();
3523 se
= elf
.GetRunPath();
3526 // Make sure the current rpath contains the new rpath.
3527 if (newRPath
.empty()) {
3533 cmSystemToolsFindRPath(se
->Value
, newRPath
) != std::string::npos
) {
3539 #if defined(CMake_USE_XCOFF_PARSER)
3540 // Parse the XCOFF binary.
3541 cmXCOFF
xcoff(file
.c_str());
3543 if (cm::optional
<cm::string_view
> libPath
= xcoff
.GetLibPath()) {
3544 if (cmSystemToolsFindRPath(*libPath
, newRPath
) != std::string::npos
) {
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
)
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
)) {
3566 cmSystemTools::Delay(retry
.Delay
);
3570 return static_cast<bool>(cmSystemTools::RemoveADirectory(dir
));
3574 std::string
cmSystemTools::EncodeURL(std::string
const& in
, bool escapeSlashes
)
3578 char hexCh
[4] = { 0, 0, 0, 0 };
3588 snprintf(hexCh
, sizeof(hexCh
), "%%%02X", static_cast<int>(c
));
3591 if (escapeSlashes
) {
3592 strcpy(hexCh
, "%2F");
3603 cmsys::Status
cmSystemTools::CreateSymlink(std::string
const& origName
,
3604 std::string
const& newName
)
3606 cmsys::Status status
=
3607 cmSystemTools::CreateSymlinkQuietly(origName
, newName
);
3609 cmSystemTools::Error(cmStrCat("failed to create symbolic link '", newName
,
3610 "': ", status
.GetString()));
3615 cmsys::Status
cmSystemTools::CreateSymlinkQuietly(std::string
const& origName
,
3616 std::string
const& newName
)
3621 if (cmsys::SystemTools::FileIsDirectory(origName
)) {
3622 flags
|= UV_FS_SYMLINK_DIR
;
3625 int err
= uv_fs_symlink(nullptr, &req
, origName
.c_str(), newName
.c_str(),
3627 cmsys::Status status
;
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
));
3634 status
= cmsys::Status::POSIX(-err
);
3640 cmsys::Status
cmSystemTools::CreateLink(std::string
const& origName
,
3641 std::string
const& newName
)
3643 cmsys::Status status
= cmSystemTools::CreateLinkQuietly(origName
, newName
);
3645 cmSystemTools::Error(
3646 cmStrCat("failed to create link '", newName
, "': ", status
.GetString()));
3651 cmsys::Status
cmSystemTools::CreateLinkQuietly(std::string
const& origName
,
3652 std::string
const& newName
)
3656 uv_fs_link(nullptr, &req
, origName
.c_str(), newName
.c_str(), nullptr);
3657 cmsys::Status status
;
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
));
3664 status
= cmsys::Status::POSIX(-err
);
3670 cm::string_view
cmSystemTools::GetSystemName()
3674 #elif defined(__MSYS__)
3676 #elif defined(__CYGWIN__)
3678 #elif defined(__ANDROID__)
3681 static struct utsname uts_name
;
3682 static bool initialized
= false;
3683 static cm::string_view systemName
;
3687 if (uname(&uts_name
) >= 0) {
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";
3712 char cmSystemTools::GetSystemPathlistSeparator()