kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/common/path_utils.h (about) 1 /* 2 * Copyright 2018 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef KYTHE_CXX_COMMON_PATH_UTILS_H_ 18 #define KYTHE_CXX_COMMON_PATH_UTILS_H_ 19 20 #include <memory> 21 #include <optional> 22 #include <string> 23 #include <utility> 24 #include <variant> 25 #include <vector> 26 27 #include "absl/container/flat_hash_map.h" 28 #include "absl/status/statusor.h" 29 #include "absl/strings/string_view.h" 30 #include "absl/synchronization/mutex.h" 31 #include "absl/types/span.h" 32 #include "kythe/cxx/common/regex.h" 33 34 namespace kythe { 35 36 /// \brief PathCleaner relativizes paths against a root using CleanPath. 37 class PathCleaner { 38 public: 39 /// \brief Creates a PathCleaner using the given root. 40 /// \param root The root against which to relativize. 41 /// Will be cleaned and made an absolute path. 42 static absl::StatusOr<PathCleaner> Create(absl::string_view root); 43 44 /// \brief Transforms the cleaned, absolute version of `path` into a path 45 /// relative to the configured root. 46 /// \return A path relative to root, if `path` is, else a cleaned `path` or an 47 /// error if the current directory cannot be determined. 48 absl::StatusOr<std::string> Relativize(absl::string_view path) const; 49 50 private: 51 explicit PathCleaner(std::string root) : root_(std::move(root)) {} 52 53 std::string root_; 54 }; 55 56 /// \brief PathRealizer relativizes paths against a root using RealPath. 57 class PathRealizer { 58 public: 59 /// \brief Creates a PathCleaner using the given root. 60 /// \param root The root against which to relativize. 61 /// Will be resolved and made an absolute path. 62 static absl::StatusOr<PathRealizer> Create(absl::string_view root); 63 64 /// PathRealizer is copyable and movable. 65 PathRealizer(const PathRealizer& other); 66 PathRealizer& operator=(const PathRealizer& other); 67 PathRealizer(PathRealizer&& other) = default; 68 PathRealizer& operator=(PathRealizer&& other) = default; 69 70 /// \brief Transforms the resolved, absolute version of `path` into a path 71 /// relative to the configured root. 72 /// \return A path relative to root, if `path` is, else a resolved `path` or 73 /// an error if the path cannot be resolved. 74 absl::StatusOr<std::string> Relativize(absl::string_view path) const; 75 76 private: 77 class PathCache { 78 public: 79 template <typename K, typename Fn> 80 absl::StatusOr<std::string> FindOrInsert(K&& key, Fn&& make); 81 82 private: 83 absl::Mutex mu_; 84 absl::flat_hash_map<std::string, absl::StatusOr<std::string>> cache_; 85 }; 86 87 explicit PathRealizer(std::string root) : root_(std::move(root)) {} 88 89 std::string root_; 90 // A trivial lookup cache; if you're changing symlinks during the build you're 91 // going to have a bad time. 92 std::unique_ptr<PathCache> cache_ = std::make_unique<PathCache>(); 93 }; 94 95 /// \brief PathCanonicalizer relatives paths against a root. 96 class PathCanonicalizer { 97 public: 98 enum class Policy { 99 kCleanOnly = 0, ///< Only clean paths when applying canonicalizer. 100 kPreferRelative = 1, ///< Use clean paths if real path is absolute. 101 kPreferReal = 2, ///< Use real path, except if errors. 102 }; 103 104 /// \brief An entry to use when overriding the default policy. 105 struct PathEntry { 106 /// \brief The path pattern to override. 107 std::variant<Regex, std::string> path; 108 109 /// \brief The policy to apply to matching paths. 110 Policy policy = Policy::kCleanOnly; 111 }; 112 113 /// \brief Creates a new PathCanonicalizer with the given root and policy. 114 /// \param root The root directory to use when relativizing paths. 115 /// \param policy The default policy to apply. 116 static absl::StatusOr<PathCanonicalizer> Create( 117 absl::string_view root, Policy policy = Policy::kCleanOnly, 118 absl::Span<const PathEntry> path_map = {}); 119 120 /// \brief Transforms the provided path into a relative path depending on 121 /// configured policy. 122 absl::StatusOr<std::string> Relativize(absl::string_view path) const; 123 124 private: 125 explicit PathCanonicalizer(Policy policy, PathCleaner cleaner, 126 std::optional<PathRealizer> realizer, 127 RegexSet override_set, 128 std::vector<Policy> override_policy) 129 : policy_(policy), 130 cleaner_(std::move(cleaner)), 131 realizer_(std::move(realizer)), 132 override_set_(std::move(override_set)), 133 override_policy_(std::move(override_policy)) {} 134 135 absl::StatusOr<Policy> PolicyFor(absl::string_view path) const; 136 137 Policy policy_; 138 PathCleaner cleaner_; 139 std::optional<PathRealizer> realizer_; 140 RegexSet override_set_; 141 std::vector<Policy> override_policy_; 142 }; 143 144 /// \brief Parses a flag string as a PathCanonicalizer::Policy. 145 /// 146 /// This is an extension point for the Abseil Flags library to allow 147 /// using PathCanonicalizer directly as a flag. 148 bool AbslParseFlag(absl::string_view text, PathCanonicalizer::Policy* policy, 149 std::string* error); 150 151 /// \brief Returns the flag string representation of PathCanonicalizer::Policy. 152 /// 153 /// This is an extension point for the Abseil Flags library to allow 154 /// using PathCanonicalizer directly as a flag. 155 std::string AbslUnparseFlag(PathCanonicalizer::Policy policy); 156 157 /// \brief Parses a string into a PathCanonicalizer::Policy. 158 /// 159 /// Parses either integeral values of enumerators or lower-case, 160 /// dash-separated names: "clean-only", "prefer-relative", "prefer-real". 161 std::optional<PathCanonicalizer::Policy> ParseCanonicalizationPolicy( 162 absl::string_view policy); 163 164 /// \brief Parses a single PathEntry flag value as `<pattern>@<policy>` 165 bool AbslParseFlag(absl::string_view text, PathCanonicalizer::PathEntry* entry, 166 std::string* error); 167 168 /// \brief Returns the flag string representation of 169 /// PathCanonicalizer::PathEntry. 170 std::string AbslUnparseFlag(const PathCanonicalizer::PathEntry& entry); 171 172 /// \brief Parses a space separated list of PathEntry flag values. 173 bool AbslParseFlag(absl::string_view text, 174 std::vector<PathCanonicalizer::PathEntry>* entries, 175 std::string* error); 176 177 /// \brief Returns the flag string representation of 178 /// PathCanonicalizer::PathEntry. 179 std::string AbslUnparseFlag( 180 const std::vector<PathCanonicalizer::PathEntry>& entries); 181 182 /// \brief Append path `b` to path `a`, cleaning and returning the result. 183 std::string JoinPath(absl::string_view a, absl::string_view b); 184 185 /// \brief Returns the part of the path before the final '/'. 186 absl::string_view Dirname(absl::string_view path); 187 188 /// \brief Returns the part of the path after the final '/'. 189 absl::string_view Basename(absl::string_view path); 190 191 /// \brief Collapse duplicate "/"s, resolve ".." and "." path elements, remove 192 /// trailing "/". 193 std::string CleanPath(absl::string_view input); 194 195 // \brief Returns true if path is absolute. 196 bool IsAbsolutePath(absl::string_view path); 197 198 /// \brief Relativize `to_relativize` with respect to `relativize_against`. 199 /// 200 /// If `to_relativize` does not name a path that is a child of 201 /// `relativize_against`, `RelativizePath` will return an absolute path 202 /// (resolved against the current working directory). 203 /// 204 /// Note: arguments are assumed to be valid paths, but validity is not checked. 205 /// 206 /// \param to_relativize Relative or absolute path to a file. 207 /// \param relativize_against Relative or absolute path to a directory. 208 std::string RelativizePath(absl::string_view to_relativize, 209 absl::string_view relativize_against); 210 211 /// \brief Convert `path` to an absolute path, eliminating `.` and `..`. 212 /// \param path The path to convert. 213 absl::StatusOr<std::string> MakeCleanAbsolutePath(absl::string_view path); 214 215 /// \brief Returns the process's current working directory or an error. 216 absl::StatusOr<std::string> GetCurrentDirectory(); 217 218 /// \brief Returns the result of resolving symbolic links. 219 absl::StatusOr<std::string> RealPath(absl::string_view path); 220 221 } // namespace kythe 222 223 #endif // KYTHE_CXX_COMMON_PATH_UTILS_H_