浏览代码

cmCMakePath: Class for path handling

Marc Chevrier 5 年之前
父节点
当前提交
212e953d35
共有 6 个文件被更改,包括 723 次插入3 次删除
  1. 2 0
      Source/CMakeLists.txt
  2. 146 0
      Source/cmCMakePath.cxx
  3. 571 0
      Source/cmCMakePath.h
  4. 1 1
      Utilities/std/cm/bits/fs_path.cxx
  5. 2 2
      Utilities/std/cm/filesystem
  6. 1 0
      bootstrap

+ 2 - 0
Source/CMakeLists.txt

@@ -182,6 +182,8 @@ set(SRCS
   cmCheckCustomOutputs.cxx
   cmCLocaleEnvironmentScope.h
   cmCLocaleEnvironmentScope.cxx
+  cmCMakePath.h
+  cmCMakePath.cxx
   cmCommandArgumentParserHelper.cxx
   cmCommonTargetGenerator.cxx
   cmCommonTargetGenerator.h

+ 146 - 0
Source/cmCMakePath.cxx

@@ -0,0 +1,146 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmCMakePath.h"
+
+#include <string>
+
+#if defined(_WIN32)
+#  include <cstdlib>
+#endif
+
+#include <cm/filesystem>
+#include <cm/string_view>
+
+#if defined(_WIN32)
+#  include "cmStringAlgorithms.h"
+#endif
+
+cmCMakePath& cmCMakePath::ReplaceWideExtension(cm::string_view extension)
+{
+  auto file = this->Path.filename().string();
+  if (!file.empty() && file != "." && file != "..") {
+    auto pos = file.find('.', file[0] == '.' ? 1 : 0);
+    if (pos != std::string::npos) {
+      file.erase(pos);
+    }
+  }
+  if (!extension.empty()) {
+    if (extension[0] != '.') {
+      file += '.';
+    }
+    file.append(std::string(extension));
+  }
+  this->Path.replace_filename(file);
+  return *this;
+}
+
+cmCMakePath cmCMakePath::GetWideExtension() const
+{
+  auto file = this->Path.filename().string();
+  if (file.empty() || file == "." || file == "..") {
+    return cmCMakePath{};
+  }
+
+  auto pos = file.find('.', file[0] == '.' ? 1 : 0);
+  if (pos != std::string::npos) {
+    return cm::string_view(file.data() + pos, file.length() - pos);
+  }
+
+  return cmCMakePath{};
+}
+
+cmCMakePath cmCMakePath::GetNarrowStem() const
+{
+  auto stem = this->Path.stem().string();
+  if (!stem.empty()) {
+    auto pos = stem.find('.', stem[0] == '.' ? 1 : 0);
+    if (pos != std::string::npos) {
+      return stem.substr(0, pos);
+    }
+  }
+  return stem;
+}
+
+cmCMakePath cmCMakePath::Absolute(const cm::filesystem::path& base) const
+{
+  if (this->Path.is_relative()) {
+    auto path = base;
+    path /= this->Path;
+    // filesystem::path::operator/= use preferred_separator ('\' on Windows)
+    // so converts back to '/'
+    return path.generic_string();
+  }
+  return *this;
+}
+
+bool cmCMakePath::IsPrefix(const cmCMakePath& path) const
+{
+  auto prefix_it = this->Path.begin();
+  auto prefix_end = this->Path.end();
+  auto path_it = path.Path.begin();
+  auto path_end = path.Path.end();
+
+  while (prefix_it != prefix_end && path_it != path_end &&
+         *prefix_it == *path_it) {
+    ++prefix_it;
+    ++path_it;
+  }
+  return prefix_it == prefix_end;
+}
+
+std::string cmCMakePath::FormatPath(std::string path, format fmt)
+{
+#if defined(_WIN32)
+  if (fmt == auto_format || fmt == native_format) {
+    auto prefix = path.substr(0, 4);
+    for (auto& c : prefix) {
+      if (c == '\\') {
+        c = '/';
+      }
+    }
+    // remove Windows long filename marker
+    if (prefix == "//?/"_s) {
+      path.erase(0, 4);
+    }
+    if (cmHasPrefix(path, "UNC/"_s) || cmHasPrefix(path, "UNC\\"_s)) {
+      path.erase(0, 2);
+      path[0] = '/';
+    }
+  }
+#else
+  static_cast<void>(fmt);
+#endif
+  return path;
+}
+
+void cmCMakePath::GetNativePath(std::string& path) const
+{
+  cm::filesystem::path tmp(this->Path);
+  tmp.make_preferred();
+
+  path = tmp.string();
+}
+void cmCMakePath::GetNativePath(std::wstring& path) const
+{
+  cm::filesystem::path tmp(this->Path);
+  tmp.make_preferred();
+
+  path = tmp.wstring();
+
+#if defined(_WIN32)
+  // Windows long filename
+  static std::wstring UNC(L"\\\\?\\UNC");
+  static std::wstring PREFIX(L"\\\\?\\");
+
+  if (this->IsAbsolute() && path.length() > _MAX_PATH - 12) {
+    if (this->HasRootName() && path[0] == L'\\') {
+      path = UNC + path.substr(1);
+    } else {
+      path = PREFIX + path;
+    }
+  }
+#endif
+}

+ 571 - 0
Source/cmCMakePath.h

@@ -0,0 +1,571 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <cstddef>
+#include <string>
+#include <utility>
+
+#include <cm/filesystem>
+#include <cm/string_view>
+#include <cm/type_traits>
+#include <cmext/string_view>
+
+namespace detail {
+#if defined(__SUNPRO_CC) && defined(__sparc)
+// Oracle DeveloperStudio C++ compiler on Solaris/Sparc fails to compile
+// the full 'is_pathable' and 'is_move_pathable' checks.  We use it only to
+// improve error messages via 'enable_if' when calling methods with incorrect
+// types. Just pretend all types are allowed so we can at least compile valid
+// code.
+template <typename T>
+struct is_pathable : std::true_type
+{
+};
+
+template <typename T>
+struct is_move_pathable : std::true_type
+{
+};
+
+#else
+template <typename T, typename = void>
+struct is_pathable : std::false_type
+{
+};
+
+template <>
+struct is_pathable<cm::filesystem::path> : std::true_type
+{
+};
+template <>
+struct is_pathable<std::string> : std::true_type
+{
+};
+template <>
+struct is_pathable<cm::string_view> : std::true_type
+{
+};
+template <>
+struct is_pathable<cm::static_string_view> : std::true_type
+{
+};
+template <typename T>
+struct is_pathable<
+  T,
+  cm::enable_if_t<std::is_same<char*, typename std::decay<T>::type>::value,
+                  void>>
+  : cm::bool_constant<std::is_same<char*, typename std::decay<T>::type>::value>
+{
+};
+
+template <typename T>
+struct is_move_pathable : std::false_type
+{
+};
+
+template <>
+struct is_move_pathable<cm::filesystem::path> : std::true_type
+{
+};
+template <>
+struct is_move_pathable<std::string> : std::true_type
+{
+};
+#endif
+}
+
+class cmCMakePath
+{
+private:
+  template <typename Source>
+  using enable_if_move_pathable =
+    cm::enable_if_t<detail::is_move_pathable<Source>::value, cmCMakePath&>;
+
+  template <typename Source>
+  using enable_if_pathable =
+    cm::enable_if_t<detail::is_pathable<Source>::value, cmCMakePath&>;
+
+public:
+  using value_type = cm::filesystem::path::value_type;
+  using string_type = cm::filesystem::path::string_type;
+
+  enum format : unsigned char
+  {
+    auto_format =
+      static_cast<unsigned char>(cm::filesystem::path::format::auto_format),
+    native_format =
+      static_cast<unsigned char>(cm::filesystem::path::format::native_format),
+    generic_format =
+      static_cast<unsigned char>(cm::filesystem::path::format::generic_format)
+  };
+
+  class iterator;
+  using const_iterator = iterator;
+
+  cmCMakePath() noexcept = default;
+
+  cmCMakePath(const cmCMakePath&) = default;
+
+  cmCMakePath(cmCMakePath&& path) noexcept
+    : Path(std::forward<cm::filesystem::path>(path.Path))
+  {
+  }
+
+  cmCMakePath(cm::filesystem::path path) noexcept
+    : Path(std::move(path))
+  {
+  }
+  cmCMakePath(cm::string_view source, format fmt = generic_format) noexcept
+    : Path(FormatPath(source, fmt))
+  {
+  }
+  template <typename Source, typename = enable_if_move_pathable<Source>>
+  cmCMakePath(Source source, format fmt = generic_format)
+    : Path(FormatPath(std::move(source), fmt))
+  {
+  }
+
+  template <typename Source, typename = enable_if_move_pathable<Source>>
+  cmCMakePath& Assign(Source&& source)
+  {
+    this->Path = std::forward<Source>(source);
+    return *this;
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath& Assign(const Source& source)
+  {
+    this->Path = source;
+    return *this;
+  }
+
+  cmCMakePath& operator=(const cmCMakePath& path)
+  {
+    if (this != &path) {
+      this->Path = path.Path;
+    }
+    return *this;
+  }
+  cmCMakePath& operator=(cmCMakePath&& path) noexcept
+  {
+    if (this != &path) {
+      this->Path = std::move(path.Path);
+    }
+    return *this;
+  }
+  template <typename Source, typename = enable_if_move_pathable<Source>>
+  cmCMakePath& operator=(Source&& source)
+  {
+    this->Assign(std::forward<Source>(source));
+    return *this;
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath& operator=(const Source& source)
+  {
+    this->Assign(source);
+    return *this;
+  }
+
+  // Concatenation
+  cmCMakePath& Append(const cmCMakePath& path)
+  {
+    return this->Append(path.Path);
+  }
+  cmCMakePath& Append(const cm::filesystem::path& path)
+  {
+    this->Path /= path;
+    // filesystem::path::append use preferred_separator ('\' on Windows)
+    // so convert back to '/'
+    this->Path = this->Path.generic_string();
+    return *this;
+  }
+
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath& Append(const Source& source)
+  {
+    return this->Append(cm::filesystem::path(source));
+  }
+
+  cmCMakePath& operator/=(const cmCMakePath& path)
+  {
+    return this->Append(path);
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath& operator/=(const Source& source)
+  {
+    return this->Append(source);
+  }
+
+  cmCMakePath& Concat(const cmCMakePath& path)
+  {
+    this->Path += path.Path;
+    return *this;
+  }
+  cmCMakePath& Concat(cm::static_string_view source)
+  {
+    this->Path.concat(std::string(source));
+    return *this;
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath& Concat(const Source& source)
+  {
+    this->Path.concat(source);
+    return *this;
+  }
+
+  cmCMakePath& operator+=(const cmCMakePath& path)
+  {
+    return this->Concat(path);
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath& operator+=(const Source& source)
+  {
+    return this->Concat(source);
+  }
+
+  // Manipulation
+  void Clear() noexcept { this->Path.clear(); }
+
+  cmCMakePath& RemoveFileName()
+  {
+    this->Path.remove_filename();
+    return *this;
+  }
+
+  cmCMakePath& ReplaceFileName(const cmCMakePath& filename)
+  {
+    if (this->Path.has_filename()) {
+      this->Path.replace_filename(filename.Path);
+    }
+    return *this;
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath& ReplaceFileName(const Source& filename)
+  {
+    if (this->Path.has_filename()) {
+      this->Path.replace_filename(filename);
+    }
+    return *this;
+  }
+
+  cmCMakePath& ReplaceExtension(const cmCMakePath& extension = cmCMakePath())
+  {
+    this->Path.replace_extension(extension.Path);
+    return *this;
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath& ReplaceExtension(const Source& extension)
+  {
+    this->Path.replace_extension(extension);
+    return *this;
+  }
+
+  cmCMakePath& ReplaceWideExtension(
+    const cmCMakePath& extension = cmCMakePath())
+  {
+    return this->ReplaceWideExtension(
+      static_cast<cm::string_view>(extension.Path.string()));
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath& ReplaceWideExtension(const Source& extension)
+  {
+    return this->ReplaceWideExtension(cm::string_view(extension));
+  }
+  cmCMakePath& ReplaceWideExtension(cm::string_view extension);
+
+  cmCMakePath& RemoveExtension()
+  {
+    if (this->Path.has_extension()) {
+      this->ReplaceExtension(cm::string_view(""));
+    }
+    return *this;
+  }
+
+  cmCMakePath& RemoveWideExtension()
+  {
+    if (this->Path.has_extension()) {
+      this->ReplaceWideExtension(cm::string_view(""));
+    }
+    return *this;
+  }
+
+  void swap(cmCMakePath& other) noexcept { this->Path.swap(other.Path); }
+
+  // Observers
+  std::string String() const { return this->Path.string(); }
+  std::wstring WString() const { return this->Path.wstring(); }
+
+  string_type Native() const
+  {
+    string_type path;
+    this->GetNativePath(path);
+
+    return path;
+  }
+  std::string NativeString() const
+  {
+    std::string path;
+    this->GetNativePath(path);
+
+    return path;
+  }
+  std::wstring NativeWString() const
+  {
+    std::wstring path;
+    this->GetNativePath(path);
+
+    return path;
+  }
+  std::string GenericString() const { return this->Path.generic_string(); }
+  std::wstring GenericWString() const { return this->Path.generic_wstring(); }
+
+  // Decomposition
+  cmCMakePath GetRootName() const { return this->Path.root_name(); }
+  cmCMakePath GetRootDirectory() const { return this->Path.root_directory(); }
+  cmCMakePath GetRootPath() const { return this->Path.root_path(); }
+  cmCMakePath GetFileName() const { return this->Path.filename(); }
+  cmCMakePath GetExtension() const { return this->Path.extension(); }
+  cmCMakePath GetWideExtension() const;
+  cmCMakePath GetStem() const { return this->Path.stem(); }
+  cmCMakePath GetNarrowStem() const;
+
+  cmCMakePath GetRelativePath() const { return this->Path.relative_path(); }
+  cmCMakePath GetParentPath() const { return this->Path.parent_path(); }
+
+  // Generation
+  cmCMakePath Normal() const
+  {
+    auto path = this->Path.lexically_normal();
+    // filesystem::path:lexically_normal use preferred_separator ('\') on
+    // Windows) so convert back to '/'
+    return path.generic_string();
+  }
+
+  cmCMakePath Relative(const cmCMakePath& base) const
+  {
+    return this->Relative(base.Path);
+  }
+  cmCMakePath Relative(const cm::filesystem::path& base) const
+  {
+    auto path = this->Path.lexically_relative(base);
+    // filesystem::path:lexically_relative use preferred_separator ('\') on
+    // Windows) so convert back to '/'
+    return path.generic_string();
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath Relative(const Source& base) const
+  {
+    return this->Relative(cm::filesystem::path(base));
+  }
+
+  cmCMakePath Proximate(const cmCMakePath& base) const
+  {
+    return this->Proximate(base.Path);
+  }
+  cmCMakePath Proximate(const cm::filesystem::path& base) const
+  {
+    auto path = this->Path.lexically_proximate(base);
+    // filesystem::path::lexically_proximate use preferred_separator ('\') on
+    // Windows) so convert back to '/'
+    return path.generic_string();
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath Proximate(const Source& base) const
+  {
+    return this->Proximate(cm::filesystem::path(base));
+  }
+
+  cmCMakePath Absolute(const cmCMakePath& base) const
+  {
+    return this->Absolute(base.Path);
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  cmCMakePath Absolute(const Source& base) const
+  {
+    return this->Absolute(cm::filesystem::path(base));
+  }
+  cmCMakePath Absolute(const cm::filesystem::path& base) const;
+
+  // Comparison
+  int Compare(const cmCMakePath& path) const noexcept
+  {
+    return this->Path.compare(path.Path);
+  }
+
+  // Query
+  bool IsEmpty() const noexcept { return this->Path.empty(); }
+
+  bool HasRootPath() const { return this->Path.has_root_path(); }
+  bool HasRootName() const { return this->Path.has_root_name(); }
+  bool HasRootDirectory() const { return this->Path.has_root_directory(); }
+  bool HasRelativePath() const { return this->Path.has_relative_path(); }
+  bool HasParentPath() const { return this->Path.has_parent_path(); }
+  bool HasFileName() const { return this->Path.has_filename(); }
+  bool HasStem() const { return this->Path.has_stem(); }
+  bool HasExtension() const { return this->Path.has_extension(); }
+
+  bool IsAbsolute() const { return this->Path.is_absolute(); }
+  bool IsRelative() const { return this->Path.is_relative(); }
+  bool IsPrefix(const cmCMakePath& path) const;
+
+  // Iterators
+  // =========
+  inline iterator begin() const;
+  inline iterator end() const;
+
+  // Non-members
+  // ===========
+  friend inline bool operator==(const cmCMakePath& lhs,
+                                const cmCMakePath& rhs) noexcept
+  {
+    return lhs.Compare(rhs) == 0;
+  }
+  friend inline bool operator!=(const cmCMakePath& lhs,
+                                const cmCMakePath& rhs) noexcept
+  {
+    return lhs.Compare(rhs) != 0;
+  }
+
+  friend inline cmCMakePath operator/(const cmCMakePath& lhs,
+                                      const cmCMakePath& rhs)
+  {
+    cmCMakePath result(lhs);
+    result /= rhs;
+
+    return result;
+  }
+
+private:
+  friend std::size_t hash_value(const cmCMakePath& path) noexcept;
+
+  static std::string FormatPath(std::string path, format fmt = generic_format);
+  static std::string FormatPath(cm::string_view path,
+                                format fmt = generic_format)
+  {
+    return FormatPath(std::string(path), fmt);
+  }
+
+  void GetNativePath(std::string& path) const;
+  void GetNativePath(std::wstring& path) const;
+
+  cm::filesystem::path Path;
+};
+
+class cmCMakePath::iterator
+{
+public:
+  using iterator_category = cm::filesystem::path::iterator::iterator_category;
+
+  using value_type = cmCMakePath;
+  using difference_type = cm::filesystem::path::iterator::difference_type;
+  using pointer = const cmCMakePath*;
+  using reference = const cmCMakePath&;
+
+  iterator() = default;
+
+  iterator(const iterator& other)
+    : Iterator(other.Iterator)
+    , Path(other.Path)
+    , PathElement(*this->Iterator)
+  {
+  }
+
+  ~iterator() = default;
+
+  iterator& operator=(const iterator& other)
+  {
+    if (this != &other) {
+      this->Iterator = other.Iterator;
+      this->Path = other.Path;
+      this->PathElement = *this->Iterator;
+    }
+
+    return *this;
+  }
+
+  reference operator*() const { return this->PathElement; }
+
+  pointer operator->() const { return &this->PathElement; }
+
+  iterator& operator++()
+  {
+    ++this->Iterator;
+    this->PathElement = *this->Iterator;
+
+    return *this;
+  }
+
+  iterator operator++(int)
+  {
+    iterator it(*this);
+    this->operator++();
+    return it;
+  }
+
+  iterator& operator--()
+  {
+    --this->Iterator;
+    this->PathElement = *this->Iterator;
+
+    return *this;
+  }
+
+  iterator operator--(int)
+  {
+    iterator it(*this);
+    this->operator--();
+    return it;
+  }
+
+private:
+  friend class cmCMakePath;
+  friend bool operator==(const iterator&, const iterator&);
+
+  iterator(const cmCMakePath* path, const cm::filesystem::path::iterator& it)
+    : Iterator(it)
+    , Path(path)
+    , PathElement(*this->Iterator)
+  {
+  }
+
+  cm::filesystem::path::iterator Iterator;
+  const cmCMakePath* Path = nullptr;
+  cmCMakePath PathElement;
+};
+
+inline cmCMakePath::iterator cmCMakePath::begin() const
+{
+  return iterator(this, this->Path.begin());
+}
+inline cmCMakePath::iterator cmCMakePath::end() const
+{
+  return iterator(this, this->Path.end());
+}
+
+// Non-member functions
+// ====================
+inline bool operator==(const cmCMakePath::iterator& lhs,
+                       const cmCMakePath::iterator& rhs)
+{
+  return lhs.Path == rhs.Path && lhs.Path != nullptr &&
+    lhs.Iterator == rhs.Iterator;
+}
+
+inline bool operator!=(const cmCMakePath::iterator& lhs,
+                       const cmCMakePath::iterator& rhs)
+{
+  return !(lhs == rhs);
+}
+
+inline void swap(cmCMakePath& lhs, cmCMakePath& rhs) noexcept
+{
+  lhs.swap(rhs);
+}
+
+inline std::size_t hash_value(const cmCMakePath& path) noexcept
+{
+  return cm::filesystem::hash_value(path.Path);
+}

+ 1 - 1
Utilities/std/cm/bits/fs_path.cxx

@@ -847,7 +847,7 @@ cm::string_view path::get_filename_fragment(filename_fragment fragment) const
 {
   auto file = this->get_filename();
 
-  if (file == "." || file == ".." || file.empty()) {
+  if (file.empty() || file == "." || file == "..") {
     return fragment == filename_fragment::stem ? file : cm::string_view{};
   }
 

+ 2 - 2
Utilities/std/cm/filesystem

@@ -1150,6 +1150,8 @@ inline path::iterator path::end() const
   return iterator(this, true);
 }
 
+// Non-member functions
+// ====================
 bool operator==(const path::iterator& lhs, const path::iterator& rhs);
 
 inline bool operator!=(const path::iterator& lhs, const path::iterator& rhs)
@@ -1157,8 +1159,6 @@ inline bool operator!=(const path::iterator& lhs, const path::iterator& rhs)
   return !(lhs == rhs);
 }
 
-// Non-member functions
-// ====================
 inline void swap(path& lhs, path& rhs) noexcept
 {
   lhs.swap(rhs);

+ 1 - 0
bootstrap

@@ -288,6 +288,7 @@ CMAKE_CXX_SOURCES="\
   cmBreakCommand \
   cmBuildCommand \
   cmCMakeMinimumRequired \
+  cmCMakePath \
   cmCMakePolicyCommand \
   cmCPackPropertiesGenerator \
   cmCacheManager \