Fixes #1417.
// Prefix to the input file when adding it to a command line.
std::string input_file_prefix;
- // The source file path run through Util::normalize_concrete_absolute_path.
- std::string normalized_input_file;
-
// In normal compiler operation an output file is created if there is no
// compiler error. However certain flags like -fsyntax-only change this
// behavior.
Depfile.cpp
Hash.cpp
ProgressBar.cpp
- Util.cpp
argprocessing.cpp
ccache.cpp
compopt.cpp
#include "Config.hpp"
-#include <ccache/Util.hpp>
#include <ccache/core/AtomicFile.hpp>
#include <ccache/core/common.hpp>
#include <ccache/core/exceptions.hpp>
#include <ccache/core/types.hpp>
#include <ccache/util/DirEntry.hpp>
+#include <ccache/util/PathString.hpp>
#include <ccache/util/Tokenizer.hpp>
#include <ccache/util/UmaskScope.hpp>
#include <ccache/util/assertions.hpp>
#include <ccache/util/file.hpp>
#include <ccache/util/filesystem.hpp>
#include <ccache/util/format.hpp>
+#include <ccache/util/path.hpp>
#include <ccache/util/string.hpp>
#include <ccache/util/wincompat.hpp>
namespace fs = util::filesystem;
+using pstr = util::PathString;
using util::DirEntry;
+using util::make_path;
namespace {
}
void
-verify_absolute_path(const std::string& value)
+verify_absolute_path(const fs::path& value)
{
- if (!fs::path(value).is_absolute()) {
+ if (!value.is_absolute()) {
throw core::Error(FMT("not an absolute path: \"{}\"", value));
}
}
auto cmdline_settings_map =
create_cmdline_settings_map(cmdline_config_settings);
- const std::string home_dir = home_directory();
- const std::string legacy_ccache_dir = Util::make_path(home_dir, ".ccache");
+ const fs::path home_dir = home_directory();
+ const fs::path legacy_ccache_dir = home_dir / ".ccache";
const bool legacy_ccache_dir_exists =
DirEntry(legacy_ccache_dir).is_directory();
#ifdef _WIN32
// Only used for ccache tests:
const char* const env_ccache_configpath2 = getenv("CCACHE_CONFIGPATH2");
- std::string sysconfdir = Util::make_path(k_sysconfdir);
+ fs::path sysconfdir(k_sysconfdir);
#ifdef _WIN32
if (const char* program_data = getenv("ALLUSERSPROFILE"))
- sysconfdir = Util::make_path(program_data, "ccache");
+ sysconfdir = fs::path(program_data) / "ccache";
#endif
- set_system_config_path(env_ccache_configpath2
- ? env_ccache_configpath2
- : Util::make_path(sysconfdir, "ccache.conf"));
+ set_system_config_path(env_ccache_configpath2 ? env_ccache_configpath2
+ : sysconfdir / "ccache.conf");
// A missing config file in SYSCONFDIR is OK so don't check return value.
update_from_file(system_config_path());
const char* const env_ccache_dir = getenv("CCACHE_DIR");
auto cmdline_cache_dir = cmdline_settings_map.find("cache_dir");
- std::string config_dir;
+ fs::path config_dir;
if (cmdline_cache_dir != cmdline_settings_map.end()) {
config_dir = cmdline_cache_dir->second;
} else if (env_ccache_dir && *env_ccache_dir) {
#ifdef _WIN32
} else if (env_local_appdata
&& DirEntry(
- Util::make_path(env_local_appdata, "ccache", "ccache.conf"))) {
- config_dir = Util::make_path(env_local_appdata, "ccache");
+ make_path(env_local_appdata, "ccache", "ccache.conf"))) {
+ config_dir = make_path(env_local_appdata, "ccache");
} else if (env_appdata
- && DirEntry(
- Util::make_path(env_appdata, "ccache", "ccache.conf"))) {
- config_dir = Util::make_path(env_appdata, "ccache");
+ && DirEntry(make_path(env_appdata, "ccache", "ccache.conf"))) {
+ config_dir = make_path(env_appdata, "ccache");
} else if (env_local_appdata) {
- config_dir = Util::make_path(env_local_appdata, "ccache");
+ config_dir = make_path(env_local_appdata, "ccache");
} else {
throw core::Fatal(
"could not find configuration file and the LOCALAPPDATA environment"
}
#else
} else if (env_xdg_config_home) {
- config_dir = Util::make_path(env_xdg_config_home, "ccache");
+ config_dir = make_path(env_xdg_config_home, "ccache");
} else {
config_dir = default_config_dir(home_dir);
}
#endif
- set_config_path(Util::make_path(config_dir, "ccache.conf"));
+ set_config_path(config_dir / "ccache.conf");
}
const std::string& cache_dir_before_config_file_was_read = cache_dir();
if (cache_dir().empty()) {
if (legacy_ccache_dir_exists) {
- set_cache_dir(legacy_ccache_dir);
+ set_cache_dir(pstr(legacy_ccache_dir));
#ifdef _WIN32
} else if (env_local_appdata) {
- set_cache_dir(Util::make_path(env_local_appdata, "ccache"));
+ set_cache_dir(pstr(fs::path(env_local_appdata) / "ccache"));
} else {
throw core::Fatal(
"could not find cache directory and the LOCALAPPDATA environment"
}
#else
} else if (env_xdg_cache_home) {
- set_cache_dir(Util::make_path(env_xdg_cache_home, "ccache"));
+ set_cache_dir(make_path(env_xdg_cache_home, "ccache"));
} else {
set_cache_dir(default_cache_dir(home_dir));
}
// system config).
}
-const std::string&
+const fs::path&
Config::config_path() const
{
return m_config_path;
}
-const std::string&
+const fs::path&
Config::system_config_path() const
{
return m_system_config_path;
}
void
-Config::set_config_path(std::string path)
+Config::set_config_path(const fs::path& path)
{
- m_config_path = std::move(path);
+ m_config_path = path;
}
void
-Config::set_system_config_path(std::string path)
+Config::set_system_config_path(const fs::path& path)
{
- m_system_config_path = std::move(path);
+ m_system_config_path = path;
}
bool
-Config::update_from_file(const std::string& path)
+Config::update_from_file(const fs::path& path)
{
return parse_config_file(
- path, [&](const auto& /*line*/, const auto& key, const auto& value) {
+ pstr(path), [&](const auto& /*line*/, const auto& key, const auto& value) {
if (!key.empty()) {
- set_item(key, value, std::nullopt, false, path);
+ set_item(key, value, std::nullopt, false, pstr(path));
}
});
}
return format_bool(m_absolute_paths_in_stderr);
case ConfigItem::base_dir:
- return m_base_dir;
+ return pstr(m_base_dir).str();
case ConfigItem::cache_dir:
return m_cache_dir;
m_base_dir = value;
if (!m_base_dir.empty()) { // The empty string means "disable"
verify_absolute_path(m_base_dir);
- m_base_dir = Util::normalize_abstract_absolute_path(m_base_dir);
+ m_base_dir = m_base_dir.lexically_normal();
}
break;
void read(const std::vector<std::string>& cmdline_config_settings = {});
bool absolute_paths_in_stderr() const;
- const std::string& base_dir() const;
+ const std::filesystem::path& base_dir() const;
const std::string& cache_dir() const;
const std::string& compiler() const;
const std::string& compiler_check() const;
util::SizeUnitPrefixType size_unit_prefix_type() const;
std::string default_temporary_dir() const;
- void set_base_dir(const std::string& value);
+ void set_base_dir(const std::filesystem::path& value);
void set_cache_dir(const std::string& value);
void set_compiler(const std::string& value);
void set_compiler_type(CompilerType value);
void set_temporary_dir(const std::string& value);
// Where to write configuration changes.
- const std::string& config_path() const;
+ const std::filesystem::path& config_path() const;
// System (read-only) configuration file (if any).
- const std::string& system_config_path() const;
+ const std::filesystem::path& system_config_path() const;
- void set_config_path(std::string path);
- void set_system_config_path(std::string path);
+ void set_config_path(const std::filesystem::path& path);
+ void set_system_config_path(const std::filesystem::path& path);
using ItemVisitor = std::function<void(const std::string& key,
const std::string& value,
//
// Returns false if the file can't be opened, otherwise true. Throws Error on
// invalid configuration values.
- bool update_from_file(const std::string& path);
+ bool update_from_file(const std::filesystem::path& path);
// Set config values from a map with key-value pairs.
//
static void check_key_tables_consistency();
private:
- std::string m_config_path;
- std::string m_system_config_path;
+ std::filesystem::path m_config_path;
+ std::filesystem::path m_system_config_path;
bool m_absolute_paths_in_stderr = false;
- std::string m_base_dir;
+ std::filesystem::path m_base_dir;
std::string m_cache_dir;
std::string m_compiler;
std::string m_compiler_check = "mtime";
return m_absolute_paths_in_stderr;
}
-inline const std::string&
+inline const std::filesystem::path&
Config::base_dir() const
{
return m_base_dir;
}
inline void
-Config::set_base_dir(const std::string& value)
+Config::set_base_dir(const std::filesystem::path& value)
{
m_base_dir = value;
}
#include "Context.hpp"
#include <ccache/SignalHandler.hpp>
-#include <ccache/Util.hpp>
#include <ccache/hashutil.hpp>
#include <ccache/util/TimePoint.hpp>
#include <ccache/util/file.hpp>
+#include <ccache/util/filesystem.hpp>
#include <ccache/util/logging.hpp>
#include <ccache/util/path.hpp>
#include <ccache/util/process.hpp>
#include <string>
#include <vector>
+namespace fs = util::filesystem;
+
Context::Context()
- : actual_cwd(util::actual_cwd()),
+ : actual_cwd(fs::current_path().value_or("")),
apparent_cwd(util::apparent_cwd(actual_cwd)),
storage(config),
#ifdef INODE_CACHE_SUPPORTED
Config config;
// Current working directory as returned by getcwd(3).
- std::string actual_cwd;
+ std::filesystem::path actual_cwd;
// Current working directory according to $PWD (falling back to getcwd(3)).
- std::string apparent_cwd;
+ std::filesystem::path apparent_cwd;
// The original argument list.
Args orig_args;
#include <ccache/Context.hpp>
#include <ccache/Hash.hpp>
-#include <ccache/Util.hpp>
+#include <ccache/core/common.hpp>
#include <ccache/core/exceptions.hpp>
+#include <ccache/util/PathString.hpp>
#include <ccache/util/Tokenizer.hpp>
#include <ccache/util/assertions.hpp>
#include <ccache/util/file.hpp>
namespace fs = util::filesystem;
+using pstr = util::PathString;
+
namespace Depfile {
std::string
if (token.empty() || token == ":") {
continue;
}
- auto rel_path = Util::make_relative_path(ctx, token);
+ auto rel_path = core::make_relative_path(ctx, token);
if (rel_path != token) {
rewritten = true;
- token = std::move(rel_path);
+ token = pstr(rel_path).str();
}
}
#include <ccache/Config.hpp>
#include <ccache/Hash.hpp>
-#include <ccache/Util.hpp>
#include <ccache/util/DirEntry.hpp>
#include <ccache/util/Fd.hpp>
#include <ccache/util/Finalizer.hpp>
+++ /dev/null
-// Copyright (C) 2019-2024 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#include "Util.hpp"
-
-#include <ccache/Config.hpp>
-#include <ccache/Context.hpp>
-#include <ccache/core/exceptions.hpp>
-#include <ccache/util/DirEntry.hpp>
-#include <ccache/util/expected.hpp>
-#include <ccache/util/file.hpp>
-#include <ccache/util/filesystem.hpp>
-#include <ccache/util/format.hpp>
-#include <ccache/util/logging.hpp>
-#include <ccache/util/path.hpp>
-#include <ccache/util/string.hpp>
-#include <ccache/util/wincompat.hpp>
-
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#include <fcntl.h>
-
-namespace fs = util::filesystem;
-
-using util::DirEntry;
-
-namespace Util {
-
-size_t
-common_dir_prefix_length(std::string_view dir, std::string_view path)
-{
- if (dir.empty() || path.empty() || dir == "/" || path == "/") {
- return 0;
- }
-
- ASSERT(dir[0] == '/');
- ASSERT(path[0] == '/');
-
- const size_t limit = std::min(dir.length(), path.length());
- size_t i = 0;
-
- while (i < limit && dir[i] == path[i]) {
- ++i;
- }
-
- if ((i == dir.length() && i == path.length())
- || (i == dir.length() && path[i] == '/')
- || (i == path.length() && dir[i] == '/')) {
- return i;
- }
-
- do {
- --i;
- } while (i > 0 && dir[i] != '/' && path[i] != '/');
-
- return i;
-}
-
-std::string
-get_relative_path(std::string_view dir, std::string_view path)
-{
- ASSERT(fs::path(dir).is_absolute());
- ASSERT(fs::path(path).is_absolute());
-
-#ifdef _WIN32
- // Paths can be escaped by a slash for use with e.g. -isystem.
- if (dir.length() >= 3 && dir[0] == '/' && dir[2] == ':') {
- dir = dir.substr(1);
- }
- if (path.length() >= 3 && path[0] == '/' && path[2] == ':') {
- path = path.substr(1);
- }
- if (dir[0] != path[0]) {
- // Drive letters differ.
- return std::string(path);
- }
- dir = dir.substr(2);
- path = path.substr(2);
-#endif
-
- std::string result;
- size_t common_prefix_len = Util::common_dir_prefix_length(dir, path);
- if (common_prefix_len > 0 || dir != "/") {
- for (size_t i = common_prefix_len; i < dir.length(); ++i) {
- if (dir[i] == '/') {
- if (!result.empty()) {
- result += '/';
- }
- result += "..";
- }
- }
- }
- if (path.length() > common_prefix_len) {
- if (!result.empty()) {
- result += '/';
- }
- result += std::string(path.substr(common_prefix_len + 1));
- }
- result.erase(result.find_last_not_of('/') + 1);
- return result.empty() ? "." : result;
-}
-
-std::string
-make_relative_path(const std::string& base_dir,
- const std::string& actual_cwd,
- const std::string& apparent_cwd,
- std::string_view path)
-{
- if (base_dir.empty() || !util::path_starts_with(path, base_dir)) {
- return std::string(path);
- }
-
-#ifdef _WIN32
- std::string winpath;
- if (path.length() >= 3 && path[0] == '/') {
- if (isalpha(path[1]) && path[2] == '/') {
- // Transform /c/path... to c:/path...
- winpath = FMT("{}:/{}", path[1], path.substr(3));
- path = winpath;
- } else if (path[2] == ':') {
- // Transform /c:/path to c:/path
- winpath = std::string(path.substr(1));
- path = winpath;
- }
- }
-#endif
-
- // The algorithm for computing relative paths below only works for existing
- // paths. If the path doesn't exist, find the first ancestor directory that
- // does exist and assemble the path again afterwards.
-
- std::vector<std::string> relpath_candidates;
- const auto original_path = path;
- fs::path path_path = path;
- while (!fs::exists(path_path)) {
- path_path = path_path.parent_path();
- }
- std::string path_str = path_path.string();
- path = path_str;
- const auto path_suffix = std::string(original_path.substr(path.length()));
- const fs::path real_path = fs::canonical(path).value_or(fs::path(path));
-
- const auto add_relpath_candidates = [&](auto p) {
- const std::string normalized_path =
- Util::normalize_abstract_absolute_path(p);
- relpath_candidates.push_back(
- Util::get_relative_path(actual_cwd, normalized_path));
- if (apparent_cwd != actual_cwd) {
- relpath_candidates.emplace_back(
- Util::get_relative_path(apparent_cwd, normalized_path));
- }
- };
- add_relpath_candidates(path);
- if (real_path != path) {
- add_relpath_candidates(real_path.string());
- }
-
- // Find best (i.e. shortest existing) match:
- std::sort(relpath_candidates.begin(),
- relpath_candidates.end(),
- [](const auto& path1, const auto& path2) {
- return path1.length() < path2.length();
- });
- for (const auto& relpath : relpath_candidates) {
- if (fs::equivalent(relpath, path_path)) {
- return relpath + path_suffix;
- }
- }
-
- // No match so nothing else to do than to return the unmodified path.
- return std::string(original_path);
-}
-
-std::string
-make_relative_path(const Context& ctx, std::string_view path)
-{
- return make_relative_path(
- ctx.config.base_dir(), ctx.actual_cwd, ctx.apparent_cwd, path);
-}
-
-static std::string
-do_normalize_abstract_absolute_path(std::string_view path)
-{
- if (!fs::path(path).is_absolute()) {
- return std::string(path);
- }
-
-#ifdef _WIN32
- std::string drive(path.substr(0, 2));
- path = path.substr(2);
-#endif
-
- std::string result = "/";
- const size_t npos = std::string_view::npos;
- size_t left = 1;
-
- while (true) {
- if (left >= path.length()) {
- break;
- }
- const auto right = path.find('/', left);
- std::string_view part =
- path.substr(left, right == npos ? npos : right - left);
- if (part == "..") {
- if (result.length() > 1) {
- // "/x/../part" -> "/part"
- result.erase(result.rfind('/', result.length() - 2) + 1);
- } else {
- // "/../part" -> "/part"
- }
- } else if (part == ".") {
- // "/x/." -> "/x"
- } else {
- result.append(part.begin(), part.end());
- if (result[result.length() - 1] != '/') {
- result += '/';
- }
- }
- if (right == npos) {
- break;
- }
- left = right + 1;
- }
- if (result.length() > 1) {
- result.erase(result.find_last_not_of('/') + 1);
- }
-
-#ifdef _WIN32
- return drive + result;
-#else
- return result;
-#endif
-}
-
-std::string
-normalize_abstract_absolute_path(std::string_view path)
-{
-#ifdef _WIN32
- std::string new_path(path);
- std::replace(new_path.begin(), new_path.end(), '\\', '/');
- return do_normalize_abstract_absolute_path(new_path);
-#else
- return do_normalize_abstract_absolute_path(path);
-#endif
-}
-
-std::string
-normalize_concrete_absolute_path(const std::string& path)
-{
- const auto normalized_path = normalize_abstract_absolute_path(path);
- return (normalized_path == path
- || DirEntry(normalized_path).same_inode_as(DirEntry(path)))
- ? normalized_path
- : path;
-}
-
-} // namespace Util
+++ /dev/null
-// Copyright (C) 2019-2024 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include <filesystem>
-#include <string>
-#include <string_view>
-
-class Context;
-
-namespace Util {
-
-// Compute the length of the longest directory path that is common to paths
-// `dir` (a directory) and `path` (any path).
-size_t common_dir_prefix_length(std::string_view dir, std::string_view path);
-
-// Compute a relative path from `dir` (an absolute path to a directory) to
-// `path` (an absolute path). Assumes that both `dir` and `path` are normalized.
-// The algorithm does *not* follow symlinks, so the result may not actually
-// resolve to the same file as `path`.
-std::string get_relative_path(std::string_view dir, std::string_view path);
-
-// Construct a normalized native path.
-//
-// Example:
-//
-// std::string path = Util::make_path("usr", "local", "bin");
-template<typename... T>
-std::string
-make_path(const T&... args)
-{
- return (std::filesystem::path{} / ... / args).lexically_normal().string();
-}
-
-// Make a relative path from current working directory (either `actual_cwd` or
-// `apparent_cwd`) to `path` if `path` is under `base_dir`.
-std::string make_relative_path(const std::string& base_dir,
- const std::string& actual_cwd,
- const std::string& apparent_cwd,
- std::string_view path);
-
-// Like above but with base directory and apparent/actual CWD taken from `ctx`.
-std::string make_relative_path(const Context& ctx, std::string_view path);
-
-// Normalize absolute path `path`, not taking symlinks into account.
-//
-// Normalization here means syntactically removing redundant slashes and
-// resolving "." and ".." parts. The algorithm does however *not* follow
-// symlinks, so the result may not actually resolve to the same filesystem entry
-// as `path` (nor to any existing file system entry for that matter).
-//
-// On Windows: Backslashes are replaced with forward slashes.
-std::string normalize_abstract_absolute_path(std::string_view path);
-
-// Like normalize_abstract_absolute_path, but returns `path` unchanged if the
-// normalized result doesn't resolve to the same file system entry as `path`.
-std::string normalize_concrete_absolute_path(const std::string& path);
-
-} // namespace Util
#include <ccache/Context.hpp>
#include <ccache/Depfile.hpp>
-#include <ccache/Util.hpp>
#include <ccache/compopt.hpp>
+#include <ccache/core/common.hpp>
#include <ccache/language.hpp>
#include <ccache/util/PathString.hpp>
#include <ccache/util/assertions.hpp>
new_profile_path = ".";
} else {
// GCC uses $PWD/$(basename $obj).
- new_profile_path = ctx.apparent_cwd;
+ new_profile_path = pstr(ctx.apparent_cwd).str();
}
} else if (util::starts_with(arg, "-fprofile-generate=")
|| util::starts_with(arg, "-fprofile-instr-generate=")) {
if (state.output_dep_origin <= OutputDepOrigin::mf) {
state.output_dep_origin = OutputDepOrigin::mf;
- args_info.output_dep = Util::make_relative_path(ctx, dep_file);
+ args_info.output_dep =
+ pstr(core::make_relative_path(ctx, dep_file)).str();
}
// Keep the format of the args the same.
if (separate_argument) {
if (util::starts_with(arg, "--sysroot=")) {
auto path = std::string_view(arg).substr(10);
- auto relpath = Util::make_relative_path(ctx, path);
- state.common_args.push_back("--sysroot=" + relpath);
+ auto relpath = core::make_relative_path(ctx, path);
+ state.common_args.push_back(FMT("--sysroot={}", relpath));
return Statistic::none;
}
return Statistic::bad_compiler_arguments;
}
state.common_args.push_back(args[i]);
- auto relpath = Util::make_relative_path(ctx, args[i + 1]);
- state.common_args.push_back(relpath);
+ auto relpath = core::make_relative_path(ctx, args[i + 1]);
+ state.common_args.push_back(pstr(relpath).str());
i++;
return Statistic::none;
}
return Statistic::bad_compiler_arguments;
}
args_info.generating_diagnostics = true;
- args_info.output_dia = Util::make_relative_path(ctx, args[i + 1]);
+ args_info.output_dia =
+ pstr(core::make_relative_path(ctx, args[i + 1])).str();
i++;
return Statistic::none;
}
// Potentially rewrite path argument to relative path to get better hit
// rate. A secondary effect is that paths in the standard error output
// produced by the compiler will be normalized.
- std::string relpath = Util::make_relative_path(ctx, args[i + next]);
+ fs::path relpath = core::make_relative_path(ctx, args[i + next]);
auto& dest_args =
compopt_affects_cpp_output(arg) ? state.cpp_args : state.common_args;
dest_args.push_back(args[i]);
if (next == 2) {
dest_args.push_back(args[i + 1]);
}
- dest_args.push_back(relpath);
+ dest_args.push_back(pstr(relpath).str());
i += next;
return Statistic::none;
const auto [option, path] = util::split_option_with_concat_path(arg);
if (path) {
if (compopt_takes_concat_arg(option) && compopt_takes_path(option)) {
- const auto relpath = Util::make_relative_path(ctx, *path);
+ const auto relpath = core::make_relative_path(ctx, *path);
std::string new_option = FMT("{}{}", option, relpath);
if (compopt_affects_cpp_output(option)) {
state.cpp_args.push_back(std::move(new_option));
args_info.orig_input_file = pstr(state.input_files.front()).str();
// Rewrite to relative to increase hit rate.
args_info.input_file =
- Util::make_relative_path(ctx, args_info.orig_input_file);
- args_info.normalized_input_file =
- Util::normalize_concrete_absolute_path(args_info.input_file);
+ pstr(core::make_relative_path(ctx, args_info.orig_input_file)).str();
// Bail out on too hard combinations of options.
if (state.found_mf_opt && state.found_wp_md_or_mmd_opt) {
}
args_info.orig_output_obj = args_info.output_obj;
- args_info.output_obj = Util::make_relative_path(ctx, args_info.output_obj);
+ args_info.output_obj =
+ pstr(core::make_relative_path(ctx, args_info.output_obj)).str();
// Determine output dependency file.
}
if (args_info.profile_path.empty()) {
- args_info.profile_path = ctx.apparent_cwd;
+ args_info.profile_path = pstr(ctx.apparent_cwd).str();
}
if (!state.explicit_language.empty() && state.explicit_language == "none") {
if (args_info.output_is_precompiled_header && output_obj_by_source) {
args_info.orig_output_obj = args_info.orig_input_file + ".gch";
args_info.output_obj =
- Util::make_relative_path(ctx, args_info.orig_output_obj);
+ pstr(core::make_relative_path(ctx, args_info.orig_output_obj)).str();
}
if (args_info.output_is_precompiled_header
if (args_info.generating_stackusage) {
std::string default_sufile_name =
fs::path(args_info.output_obj).replace_extension(".su").string();
- args_info.output_su = Util::make_relative_path(ctx, default_sufile_name);
+ args_info.output_su =
+ pstr(core::make_relative_path(ctx, default_sufile_name)).str();
}
if (args_info.generating_callgraphinfo) {
std::string default_cifile_name =
fs::path(args_info.output_obj).replace_extension(".ci").string();
- args_info.output_ci = Util::make_relative_path(ctx, default_cifile_name);
+ args_info.output_ci =
+ pstr(core::make_relative_path(ctx, default_cifile_name)).str();
}
Args compiler_args = state.common_args;
#include <ccache/Depfile.hpp>
#include <ccache/Hash.hpp>
#include <ccache/SignalHandler.hpp>
-#include <ccache/Util.hpp>
#include <ccache/argprocessing.hpp>
#include <ccache/compopt.hpp>
#include <ccache/core/CacheEntry.hpp>
return {};
}
- if (path == ctx.args_info.normalized_input_file) {
+ if (fs::path(path) == ctx.args_info.input_file) {
// Don't remember the input file.
return {};
}
// p and q span the include file path.
std::string inc_path(p, q - p);
- auto it = relative_inc_path_cache.find(inc_path);
- if (it == relative_inc_path_cache.end()) {
- auto rel_inc_path = Util::make_relative_path(
- ctx, Util::normalize_concrete_absolute_path(inc_path));
- relative_inc_path_cache.emplace(inc_path, rel_inc_path);
- inc_path = std::move(rel_inc_path);
- } else {
- inc_path = it->second;
+ while (!inc_path.empty() && inc_path.back() == '/') {
+ inc_path.pop_back();
+ }
+ if (!ctx.config.base_dir().empty()) {
+ auto it = relative_inc_path_cache.find(inc_path);
+ if (it == relative_inc_path_cache.end()) {
+ std::string rel_inc_path =
+ pstr(core::make_relative_path(ctx, inc_path)).str();
+ relative_inc_path_cache.emplace(inc_path, rel_inc_path);
+ inc_path = pstr(rel_inc_path).str();
+ } else {
+ inc_path = it->second;
+ }
}
- if ((inc_path != ctx.apparent_cwd) || ctx.config.hash_dir()) {
- hash.hash(inc_path);
+ if (inc_path != ctx.apparent_cwd || ctx.config.hash_dir()) {
+ hash.hash(pstr(inc_path).str());
}
TRY(remember_include_file(ctx, inc_path, hash, system, nullptr));
if (!ctx.args_info.included_pch_file.empty()
&& !ctx.args_info.generating_pch) {
std::string pch_path =
- Util::make_relative_path(ctx, ctx.args_info.included_pch_file);
+ pstr(core::make_relative_path(ctx, ctx.args_info.included_pch_file));
hash.hash(pch_path);
TRY(remember_include_file(ctx, pch_path, hash, false, nullptr));
}
continue;
}
if (seen_colon) {
- std::string path = Util::make_relative_path(ctx, token);
- TRY(remember_include_file(ctx, path, hash, false, &hash));
+ fs::path path = core::make_relative_path(ctx, token);
+ TRY(remember_include_file(ctx, pstr(path), hash, false, &hash));
} else if (token == ":") {
seen_colon = true;
}
// dependencies output.
if (!ctx.args_info.included_pch_file.empty()
&& !ctx.args_info.generating_pch) {
- std::string pch_path =
- Util::make_relative_path(ctx, ctx.args_info.included_pch_file);
- hash.hash(pch_path);
- TRY(remember_include_file(ctx, pch_path, hash, false, nullptr));
+ fs::path pch_path =
+ core::make_relative_path(ctx, ctx.args_info.included_pch_file);
+ hash.hash(pstr(pch_path).str());
+ TRY(remember_include_file(ctx, pstr(pch_path), hash, false, nullptr));
}
bool debug_included = getenv("CCACHE_DEBUG_INCLUDED");
{
for (std::string_view include : core::MsvcShowIncludesOutput::get_includes(
stdout_data, ctx.config.msvc_dep_prefix())) {
- const std::string path = Util::make_relative_path(
- ctx, Util::normalize_abstract_absolute_path(include));
- TRY(remember_include_file(ctx, path, hash, false, &hash));
+ const fs::path path = core::make_relative_path(ctx, include);
+ TRY(remember_include_file(ctx, pstr(path), hash, false, &hash));
}
// Explicitly check the .pch file as it is not mentioned in the
// includes output.
if (!ctx.args_info.included_pch_file.empty()
&& !ctx.args_info.generating_pch) {
- std::string pch_path =
- Util::make_relative_path(ctx, ctx.args_info.included_pch_file);
- hash.hash(pch_path);
- TRY(remember_include_file(ctx, pch_path, hash, false, nullptr));
+ fs::path pch_path =
+ core::make_relative_path(ctx, ctx.args_info.included_pch_file);
+ hash.hash(pstr(pch_path).str());
+ TRY(remember_include_file(ctx, pstr(pch_path), hash, false, nullptr));
}
const bool debug_included = getenv("CCACHE_DEBUG_INCLUDED");
std::string abs_inc_path =
util::replace_first(orig_line, ctx.config.msvc_dep_prefix(), "");
abs_inc_path = util::strip_whitespace(abs_inc_path);
- std::string rel_inc_path = Util::make_relative_path(
- ctx, Util::normalize_concrete_absolute_path(abs_inc_path));
- std::string line_with_rel_inc =
- util::replace_first(orig_line, abs_inc_path, rel_inc_path);
+ fs::path rel_inc_path = core::make_relative_path(ctx, abs_inc_path);
+ std::string line_with_rel_inc = util::replace_first(
+ orig_line, abs_inc_path, pstr(rel_inc_path).str());
new_stdout_data.insert(new_stdout_data.end(),
line_with_rel_inc.data(),
line_with_rel_inc.size());
// Possibly hash the current working directory.
if (args_info.generating_debuginfo && ctx.config.hash_dir()) {
- std::string dir_to_hash = ctx.apparent_cwd;
+ std::string dir_to_hash = pstr(ctx.apparent_cwd).str();
for (const auto& map : args_info.debug_prefix_maps) {
size_t sep_pos = map.find('=');
if (sep_pos != std::string::npos) {
old_path,
new_path,
ctx.apparent_cwd);
- if (util::starts_with(ctx.apparent_cwd, old_path)) {
- dir_to_hash = new_path + ctx.apparent_cwd.substr(old_path.size());
+ if (util::starts_with(pstr(ctx.apparent_cwd), old_path)) {
+ dir_to_hash =
+ new_path + pstr(ctx.apparent_cwd).str().substr(old_path.size());
}
}
}
const std::string output_obj_dir =
fs::path(args_info.output_obj).is_absolute()
? fs::path(args_info.output_obj).parent_path().string()
- : ctx.actual_cwd;
+ : pstr(ctx.actual_cwd);
LOG("Hashing object file directory {}", output_obj_dir);
hash.hash_delimiter("source path");
hash.hash(output_obj_dir);
// the directory in the hash for now.
LOG_RAW("Hashing apparent CWD due to generating a .gcno file");
hash.hash_delimiter("CWD in .gcno");
- hash.hash(ctx.apparent_cwd);
+ hash.hash(pstr(ctx.apparent_cwd).str());
}
// Possibly hash the coverage data file path.
const std::string& profile_path = ctx.args_info.profile_path;
const std::string base_name =
pstr(fs::path(ctx.args_info.output_obj).replace_extension("")).str();
- std::string hashified_cwd = ctx.apparent_cwd;
+ std::string hashified_cwd = pstr(ctx.apparent_cwd).str();
std::replace(hashified_cwd.begin(), hashified_cwd.end(), '/', '#');
std::vector<std::string> paths_to_try{
if (processed.hash_actual_cwd) {
common_hash.hash_delimiter("actual_cwd");
- common_hash.hash(ctx.actual_cwd);
+ common_hash.hash(pstr(ctx.actual_cwd).str());
}
// Try to find the hash using the manifest.
#include "FileRecompressor.hpp"
-#include <ccache/Util.hpp>
#include <ccache/core/AtomicFile.hpp>
#include <ccache/core/CacheEntry.hpp>
#include <ccache/core/exceptions.hpp>
#include "ResultExtractor.hpp"
-#include <ccache/Util.hpp>
#include <ccache/core/exceptions.hpp>
#include <ccache/util/Bytes.hpp>
#include <ccache/util/DirEntry.hpp>
#include "Statistics.hpp"
#include <ccache/Config.hpp>
-#include <ccache/Util.hpp>
+#include <ccache/util/PathString.hpp>
#include <ccache/util/TextTable.hpp>
#include <ccache/util/format.hpp>
#include <ccache/util/logging.hpp>
namespace core {
+using pstr = util::PathString;
using core::Statistic;
const unsigned FLAG_NOZERO = 1U << 0; // don't zero with --zero-stats
if (verbosity > 0 && !from_log) {
table.add_row({"Cache directory:", C(config.cache_dir()).colspan(4)});
- table.add_row({"Config file:", C(config.config_path()).colspan(4)});
table.add_row(
- {"System config file:", C(config.system_config_path()).colspan(4)});
+ {"Config file:", C(pstr(config.config_path()).str()).colspan(4)});
+ table.add_row({"System config file:",
+ C(pstr(config.system_config_path()).str()).colspan(4)});
table.add_row(
{"Stats updated:", C(format_timestamp(last_updated)).colspan(4)});
if (verbosity > 1) {
}
}
+std::filesystem::path
+make_relative_path(const Context& ctx, const std::filesystem::path& path)
+{
+ if (!ctx.config.base_dir().empty() && path.is_absolute()
+ && util::path_starts_with(path, ctx.config.base_dir())) {
+ return util::make_relative_path(ctx.actual_cwd, ctx.apparent_cwd, path);
+ } else {
+ return path;
+ }
+}
+
std::string
rewrite_stderr_to_absolute_paths(std::string_view text)
{
-// Copyright (C) 2023 Joel Rosdahl and other contributors
+// Copyright (C) 2023-2024 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
// Like std::filesystem::create_directories but throws core::Fatal on error.
void ensure_dir_exists(const std::filesystem::path& dir);
+// Make a `path` relative to CWD if it's under base_dir.
+std::filesystem::path make_relative_path(const Context& ctx,
+ const std::filesystem::path& path);
+
// Rewrite path to absolute path in `text` in the following two cases, where X
// may be optional ANSI CSI sequences:
//
#include <ccache/Hash.hpp>
#include <ccache/InodeCache.hpp>
#include <ccache/ProgressBar.hpp>
-#include <ccache/Util.hpp>
#include <ccache/ccache.hpp>
#include <ccache/core/CacheEntry.hpp>
#include <ccache/core/FileRecompressor.hpp>
case 'F': { // --max-files
auto files = util::value_or_throw<Error>(util::parse_unsigned(arg));
- config.set_value_in_file(config.config_path(), "max_files", arg);
+ config.set_value_in_file(
+ pstr(config.config_path()).str(), "max_files", arg);
if (files == 0) {
PRINT_RAW(stdout, "Unset cache file limit\n");
} else {
auto [size, suffix_type] =
util::value_or_throw<Error>(util::parse_size(arg));
uint64_t max_size = size;
- config.set_value_in_file(config.config_path(), "max_size", arg);
+ config.set_value_in_file(
+ pstr(config.config_path()).str(), "max_size", arg);
if (max_size == 0) {
PRINT_RAW(stdout, "Unset cache size limit\n");
} else {
}
std::string key = arg.substr(0, eq_pos);
std::string value = arg.substr(eq_pos + 1);
- config.set_value_in_file(config.config_path(), key, value);
+ config.set_value_in_file(pstr(config.config_path()).str(), key, value);
break;
}
#include <ccache/Config.hpp>
#include <ccache/Context.hpp>
#include <ccache/SignalHandler.hpp>
-#include <ccache/Util.hpp>
#include <ccache/ccache.hpp>
#include <ccache/core/exceptions.hpp>
#include <ccache/util/DirEntry.hpp>
#include "Storage.hpp"
#include <ccache/Config.hpp>
-#include <ccache/Util.hpp>
#include <ccache/core/CacheEntry.hpp>
#include <ccache/core/Statistic.hpp>
#include <ccache/core/exceptions.hpp>
#include "util.hpp"
-#include <ccache/Util.hpp>
#include <ccache/core/exceptions.hpp>
#include <ccache/util/PathString.hpp>
#include <ccache/util/expected.hpp>
#include "FileStorage.hpp"
-#include <ccache/Util.hpp>
#include <ccache/core/AtomicFile.hpp>
#include <ccache/core/exceptions.hpp>
#include <ccache/util/Bytes.hpp>
#include "LockFile.hpp"
-#include <ccache/Util.hpp>
#include <ccache/util/DirEntry.hpp>
#include <ccache/util/PathString.hpp>
#include <ccache/util/assertions.hpp>
#include "LongLivedLockFileManager.hpp"
-#include <ccache/Util.hpp>
#include <ccache/util/file.hpp>
#include <ccache/util/filesystem.hpp>
#include <ccache/util/logging.hpp>
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#include <ccache/Util.hpp>
#include <ccache/util/assertions.hpp>
#include <ccache/util/filesystem.hpp>
#include <ccache/util/format.hpp>
#include "path.hpp"
-#include <ccache/Util.hpp>
#include <ccache/util/DirEntry.hpp>
+#include <ccache/util/PathString.hpp>
#include <ccache/util/filesystem.hpp>
#include <ccache/util/format.hpp>
#include <ccache/util/string.hpp>
namespace fs = util::filesystem;
-namespace util {
+using pstr = util::PathString;
-std::string
-actual_cwd()
-{
- auto cwd = fs::current_path();
- if (!cwd) {
- return {};
- }
- auto cwd_str = cwd->string();
-#ifdef _WIN32
- std::replace(cwd_str.begin(), cwd_str.end(), '\\', '/');
-#endif
- return cwd_str;
-}
+namespace util {
std::string
add_exe_suffix(const std::string& program)
}
}
-std::string
-apparent_cwd(const std::string& actual_cwd)
+fs::path
+apparent_cwd(const fs::path& actual_cwd)
{
#ifdef _WIN32
return actual_cwd;
DirEntry pwd_de(pwd);
DirEntry cwd_de(actual_cwd);
- return !pwd_de || !cwd_de || !pwd_de.same_inode_as(cwd_de)
- ? actual_cwd
- : Util::normalize_concrete_absolute_path(pwd);
+ return !pwd_de || !cwd_de || !pwd_de.same_inode_as(cwd_de) ? actual_cwd : pwd;
#endif
}
return k_dev_null_path;
}
-bool
-path_starts_with(std::string_view path, std::string_view prefix)
+std::filesystem::path
+make_relative_path(const fs::path& actual_cwd,
+ const fs::path& apparent_cwd,
+ const fs::path& path)
{
- if (path.empty()) {
- return false;
- }
- for (size_t i = 0, j = 0; i < path.length() && j < prefix.length();
- ++i, ++j) {
-#ifdef _WIN32
- // Skip escaped backslashes \\\\ as seen by the preprocessor.
- if (i > 0 && path[i] == '\\' && path[i - 1] == '\\') {
- ++i;
- }
- if (j > 0 && prefix[j] == '\\' && prefix[j - 1] == '\\') {
- ++j;
- }
+ DEBUG_ASSERT(actual_cwd.is_absolute());
+ DEBUG_ASSERT(apparent_cwd.is_absolute());
+ DEBUG_ASSERT(path.is_absolute());
- // Handle back and forward slashes as equal.
- if (path[i] == '/' && prefix[j] == '\\') {
- continue;
+ fs::path normalized_path = path.lexically_normal();
+ fs::path closest_existing_path = normalized_path;
+ std::vector<fs::path> relpath_candidates;
+ fs::path path_suffix;
+ while (!fs::exists(closest_existing_path)) {
+ if (path_suffix.empty()) {
+ path_suffix = closest_existing_path.filename();
+ } else {
+ path_suffix = closest_existing_path.filename() / path_suffix;
}
- if (path[i] == '\\' && prefix[j] == '/') {
- continue;
+ closest_existing_path = closest_existing_path.parent_path();
+ }
+
+ const auto add_relpath_candidates = [&](auto p) {
+ relpath_candidates.push_back(p.lexically_relative(actual_cwd));
+ if (apparent_cwd != actual_cwd) {
+ relpath_candidates.emplace_back(p.lexically_relative(apparent_cwd));
}
- if (std::tolower(path[i]) != std::tolower(prefix[j])) {
- return false;
+ };
+
+ add_relpath_candidates(closest_existing_path);
+ const fs::path real_closest_existing_path =
+ fs::canonical(closest_existing_path).value_or(closest_existing_path);
+ if (real_closest_existing_path != closest_existing_path) {
+ add_relpath_candidates(real_closest_existing_path);
+ }
+
+ // Find best (i.e. shortest existing) match:
+ std::sort(relpath_candidates.begin(),
+ relpath_candidates.end(),
+ [](const auto& path1, const auto& path2) {
+ return pstr(path1).str().length() < pstr(path2).str().length();
+ });
+ for (const auto& relpath : relpath_candidates) {
+ if (fs::equivalent(relpath, closest_existing_path)) {
+ return path_suffix.empty() ? relpath
+ : (relpath / path_suffix).lexically_normal();
}
+ }
+
+ // No match so nothing else to do than to return the unmodified path.
+ return path;
+}
+
+bool
+path_starts_with(const fs::path& path, const fs::path& prefix)
+{
+#ifdef _WIN32
+ // Note: Not all paths on Windows are case insensitive, but for our purposes
+ // (checking whether a path is below the base directory) users will expect
+ // them to be.
+ fs::path p1 = util::to_lowercase(path.string());
+ fs::path p2 = util::to_lowercase(prefix.string());
#else
- if (path[i] != prefix[j]) {
- return false;
- }
+ const fs::path& p1 = path;
+ const fs::path& p2 = prefix;
#endif
- }
- return true;
+ return std::mismatch(p1.begin(), p1.end(), p2.begin(), p2.end()).second
+ == p2.end();
}
} // namespace util
// --- Interface ---
-// Return current working directory (CWD) as returned from getcwd(3) (i.e.,
-// normalized path without symlink parts). Returns the empty string on error.
-std::string actual_cwd();
-
// Add ".exe" suffix to `program` if it doesn't already end with ".exe", ".bat"
// or ".sh".
std::string add_exe_suffix(const std::string& program);
// PWD (thus keeping any symlink parts in the path and potentially ".." or "//"
// parts). If PWD does not resolve to the same inode as `actual_cwd` then
// `actual_cwd` is returned instead.
-std::string apparent_cwd(const std::string& actual_cwd);
+std::filesystem::path apparent_cwd(const std::filesystem::path& actual_cwd);
const char* get_dev_null_path();
// Return whether `path` includes at least one directory separator.
bool is_full_path(std::string_view path);
+// Make a relative path from current working directory (either `actual_cwd` or
+// `apparent_cwd`) to `path` if `path` is under `base_dir`.
+std::filesystem::path
+make_relative_path(const std::filesystem::path& actual_cwd,
+ const std::filesystem::path& apparent_cwd,
+ const std::filesystem::path& path);
+
+// Construct a normalized native path.
+//
+// Example:
+//
+// std::string path = make_path("usr", "local", "bin");
+template<typename... T>
+std::filesystem::path
+make_path(const T&... args)
+{
+ return (std::filesystem::path{} / ... / args).lexically_normal();
+}
+
// Return whether `path` starts with `prefix` considering path specifics on
-// Windows
-bool path_starts_with(std::string_view path, std::string_view prefix);
+// Windows.
+bool path_starts_with(const std::filesystem::path& path,
+ const std::filesystem::path& prefix);
// --- Inline implementations ---
# A simple test suite for ccache.
#
# Copyright (C) 2002-2007 Andrew Tridgell
-# Copyright (C) 2009-2023 Joel Rosdahl and other contributors
+# Copyright (C) 2009-2024 Joel Rosdahl and other contributors
#
# See doc/AUTHORS.adoc for a complete list of contributors.
#
# -------------------------------------------------------------------------
TEST "Dependency file paths converted to relative if CCACHE_BASEDIR specified"
- CCACHE_DEPEND=1 CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE $DEPSFLAGS_CCACHE -c "`pwd`/test.c"
- if grep -q "[^.]/test.c" "test.d"; then
+ CCACHE_DEPEND=1 CCACHE_BASEDIR="$(pwd)" $CCACHE_COMPILE $DEPSFLAGS_CCACHE -c "$(pwd)/test.c"
+ if ! grep -q " test.c" test.d; then
test_failed "Dependency file does not contain relative path to test.c"
fi
test_Config.cpp
test_Depfile.cpp
test_Hash.cpp
- test_Util.cpp
test_argprocessing.cpp
test_ccache.cpp
test_compopt.cpp
#endif
util::unsetenv("GCC_COLORS"); // Don't confuse argument processing tests.
- std::string dir_before = util::actual_cwd();
+ auto dir_before = *fs::current_path();
std::string testdir = FMT("testdir/{}", getpid());
fs::remove_all(testdir);
fs::create_directories(testdir);
#include "TestUtil.hpp"
#include <ccache/Config.hpp>
-#include <ccache/Util.hpp>
#include <ccache/core/exceptions.hpp>
#include <ccache/util/environment.hpp>
#include <ccache/util/file.hpp>
#ifndef _WIN32
"base_dir = /bd\n"
#else
- "base_dir = C:/bd\n"
+ "base_dir = C:\\bd\n"
#endif
"cache_dir = cd\n"
"compiler = c\n"
#ifndef _WIN32
"(test.conf) base_dir = /bd",
#else
- "(test.conf) base_dir = C:/bd",
+ "(test.conf) base_dir = C:\\bd",
#endif
"(test.conf) cache_dir = cd",
"(test.conf) compiler = c",
const auto expected = FMT(
"{0}/foo.o: \\\n"
" bar.c \\\n"
- " ./bar/bar.h \\\n"
+ " {2} \\\n"
" {1}/fie.h\n"
- "./bar/bar.h:\n"
+ "{2}:\n"
"{1}/fie.h:\n",
Depfile::escape_filename(pstr(cwd).str()),
- Depfile::escape_filename(pstr(cwd.parent_path()).str()));
+ Depfile::escape_filename(pstr(cwd.parent_path()).str()),
+ Depfile::escape_filename(
+ pstr(fs::path("bar/bar.h").lexically_normal()).str()));
REQUIRE(actual);
CHECK(*actual == expected);
}
#include <ccache/Hash.hpp>
#include <ccache/InodeCache.hpp>
#include <ccache/util/Fd.hpp>
+#include <ccache/util/PathString.hpp>
#include <ccache/util/TemporaryFile.hpp>
#include <ccache/util/file.hpp>
#include <ccache/util/filesystem.hpp>
namespace fs = util::filesystem;
using TestUtil::TestContext;
+using pstr = util::PathString;
namespace {
bool
inode_cache_available()
{
- auto tmp_file = util::TemporaryFile::create(util::actual_cwd() + "/fs_test");
+ auto tmp_file =
+ util::TemporaryFile::create((*fs::current_path()) / "fs_test");
if (!tmp_file) {
return false;
}
{
config.set_debug(true);
config.set_inode_cache(true);
- config.set_temporary_dir(util::actual_cwd());
+ config.set_temporary_dir(pstr(*fs::current_path()).str());
}
bool
+++ /dev/null
-// Copyright (C) 2019-2024 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#include "TestUtil.hpp"
-
-#include <ccache/Config.hpp>
-#include <ccache/Util.hpp>
-#include <ccache/core/exceptions.hpp>
-#include <ccache/util/environment.hpp>
-#include <ccache/util/file.hpp>
-#include <ccache/util/filesystem.hpp>
-#include <ccache/util/format.hpp>
-#include <ccache/util/path.hpp>
-#include <ccache/util/wincompat.hpp>
-
-#include <doctest/doctest.h>
-
-#include <fcntl.h>
-
-#include <optional>
-#include <string>
-#include <vector>
-
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#include <algorithm>
-
-using TestUtil::TestContext;
-
-namespace fs = util::filesystem;
-
-TEST_SUITE_BEGIN("Util");
-
-TEST_CASE("Util::common_dir_prefix_length")
-{
- CHECK(Util::common_dir_prefix_length("", "") == 0);
- CHECK(Util::common_dir_prefix_length("/", "") == 0);
- CHECK(Util::common_dir_prefix_length("", "/") == 0);
- CHECK(Util::common_dir_prefix_length("/", "/") == 0);
- CHECK(Util::common_dir_prefix_length("/", "/b") == 0);
- CHECK(Util::common_dir_prefix_length("/a", "/") == 0);
- CHECK(Util::common_dir_prefix_length("/a", "/b") == 0);
- CHECK(Util::common_dir_prefix_length("/a", "/a") == 2);
- CHECK(Util::common_dir_prefix_length("/a", "/a/b") == 2);
- CHECK(Util::common_dir_prefix_length("/a/b", "/a") == 2);
- CHECK(Util::common_dir_prefix_length("/a/b", "/a/c") == 2);
- CHECK(Util::common_dir_prefix_length("/a/b", "/a/b") == 4);
- CHECK(Util::common_dir_prefix_length("/a/bc", "/a/b") == 2);
- CHECK(Util::common_dir_prefix_length("/a/b", "/a/bc") == 2);
-}
-
-TEST_CASE("Util::get_relative_path")
-{
-#ifdef _WIN32
- CHECK(Util::get_relative_path("C:/a", "C:/a") == ".");
- CHECK(Util::get_relative_path("C:/a", "Z:/a") == "Z:/a");
- CHECK(Util::get_relative_path("C:/a/b", "C:/a") == "..");
- CHECK(Util::get_relative_path("C:/a", "C:/a/b") == "b");
- CHECK(Util::get_relative_path("C:/a", "C:/a/b/c") == "b/c");
- CHECK(Util::get_relative_path("C:/a/b", "C:/a/c") == "../c");
- CHECK(Util::get_relative_path("C:/a/b", "C:/a/c/d") == "../c/d");
- CHECK(Util::get_relative_path("C:/a/b/c", "C:/a/c/d") == "../../c/d");
- CHECK(Util::get_relative_path("C:/a/b", "C:/") == "../..");
- CHECK(Util::get_relative_path("C:/a/b", "C:/c") == "../../c");
- CHECK(Util::get_relative_path("C:/", "C:/a/b") == "a/b");
- CHECK(Util::get_relative_path("C:/a", "D:/a/b") == "D:/a/b");
-#else
- CHECK(Util::get_relative_path("/a", "/a") == ".");
- CHECK(Util::get_relative_path("/a/b", "/a") == "..");
- CHECK(Util::get_relative_path("/a", "/a/b") == "b");
- CHECK(Util::get_relative_path("/a", "/a/b/c") == "b/c");
- CHECK(Util::get_relative_path("/a/b", "/a/c") == "../c");
- CHECK(Util::get_relative_path("/a/b", "/a/c/d") == "../c/d");
- CHECK(Util::get_relative_path("/a/b/c", "/a/c/d") == "../../c/d");
- CHECK(Util::get_relative_path("/a/b", "/") == "../..");
- CHECK(Util::get_relative_path("/a/b", "/c") == "../../c");
- CHECK(Util::get_relative_path("/", "/a/b") == "a/b");
-#endif
-}
-
-TEST_CASE("Util::make_relative_path")
-{
- using Util::make_relative_path;
-
- const TestContext test_context;
-
- const std::string cwd = util::actual_cwd();
- const std::string actual_cwd = FMT("{}/d", cwd);
-#if defined(_WIN32) || defined(__CYGWIN__)
- const std::string apparent_cwd = actual_cwd;
-#else
- const std::string apparent_cwd = FMT("{}/s", cwd);
-#endif
-
- REQUIRE(fs::create_directory("d"));
-#ifndef _WIN32
- REQUIRE(symlink("d", "s") == 0);
-#endif
- REQUIRE(fs::current_path("d"));
- util::setenv("PWD", apparent_cwd);
- SUBCASE("No base directory")
- {
- CHECK(make_relative_path("", "/a", "/a", "/a/x") == "/a/x");
- }
-
- SUBCASE("Path matches neither actual nor apparent CWD")
- {
-#ifdef _WIN32
- CHECK(make_relative_path("C:/", "C:/a", "C:/b", "C:/x") == "C:/x");
-#else
- CHECK(make_relative_path("/", "/a", "/b", "/x") == "/x");
-#endif
- }
-
- SUBCASE("Match of actual CWD")
- {
-#ifdef _WIN32
- CHECK(
- make_relative_path(
- actual_cwd.substr(0, 3), actual_cwd, apparent_cwd, actual_cwd + "/x")
- == "./x");
- CHECK(
- make_relative_path(
- actual_cwd.substr(0, 3), actual_cwd, apparent_cwd, actual_cwd + "\\x")
- == ".\\x");
- CHECK(
- make_relative_path(
- actual_cwd.substr(0, 3), actual_cwd, apparent_cwd, actual_cwd + "\\\\x")
- == ".\\\\x");
-#else
- CHECK(make_relative_path("/", actual_cwd, apparent_cwd, actual_cwd + "/x")
- == "./x");
-#endif
- }
-
-#ifndef _WIN32
- SUBCASE("Match of apparent CWD")
- {
- CHECK(make_relative_path("/", actual_cwd, apparent_cwd, apparent_cwd + "/x")
- == "./x");
- }
-
- SUBCASE("Match if using resolved (using realpath(3)) path")
- {
- CHECK(make_relative_path("/", actual_cwd, actual_cwd, apparent_cwd + "/x")
- == "./x");
- }
-#endif
-}
-
-TEST_CASE("Util::normalize_abstract_absolute_path")
-{
- CHECK(Util::normalize_abstract_absolute_path("") == "");
- CHECK(Util::normalize_abstract_absolute_path(".") == ".");
- CHECK(Util::normalize_abstract_absolute_path("..") == "..");
- CHECK(Util::normalize_abstract_absolute_path("...") == "...");
- CHECK(Util::normalize_abstract_absolute_path("x/./") == "x/./");
-
-#ifdef _WIN32
- CHECK(Util::normalize_abstract_absolute_path("c:/") == "c:/");
- CHECK(Util::normalize_abstract_absolute_path("c:\\") == "c:/");
- CHECK(Util::normalize_abstract_absolute_path("c:/.") == "c:/");
- CHECK(Util::normalize_abstract_absolute_path("c:\\..") == "c:/");
- CHECK(Util::normalize_abstract_absolute_path("c:\\x/..") == "c:/");
- CHECK(Util::normalize_abstract_absolute_path("c:\\x/./y\\..\\\\z")
- == "c:/x/z");
-#else
- CHECK(Util::normalize_abstract_absolute_path("/") == "/");
- CHECK(Util::normalize_abstract_absolute_path("/.") == "/");
- CHECK(Util::normalize_abstract_absolute_path("/..") == "/");
- CHECK(Util::normalize_abstract_absolute_path("/./") == "/");
- CHECK(Util::normalize_abstract_absolute_path("//") == "/");
- CHECK(Util::normalize_abstract_absolute_path("/../x") == "/x");
- CHECK(Util::normalize_abstract_absolute_path("/x/./y/z") == "/x/y/z");
- CHECK(Util::normalize_abstract_absolute_path("/x/../y/z/") == "/y/z");
- CHECK(Util::normalize_abstract_absolute_path("/x/.../y/z") == "/x/.../y/z");
- CHECK(Util::normalize_abstract_absolute_path("/x/yyy/../zz") == "/x/zz");
- CHECK(Util::normalize_abstract_absolute_path("//x/yyy///.././zz") == "/x/zz");
-#endif
-}
-
-TEST_CASE("Util::normalize_concrete_absolute_path")
-{
-#if !defined(_WIN32) && !defined(__CYGWIN__)
- TestContext test_context;
-
- util::write_file("file", "");
- REQUIRE(fs::create_directories("dir1/dir2"));
- REQUIRE(symlink("dir1/dir2", "symlink") == 0);
- const auto cwd = util::actual_cwd();
-
- CHECK(Util::normalize_concrete_absolute_path(FMT("{}/file", cwd))
- == FMT("{}/file", cwd));
- CHECK(Util::normalize_concrete_absolute_path(FMT("{}/dir1/../file", cwd))
- == FMT("{}/file", cwd));
- CHECK(Util::normalize_concrete_absolute_path(FMT("{}/symlink/../file", cwd))
- == FMT("{}/symlink/../file", cwd));
-#endif
-}
-
-TEST_SUITE_END();
#include <ccache/Args.hpp>
#include <ccache/Config.hpp>
#include <ccache/Context.hpp>
-#include <ccache/Util.hpp>
#include <ccache/argprocessing.hpp>
#include <ccache/core/Statistic.hpp>
+#include <ccache/util/PathString.hpp>
#include <ccache/util/file.hpp>
+#include <ccache/util/filesystem.hpp>
#include <ccache/util/format.hpp>
#include <ccache/util/path.hpp>
#include <ccache/util/string.hpp>
#include <algorithm>
+namespace fs = util::filesystem;
+
using core::Statistic;
using TestUtil::TestContext;
+using pstr = util::PathString;
namespace {
return "/";
#else
char volume[4]; // "C:\"
- GetVolumePathName(util::actual_cwd().c_str(), volume, sizeof(volume));
- volume[2] = '/'; // Since base directory is normalized to forward slashes
+ GetVolumePathName(pstr(*fs::current_path()).c_str(), volume, sizeof(volume));
return volume;
#endif
}
-std::string
-get_posix_path(const std::string& path)
-{
-#ifndef _WIN32
- return path;
-#else
- std::string posix;
-
- // /-escape volume.
- if (path[1] == ':'
- && ((path[0] >= 'A' && path[0] <= 'Z')
- || (path[0] >= 'a' && path[0] <= 'z'))) {
- posix = "/" + path;
- } else {
- posix = path;
- }
- // Convert slashes.
- std::replace(posix.begin(), posix.end(), '\\', '/');
- return posix;
-#endif
-}
-
} // namespace
TEST_SUITE_BEGIN("argprocessing");
const ProcessArgsResult result = process_args(ctx);
CHECK(!result.error);
- CHECK(result.preprocessor_args[1] == "--sysroot=./foo/bar");
+#ifdef _WIN32
+ CHECK(result.preprocessor_args[1] == "--sysroot=foo\\bar");
+#else
+ CHECK(result.preprocessor_args[1] == "--sysroot=foo/bar");
+#endif
}
TEST_CASE(
const ProcessArgsResult result = process_args(ctx);
CHECK(!result.error);
CHECK(result.preprocessor_args[1] == "--sysroot");
- CHECK(result.preprocessor_args[2] == "./foo");
+ CHECK(result.preprocessor_args[2] == "foo");
}
TEST_CASE("MF_flag_with_immediate_argument_should_work_as_last_argument")
const ProcessArgsResult result = process_args(ctx);
CHECK(!result.error);
- CHECK(result.preprocessor_args[2] == "./foo");
+ CHECK(result.preprocessor_args[2] == "foo");
}
+#ifndef _WIN32
TEST_CASE("isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
{
TestContext test_context;
Context ctx;
util::write_file("foo.c", "");
- ctx.config.set_base_dir("/"); // posix
- // Windows path doesn't work concatenated.
- std::string cwd = get_posix_path(ctx.actual_cwd);
+ ctx.config.set_base_dir("/");
+ std::string cwd = ctx.actual_cwd;
std::string arg_string = FMT("cc -isystem{}/foo -c foo.c", cwd);
ctx.orig_args = Args::from_string(arg_string);
const ProcessArgsResult result = process_args(ctx);
CHECK(!result.error);
- CHECK(result.preprocessor_args[1] == "-isystem./foo");
+ CHECK(result.preprocessor_args[1] == "-isystemfoo");
}
TEST_CASE("I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
Context ctx;
util::write_file("foo.c", "");
- ctx.config.set_base_dir("/"); // posix
- // Windows path doesn't work concatenated.
- std::string cwd = get_posix_path(ctx.actual_cwd);
+ ctx.config.set_base_dir("/");
+ std::string cwd = *fs::current_path();
std::string arg_string = FMT("cc -I{}/foo -c foo.c", cwd);
ctx.orig_args = Args::from_string(arg_string);
const ProcessArgsResult result = process_args(ctx);
CHECK(!result.error);
- CHECK(result.preprocessor_args[1] == "-I./foo");
+ CHECK(result.preprocessor_args[1] == "-Ifoo");
}
+#endif // _WIN32
TEST_CASE("debug_flag_order_with_known_option_first")
{
// MSVC's /U option, so disable the test case there. This will be possible to
// improve when/if a compiler abstraction is introduced (issue #956).
TEST_CASE("MSVC options"
- * doctest::skip(util::starts_with(util::actual_cwd(), "/U")))
+ * doctest::skip(util::starts_with(fs::current_path()->string(),
+ "/U")))
{
TestContext test_context;
Context ctx;
#ifndef _WIN32
SUBCASE("Follow symlink to actual compiler")
{
- const auto cwd = fs::path(util::actual_cwd());
+ const auto cwd = *fs::current_path();
util::write_file(cwd / "gcc", "");
CHECK(fs::create_symlink("gcc", cwd / "intermediate"));
const auto cc = cwd / "cc";
SUBCASE("Classify clang-cl symlink to clang")
{
- const auto cwd = fs::path(util::actual_cwd());
+ const auto cwd = *fs::current_path();
util::write_file(cwd / "clang", "");
const auto clang_cl = cwd / "clang-cl";
CHECK(fs::create_symlink("clang", clang_cl));
#include "TestUtil.hpp"
-#include <ccache/Util.hpp>
#include <ccache/core/StatsLog.hpp>
#include <ccache/util/file.hpp>
#include "TestUtil.hpp"
-#include <ccache/Util.hpp>
#include <ccache/core/Statistic.hpp>
#include <ccache/storage/local/StatsFile.hpp>
#include <ccache/util/file.hpp>
#include "TestUtil.hpp"
-#include <ccache/Util.hpp>
#include <ccache/util/DirEntry.hpp>
#include <ccache/util/LockFile.hpp>
#include <ccache/util/file.hpp>
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#include <ccache/Util.hpp>
#include <ccache/util/string.hpp>
#include <doctest/doctest.h>
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#include <ccache/Util.hpp>
+#include "TestUtil.hpp"
+
+#include <ccache/util/PathString.hpp>
+#include <ccache/util/environment.hpp>
+#include <ccache/util/filesystem.hpp>
#include <ccache/util/format.hpp>
#include <ccache/util/path.hpp>
#include <ostream> // https://github.com/doctest/doctest/issues/618
+namespace fs = util::filesystem;
+
+using pstr = util::PathString;
+using TestUtil::TestContext;
+
TEST_CASE("util::add_exe_suffix")
{
CHECK(util::add_exe_suffix("foo") == "foo.exe");
#endif
}
+TEST_CASE("util::make_relative_path")
+{
+ using util::make_relative_path;
+
+ const TestContext test_context;
+
+ const std::string cwd = pstr(*fs::current_path()).str();
+ const std::string actual_cwd = FMT("{}/d", cwd);
+#if defined(_WIN32) || defined(__CYGWIN__)
+ const std::string apparent_cwd = actual_cwd;
+#else
+ const std::string apparent_cwd = FMT("{}/s", cwd);
+#endif
+
+ REQUIRE(fs::create_directory("d"));
+#ifndef _WIN32
+ REQUIRE(fs::create_symlink("d", "s"));
+#endif
+ REQUIRE(fs::current_path("d"));
+ util::setenv("PWD", apparent_cwd);
+
+ SUBCASE("Path matches neither actual nor apparent CWD")
+ {
+#ifdef _WIN32
+ CHECK(make_relative_path("C:/a", "C:/b", "C:/x") == "C:/x");
+#else
+ CHECK(make_relative_path("/a", "/b", "/x") == "/x");
+#endif
+ }
+
+ SUBCASE("Match of actual CWD")
+ {
+ CHECK(make_relative_path(actual_cwd, apparent_cwd, actual_cwd + "/x")
+ == "x");
+#ifdef _WIN32
+ CHECK(make_relative_path(actual_cwd, apparent_cwd, actual_cwd + "\\x")
+ == "x");
+ CHECK(make_relative_path(actual_cwd, apparent_cwd, actual_cwd + "\\\\x")
+ == "x");
+#endif
+ }
+
+#ifndef _WIN32
+ SUBCASE("Match of apparent CWD")
+ {
+ CHECK(make_relative_path(actual_cwd, apparent_cwd, apparent_cwd + "/x")
+ == "x");
+ }
+
+ SUBCASE("Match if using resolved (using realpath(3)) path")
+ {
+ CHECK(make_relative_path(actual_cwd, actual_cwd, apparent_cwd + "/x")
+ == "x");
+ }
+#endif
+}
+
TEST_CASE("util::path_starts_with")
{
- CHECK(!util::path_starts_with("", ""));
+ CHECK(util::path_starts_with("", ""));
CHECK(!util::path_starts_with("", "/"));
CHECK(util::path_starts_with("/foo/bar", "/foo"));
CHECK(!util::path_starts_with("/batz/bar", "/foo"));