summaryrefslogtreecommitdiff
path: root/Source/cmSystemTools.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmSystemTools.cxx')
-rw-r--r--Source/cmSystemTools.cxx447
1 files changed, 351 insertions, 96 deletions
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index c1ce7ec98..10d2e50a4 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -14,11 +14,13 @@
#include "cmSystemTools.h"
+#include <cm/optional>
#include <cmext/algorithm>
#include <cm3p/uv.h>
#include "cmDuration.h"
+#include "cmMessageMetadata.h"
#include "cmProcessOutput.h"
#include "cmRange.h"
#include "cmStringAlgorithms.h"
@@ -64,6 +66,7 @@
#include <cstdlib>
#include <cstring>
#include <ctime>
+#include <functional>
#include <iostream>
#include <sstream>
#include <utility>
@@ -86,6 +89,7 @@
# include <unistd.h>
# include <sys/time.h>
+# include <sys/types.h>
#endif
#if defined(_WIN32) && \
@@ -101,6 +105,10 @@
# include <malloc.h> /* for malloc/free on QNX */
#endif
+#if !defined(_WIN32) && !defined(__ANDROID__)
+# include <sys/utsname.h>
+#endif
+
namespace {
cmSystemTools::InterruptCallback s_InterruptCallback;
@@ -258,8 +266,15 @@ void cmSystemTools::Stdout(const std::string& s)
void cmSystemTools::Message(const std::string& m, const char* title)
{
+ cmMessageMetadata md;
+ md.title = title;
+ Message(m, md);
+}
+
+void cmSystemTools::Message(const std::string& m, const cmMessageMetadata& md)
+{
if (s_MessageCallback) {
- s_MessageCallback(m, title);
+ s_MessageCallback(m, md);
} else {
std::cerr << m << std::endl;
}
@@ -951,21 +966,87 @@ void cmSystemTools::InitializeLibUV()
#ifdef _WIN32
namespace {
-bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname)
+bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname,
+ cmSystemTools::Replace replace)
{
// Not only ignore any previous error, but clear any memory of it.
SetLastError(0);
- // Use MOVEFILE_REPLACE_EXISTING to replace an existing destination file.
- return MoveFileExW(oldname.c_str(), newname.c_str(),
- MOVEFILE_REPLACE_EXISTING);
+ DWORD flags = 0;
+ if (replace == cmSystemTools::Replace::Yes) {
+ // Use MOVEFILE_REPLACE_EXISTING to replace an existing destination file.
+ flags = flags | MOVEFILE_REPLACE_EXISTING;
+ }
+
+ return MoveFileExW(oldname.c_str(), newname.c_str(), flags);
}
}
#endif
+bool cmSystemTools::CopySingleFile(const std::string& oldname,
+ const std::string& newname)
+{
+ return cmSystemTools::CopySingleFile(oldname, newname, CopyWhen::Always) ==
+ CopyResult::Success;
+}
+
+cmSystemTools::CopyResult cmSystemTools::CopySingleFile(
+ std::string const& oldname, std::string const& newname, CopyWhen when,
+ std::string* err)
+{
+ switch (when) {
+ case CopyWhen::Always:
+ break;
+ case CopyWhen::OnlyIfDifferent:
+ if (!FilesDiffer(oldname, newname)) {
+ return CopyResult::Success;
+ }
+ break;
+ }
+
+ mode_t perm = 0;
+ cmsys::Status perms = SystemTools::GetPermissions(oldname, perm);
+
+ // If files are the same do not copy
+ if (SystemTools::SameFile(oldname, newname)) {
+ return CopyResult::Success;
+ }
+
+ cmsys::Status status;
+ status = cmsys::SystemTools::CloneFileContent(oldname, newname);
+ if (!status) {
+ // if cloning did not succeed, fall back to blockwise copy
+ status = cmsys::SystemTools::CopyFileContentBlockwise(oldname, newname);
+ }
+ if (!status) {
+ if (err) {
+ *err = status.GetString();
+ }
+ return CopyResult::Failure;
+ }
+ if (perms) {
+ status = SystemTools::SetPermissions(newname, perm);
+ if (!status) {
+ if (err) {
+ *err = status.GetString();
+ }
+ return CopyResult::Failure;
+ }
+ }
+ return CopyResult::Success;
+}
+
bool cmSystemTools::RenameFile(const std::string& oldname,
const std::string& newname)
{
+ return cmSystemTools::RenameFile(oldname, newname, Replace::Yes) ==
+ RenameResult::Success;
+}
+
+cmSystemTools::RenameResult cmSystemTools::RenameFile(
+ std::string const& oldname, std::string const& newname, Replace replace,
+ std::string* err)
+{
#ifdef _WIN32
# ifndef INVALID_FILE_ATTRIBUTES
# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
@@ -987,7 +1068,7 @@ bool cmSystemTools::RenameFile(const std::string& oldname,
oldname_wstr, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
DWORD move_last_error = 0;
- while (!cmMoveFile(oldname_wstr, newname_wstr) && --retry.Count) {
+ while (!cmMoveFile(oldname_wstr, newname_wstr, replace) && --retry.Count) {
move_last_error = GetLastError();
// There was no error ==> the operation is not yet complete.
@@ -1003,7 +1084,13 @@ bool cmSystemTools::RenameFile(const std::string& oldname,
// 3) Windows Explorer has an associated directory already opened.
if (move_last_error != ERROR_ACCESS_DENIED &&
move_last_error != ERROR_SHARING_VIOLATION) {
- return false;
+ if (replace == Replace::No && move_last_error == ERROR_ALREADY_EXISTS) {
+ return RenameResult::NoReplace;
+ }
+ if (err) {
+ *err = cmsys::Status::Windows(move_last_error).GetString();
+ }
+ return RenameResult::Failure;
}
DWORD const attrs = GetFileAttributesW(newname_wstr.c_str());
@@ -1027,10 +1114,37 @@ bool cmSystemTools::RenameFile(const std::string& oldname,
save_restore_file_attributes.SetPath(newname_wstr);
}
SetLastError(move_last_error);
- return retry.Count > 0;
+ if (retry.Count > 0) {
+ return RenameResult::Success;
+ }
+ if (replace == Replace::No && GetLastError() == ERROR_ALREADY_EXISTS) {
+ return RenameResult::NoReplace;
+ }
+ if (err) {
+ *err = cmsys::Status::Windows_GetLastError().GetString();
+ }
+ return RenameResult::Failure;
#else
- /* On UNIX we have an OS-provided call to do this atomically. */
- return rename(oldname.c_str(), newname.c_str()) == 0;
+ // On UNIX we have OS-provided calls to create 'newname' atomically.
+ if (replace == Replace::No) {
+ if (link(oldname.c_str(), newname.c_str()) == 0) {
+ return RenameResult::Success;
+ }
+ if (errno == EEXIST) {
+ return RenameResult::NoReplace;
+ }
+ if (err) {
+ *err = cmsys::Status::POSIX_errno().GetString();
+ }
+ return RenameResult::Failure;
+ }
+ if (rename(oldname.c_str(), newname.c_str()) == 0) {
+ return RenameResult::Success;
+ }
+ if (err) {
+ *err = cmsys::Status::POSIX_errno().GetString();
+ }
+ return RenameResult::Failure;
#endif
}
@@ -1387,6 +1501,20 @@ std::string cmSystemTools::ForceToRelativePath(std::string const& local_path,
return relative;
}
+std::string cmSystemTools::RelativeIfUnder(std::string const& top,
+ std::string const& in)
+{
+ std::string out;
+ if (in == top) {
+ out = ".";
+ } else if (cmSystemTools::IsSubDirectory(in, top)) {
+ out = in.substr(top.size() + 1);
+ } else {
+ out = in;
+ }
+ return out;
+}
+
#ifndef CMAKE_BOOTSTRAP
bool cmSystemTools::UnsetEnv(const char* value)
{
@@ -1693,7 +1821,7 @@ bool copy_data(struct archive* ar, struct archive* aw)
return false;
}
}
-# if !defined(__clang__) && !defined(__HP_aCC)
+# if !defined(__clang__) && !defined(__NVCOMPILER) && !defined(__HP_aCC)
return false; /* this should not happen but it quiets some compilers */
# endif
}
@@ -2398,6 +2526,7 @@ std::string::size_type cmSystemToolsFindRPath(cm::string_view const& have,
#endif
#if defined(CMake_USE_ELF_PARSER)
+namespace {
struct cmSystemToolsRPathInfo
{
unsigned long Position;
@@ -2405,15 +2534,15 @@ struct cmSystemToolsRPathInfo
std::string Name;
std::string Value;
};
-#endif
+
+using EmptyCallback = std::function<bool(std::string*, const cmELF&)>;
+using AdjustCallback = std::function<bool(
+ cm::optional<std::string>&, const std::string&, const char*, std::string*)>;
// FIXME: Dispatch if multiple formats are supported.
-#if defined(CMake_USE_ELF_PARSER)
-bool cmSystemTools::ChangeRPath(std::string const& file,
- std::string const& oldRPath,
- std::string const& newRPath,
- bool removeEnvironmentRPath, std::string* emsg,
- bool* changed)
+bool AdjustRPath(std::string const& file, const EmptyCallback& emptyCallback,
+ const AdjustCallback& adjustCallback, std::string* emsg,
+ bool* changed)
{
if (changed) {
*changed = false;
@@ -2440,17 +2569,7 @@ bool cmSystemTools::ChangeRPath(std::string const& file,
++se_count;
}
if (se_count == 0) {
- if (newRPath.empty()) {
- // The new rpath is empty and there is no rpath anyway so it is
- // okay.
- return true;
- }
- if (emsg) {
- *emsg =
- cmStrCat("No valid ELF RPATH or RUNPATH entry exists in the file; ",
- elf.GetErrorMessage());
- }
- return false;
+ return emptyCallback(emsg, elf);
}
for (int i = 0; i < se_count; ++i) {
@@ -2460,68 +2579,38 @@ bool cmSystemTools::ChangeRPath(std::string const& file,
continue;
}
- // Make sure the current rpath contains the old rpath.
- std::string::size_type pos =
- cmSystemToolsFindRPath(se[i]->Value, oldRPath);
- if (pos == std::string::npos) {
- // If it contains the new rpath instead then it is okay.
- if (cmSystemToolsFindRPath(se[i]->Value, newRPath) !=
- std::string::npos) {
- remove_rpath = false;
- continue;
- }
- if (emsg) {
- std::ostringstream e;
- /* clang-format off */
- e << "The current " << se_name[i] << " is:\n"
- << " " << se[i]->Value << "\n"
- << "which does not contain:\n"
- << " " << oldRPath << "\n"
- << "as was expected.";
- /* clang-format on */
- *emsg = e.str();
- }
- return false;
- }
-
// Store information about the entry in the file.
rp[rp_count].Position = se[i]->Position;
rp[rp_count].Size = se[i]->Size;
rp[rp_count].Name = se_name[i];
- std::string::size_type prefix_len = pos;
-
- // If oldRPath was at the end of the file's RPath, and newRPath is empty,
- // we should remove the unnecessary ':' at the end.
- if (newRPath.empty() && pos > 0 && se[i]->Value[pos - 1] == ':' &&
- pos + oldRPath.length() == se[i]->Value.length()) {
- prefix_len--;
- }
-
- // Construct the new value which preserves the part of the path
- // not being changed.
- if (!removeEnvironmentRPath) {
- rp[rp_count].Value = se[i]->Value.substr(0, prefix_len);
+ // Adjust the rpath.
+ cm::optional<std::string> outRPath;
+ if (!adjustCallback(outRPath, se[i]->Value, se_name[i], emsg)) {
+ return false;
}
- rp[rp_count].Value += newRPath;
- rp[rp_count].Value += se[i]->Value.substr(pos + oldRPath.length());
- if (!rp[rp_count].Value.empty()) {
- remove_rpath = false;
- }
+ if (outRPath) {
+ if (!outRPath->empty()) {
+ remove_rpath = false;
+ }
- // Make sure there is enough room to store the new rpath and at
- // least one null terminator.
- if (rp[rp_count].Size < rp[rp_count].Value.length() + 1) {
- if (emsg) {
- *emsg = cmStrCat("The replacement path is too long for the ",
- se_name[i], " entry.");
+ // Make sure there is enough room to store the new rpath and at
+ // least one null terminator.
+ if (rp[rp_count].Size < outRPath->length() + 1) {
+ if (emsg) {
+ *emsg = cmStrCat("The replacement path is too long for the ",
+ se_name[i], " entry.");
+ }
+ return false;
}
- return false;
- }
- // This entry is ready for update.
- ++rp_count;
+ // This entry is ready for update.
+ rp[rp_count].Value = std::move(*outRPath);
+ ++rp_count;
+ } else {
+ remove_rpath = false;
+ }
}
}
@@ -2580,6 +2669,99 @@ bool cmSystemTools::ChangeRPath(std::string const& file,
}
return true;
}
+
+std::function<bool(std::string*, const cmELF&)> MakeEmptyCallback(
+ const std::string& newRPath)
+{
+ return [newRPath](std::string* emsg, const cmELF& elf) -> bool {
+ if (newRPath.empty()) {
+ // The new rpath is empty and there is no rpath anyway so it is
+ // okay.
+ return true;
+ }
+ if (emsg) {
+ *emsg =
+ cmStrCat("No valid ELF RPATH or RUNPATH entry exists in the file; ",
+ elf.GetErrorMessage());
+ }
+ return false;
+ };
+};
+}
+
+bool cmSystemTools::ChangeRPath(std::string const& file,
+ std::string const& oldRPath,
+ std::string const& newRPath,
+ bool removeEnvironmentRPath, std::string* emsg,
+ bool* changed)
+{
+ auto adjustCallback = [oldRPath, newRPath, removeEnvironmentRPath](
+ cm::optional<std::string>& outRPath,
+ const std::string& inRPath, const char* se_name,
+ std::string* emsg2) -> bool {
+ // Make sure the current rpath contains the old rpath.
+ std::string::size_type pos = cmSystemToolsFindRPath(inRPath, oldRPath);
+ if (pos == std::string::npos) {
+ // If it contains the new rpath instead then it is okay.
+ if (cmSystemToolsFindRPath(inRPath, newRPath) != std::string::npos) {
+ return true;
+ }
+ if (emsg2) {
+ std::ostringstream e;
+ /* clang-format off */
+ e << "The current " << se_name << " is:\n"
+ << " " << inRPath << "\n"
+ << "which does not contain:\n"
+ << " " << oldRPath << "\n"
+ << "as was expected.";
+ /* clang-format on */
+ *emsg2 = e.str();
+ }
+ return false;
+ }
+
+ std::string::size_type prefix_len = pos;
+
+ // If oldRPath was at the end of the file's RPath, and newRPath is empty,
+ // we should remove the unnecessary ':' at the end.
+ if (newRPath.empty() && pos > 0 && inRPath[pos - 1] == ':' &&
+ pos + oldRPath.length() == inRPath.length()) {
+ prefix_len--;
+ }
+
+ // Construct the new value which preserves the part of the path
+ // not being changed.
+ outRPath.emplace();
+ if (!removeEnvironmentRPath) {
+ *outRPath += inRPath.substr(0, prefix_len);
+ }
+ *outRPath += newRPath;
+ *outRPath += inRPath.substr(pos + oldRPath.length());
+
+ return true;
+ };
+
+ return AdjustRPath(file, MakeEmptyCallback(newRPath), adjustCallback, emsg,
+ changed);
+}
+
+bool cmSystemTools::SetRPath(std::string const& file,
+ std::string const& newRPath, std::string* emsg,
+ bool* changed)
+{
+ auto adjustCallback = [newRPath](cm::optional<std::string>& outRPath,
+ const std::string& inRPath,
+ const char* /*se_name*/, std::string *
+ /*emsg*/) -> bool {
+ if (inRPath != newRPath) {
+ outRPath = newRPath;
+ }
+ return true;
+ };
+
+ return AdjustRPath(file, MakeEmptyCallback(newRPath), adjustCallback, emsg,
+ changed);
+}
#elif defined(CMake_USE_XCOFF_PARSER)
bool cmSystemTools::ChangeRPath(std::string const& file,
std::string const& oldRPath,
@@ -2649,6 +2831,13 @@ bool cmSystemTools::ChangeRPath(std::string const& file,
}
return true;
}
+
+bool cmSystemTools::SetRPath(std::string const& /*file*/,
+ std::string const& /*newRPath*/,
+ std::string* /*emsg*/, bool* /*changed*/)
+{
+ return false;
+}
#else
bool cmSystemTools::ChangeRPath(std::string const& /*file*/,
std::string const& /*oldRPath*/,
@@ -2658,6 +2847,13 @@ bool cmSystemTools::ChangeRPath(std::string const& /*file*/,
{
return false;
}
+
+bool cmSystemTools::SetRPath(std::string const& /*file*/,
+ std::string const& /*newRPath*/,
+ std::string* /*emsg*/, bool* /*changed*/)
+{
+ return false;
+}
#endif
bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
@@ -3021,7 +3217,7 @@ bool cmSystemTools::RepeatedRemoveDirectory(const std::string& dir)
}
return false;
#else
- return cmSystemTools::RemoveADirectory(dir);
+ return static_cast<bool>(cmSystemTools::RemoveADirectory(dir));
#endif
}
@@ -3054,9 +3250,9 @@ std::string cmSystemTools::EncodeURL(std::string const& in, bool escapeSlashes)
return out;
}
-bool cmSystemTools::CreateSymlink(const std::string& origName,
- const std::string& newName,
- std::string* errorMessage)
+cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName,
+ std::string const& newName,
+ std::string* errorMessage)
{
uv_fs_t req;
int flags = 0;
@@ -3067,37 +3263,96 @@ bool cmSystemTools::CreateSymlink(const std::string& origName,
#endif
int err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(),
flags, nullptr);
+ cmsys::Status status;
if (err) {
- std::string e =
- "failed to create symbolic link '" + newName + "': " + uv_strerror(err);
+#if defined(_WIN32)
+ status = cmsys::Status::Windows(uv_fs_get_system_error(&req));
+#elif UV_VERSION_MAJOR > 1 || (UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR >= 38)
+ status = cmsys::Status::POSIX(uv_fs_get_system_error(&req));
+#else
+ status = cmsys::Status::POSIX(-err);
+#endif
+ std::string e = cmStrCat("failed to create symbolic link '", newName,
+ "': ", status.GetString());
if (errorMessage) {
*errorMessage = std::move(e);
} else {
cmSystemTools::Error(e);
}
- return false;
}
-
- return true;
+ return status;
}
-bool cmSystemTools::CreateLink(const std::string& origName,
- const std::string& newName,
- std::string* errorMessage)
+cmsys::Status cmSystemTools::CreateLink(std::string const& origName,
+ std::string const& newName,
+ std::string* errorMessage)
{
uv_fs_t req;
int err =
uv_fs_link(nullptr, &req, origName.c_str(), newName.c_str(), nullptr);
+ cmsys::Status status;
if (err) {
+#if defined(_WIN32)
+ status = cmsys::Status::Windows(uv_fs_get_system_error(&req));
+#elif UV_VERSION_MAJOR > 1 || (UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR >= 38)
+ status = cmsys::Status::POSIX(uv_fs_get_system_error(&req));
+#else
+ status = cmsys::Status::POSIX(-err);
+#endif
std::string e =
- "failed to create link '" + newName + "': " + uv_strerror(err);
+ cmStrCat("failed to create link '", newName, "': ", status.GetString());
if (errorMessage) {
*errorMessage = std::move(e);
} else {
cmSystemTools::Error(e);
}
- return false;
}
+ return status;
+}
- return true;
+cm::string_view cmSystemTools::GetSystemName()
+{
+#if defined(_WIN32)
+ return "Windows";
+#elif defined(__ANDROID__)
+ return "Android";
+#else
+ static struct utsname uts_name;
+ static bool initialized = false;
+ static cm::string_view systemName;
+ if (initialized) {
+ return systemName;
+ }
+ if (uname(&uts_name) >= 0) {
+ initialized = true;
+ systemName = uts_name.sysname;
+
+ if (cmIsOff(systemName)) {
+ systemName = "UnknownOS";
+ }
+
+ // fix for BSD/OS, remove the /
+ static const cmsys::RegularExpression bsdOsRegex("BSD.OS");
+ cmsys::RegularExpressionMatch match;
+ if (bsdOsRegex.find(uts_name.sysname, match)) {
+ systemName = "BSDOS";
+ }
+
+ // fix for GNU/kFreeBSD, remove the GNU/
+ if (systemName.find("kFreeBSD") != cm::string_view::npos) {
+ systemName = "kFreeBSD";
+ }
+
+ // fix for CYGWIN and MSYS which have windows version in them
+ if (systemName.find("CYGWIN") != cm::string_view::npos) {
+ systemName = "CYGWIN";
+ }
+
+ if (systemName.find("MSYS") != cm::string_view::npos) {
+ systemName = "MSYS";
+ }
+ return systemName;
+ }
+ return "";
+#endif
}