summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Woehlke <matthew.woehlke@kitware.com>2022-08-17 18:20:47 (GMT)
committerMatthew Woehlke <matthew.woehlke@kitware.com>2022-09-05 17:19:58 (GMT)
commitd1befe5515968d7fe4f7fd3f2ca243e98d6012d0 (patch)
treef60ecc3aced197ff90d2d2d2d3705cd3178818a1
parent7b9757e50715ab6abc08e7005c66565f5c965de3 (diff)
downloadCMake-d1befe5515968d7fe4f7fd3f2ca243e98d6012d0.zip
CMake-d1befe5515968d7fe4f7fd3f2ca243e98d6012d0.tar.gz
CMake-d1befe5515968d7fe4f7fd3f2ca243e98d6012d0.tar.bz2
cmSystemTools: Add MakeTempDirectory
Add a cross-platform wrapper over mkdtemp. This will allow us to create guaranteed-unique directories. On POSIX platforms, this is simply a wrapper over mkdtemp. On Windows, we take a brute-force approach using C++11's random facilities and relying on attempts to create an existing directory resulting in an error. (This approach is very possibly how mkdtemp is implemented internally, and should be suitable for any platform if needed, although at present it only uses a partial set of substitution characters since Windows likely implies a case-insensitive file system.)
-rw-r--r--Source/cmSystemTools.cxx93
-rw-r--r--Source/cmSystemTools.h25
2 files changed, 117 insertions, 1 deletions
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 3c4e709..8e77afa 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -11,6 +11,11 @@
// NOLINTNEXTLINE(bugprone-reserved-identifier)
# define _XOPEN_SOURCE 700
#endif
+#if defined(__APPLE__)
+// Restore Darwin APIs removed by _POSIX_C_SOURCE.
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+# define _DARWIN_C_SOURCE
+#endif
#include "cmSystemTools.h"
@@ -88,7 +93,6 @@
# include <unistd.h>
# include <sys/time.h>
-# include <sys/types.h>
#endif
#if defined(_WIN32) && \
@@ -1001,6 +1005,93 @@ void cmSystemTools::InitializeLibUV()
#endif
}
+#if defined(_WIN32)
+# include <random>
+
+# include <wctype.h>
+# ifdef _MSC_VER
+using mode_t = cmSystemTools::SystemTools::mode_t;
+# endif
+#else
+# include <sys/stat.h>
+#endif
+
+inline int Mkdir(const char* dir, const mode_t* mode)
+{
+#if defined(_WIN32)
+ int ret = _wmkdir(cmSystemTools::ConvertToWindowsExtendedPath(dir).c_str());
+ if (ret == 0 && mode)
+ cmSystemTools::SystemTools::SetPermissions(dir, *mode);
+ return ret;
+#else
+ return mkdir(dir, mode ? *mode : 0777);
+#endif
+}
+
+cmsys::Status cmSystemTools::MakeTempDirectory(std::string& path,
+ const mode_t* mode)
+{
+ if (path.empty()) {
+ return cmsys::Status::POSIX(EINVAL);
+ }
+ return cmSystemTools::MakeTempDirectory(&path.front(), mode);
+}
+
+cmsys::Status cmSystemTools::MakeTempDirectory(char* path, const mode_t* mode)
+{
+ if (!path) {
+ return cmsys::Status::POSIX(EINVAL);
+ }
+
+ // verify that path ends with "XXXXXX"
+ const auto l = std::strlen(path);
+ if (!cmHasLiteralSuffix(cm::string_view{ path, l }, "XXXXXX")) {
+ return cmsys::Status::POSIX(EINVAL);
+ }
+
+ // create parent directories
+ auto* sep = path;
+ while ((sep = strchr(sep, '/'))) {
+ // all underlying functions use C strings,
+ // so temporarily end the string here
+ *sep = '\0';
+ Mkdir(path, mode);
+
+ *sep = '/';
+ ++sep;
+ }
+
+#ifdef _WIN32
+ const int nchars = 36;
+ const char chars[nchars + 1] = "abcdefghijklmnopqrstuvwxyz0123456789";
+
+ std::random_device rd;
+ std::mt19937 rg{ rd() };
+ std::uniform_int_distribution<int> dist{ 0, nchars - 1 };
+
+ for (auto tries = 100; tries; --tries) {
+ for (auto n = l - 6; n < l; ++n) {
+ path[n] = chars[dist(rg)];
+ }
+ if (Mkdir(path, mode) == 0) {
+ return cmsys::Status::Success();
+ } else if (errno != EEXIST) {
+ return cmsys::Status::POSIX_errno();
+ }
+ }
+ return cmsys::Status::POSIX(EAGAIN);
+#else
+ if (mkdtemp(path)) {
+ if (mode) {
+ chmod(path, *mode);
+ }
+ } else {
+ return cmsys::Status::POSIX_errno();
+ }
+ return cmsys::Status::Success();
+#endif
+}
+
#ifdef _WIN32
namespace {
bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname,
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 48bbe23..b02a977 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -4,6 +4,10 @@
#include "cmConfigure.h" // IWYU pragma: keep
+#if !defined(_WIN32)
+# include <sys/types.h>
+#endif
+
#include <cstddef>
#include <functional>
#include <map>
@@ -151,6 +155,27 @@ public:
Failure,
};
+#if defined(_MSC_VER)
+ /** Visual C++ does not define mode_t. */
+ using mode_t = unsigned short;
+#endif
+
+ /**
+ * Make a new temporary directory. The path must end in "XXXXXX", and will
+ * be modified to reflect the name of the directory created. This function
+ * is similar to POSIX mkdtemp (and is implemented using the same where that
+ * function is available).
+ *
+ * This function can make a full path even if none of the directories existed
+ * prior to calling this function.
+ *
+ * Note that this function may modify \p path even if it does not succeed.
+ */
+ static cmsys::Status MakeTempDirectory(char* path,
+ const mode_t* mode = nullptr);
+ static cmsys::Status MakeTempDirectory(std::string& path,
+ const mode_t* mode = nullptr);
+
/** Copy a file. */
static bool CopySingleFile(const std::string& oldname,
const std::string& newname);