environment variable name is indicated in parentheses after each configuration
setting key.
+[[config_absolute_paths_in_stderr]] *absolute_paths_in_stderr* (*CCACHE_ABSSTDERR*)::
+
+ This setting specifies whether ccache should rewrite relative paths in the
+ compiler's standard error output to absolute paths. This can be useful if
+ you use <<config_base_dir,*base_dir*>> with a build system (e.g. CMake with
+ the "Unix Makefiles" generator) that executes the compiler in a different
+ working directory, which makes relative paths in compiler errors or
+ warnings incorrect. The default is false.
+
[[config_base_dir]] *base_dir* (*CCACHE_BASEDIR*)::
This setting should be an absolute path to a directory. ccache then
+
See also the discussion under <<_compiling_in_different_directories,COMPILING
IN DIFFERENT DIRECTORIES>>.
++
+You may also want to enable the
+<<config_absolute_paths_in_stderr,*absolute_paths_in_stderr*>> option.
[[config_cache_dir]] *cache_dir* (*CCACHE_DIR*)::
namespace {
enum class ConfigItem {
+ absolute_paths_in_stderr,
base_dir,
cache_dir,
cache_dir_levels,
};
const std::unordered_map<std::string, ConfigItem> k_config_key_table = {
+ {"absolute_paths_in_stderr", ConfigItem::absolute_paths_in_stderr},
{"base_dir", ConfigItem::base_dir},
{"cache_dir", ConfigItem::cache_dir},
{"cache_dir_levels", ConfigItem::cache_dir_levels},
};
const std::unordered_map<std::string, std::string> k_env_variable_table = {
+ {"ABSSTDERR", "absolute_paths_in_stderr"},
{"BASEDIR", "base_dir"},
{"CC", "compiler"}, // Alias for CCACHE_COMPILER
{"COMMENTS", "keep_comments_cpp"},
}
switch (it->second) {
+ case ConfigItem::absolute_paths_in_stderr:
+ return format_bool(m_absolute_paths_in_stderr);
+
case ConfigItem::base_dir:
return m_base_dir;
}
switch (it->second) {
+ case ConfigItem::absolute_paths_in_stderr:
+ m_absolute_paths_in_stderr = parse_bool(value, env_var_key, negate);
+ break;
+
case ConfigItem::base_dir:
m_base_dir = Util::expand_environment_variables(value);
if (!m_base_dir.empty()) { // The empty string means "disable"
Config(Config&) = default;
Config& operator=(const Config&) = default;
+ bool absolute_paths_in_stderr() const;
const std::string& base_dir() const;
const std::string& cache_dir() const;
uint32_t cache_dir_levels() const;
std::string m_primary_config_path;
std::string m_secondary_config_path;
+ bool m_absolute_paths_in_stderr = false;
std::string m_base_dir = "";
std::string m_cache_dir;
uint32_t m_cache_dir_levels = 2;
static std::string default_temporary_dir(const std::string& cache_dir);
};
+inline bool
+Config::absolute_paths_in_stderr() const
+{
+ return m_absolute_paths_in_stderr;
+}
+
inline const std::string&
Config::base_dir() const
{
return result;
}
+std::string
+rewrite_stderr_to_absolute_paths(string_view text)
+{
+ static const std::string in_file_included_from = "In file included from ";
+
+ std::string result;
+ for (auto line : Util::split_into_views(text, "\n")) {
+ // Rewrite <path> to <absolute path> in the following two cases, where X may
+ // be optional ANSI CSI sequences:
+ //
+ // In file included from X<path>X:1:
+ // X<path>X:1:2: ...
+
+ if (Util::starts_with(line, in_file_included_from)) {
+ result += in_file_included_from;
+ line = line.substr(in_file_included_from.length());
+ }
+ while (!line.empty() && line[0] == 0x1b) {
+ auto csi_seq = find_first_ansi_csi_seq(line);
+ result.append(csi_seq.data(), csi_seq.length());
+ line = line.substr(csi_seq.length());
+ }
+ size_t path_end = line.find(':');
+ if (path_end == nonstd::string_view::npos) {
+ result.append(line.data(), line.length());
+ } else {
+ std::string path(line.substr(0, path_end));
+ if (Stat::stat(path)) {
+ result += Util::real_path(path);
+ auto tail = line.substr(path_end);
+ result.append(tail.data(), tail.length());
+ } else {
+ result.append(line.data(), line.length());
+ }
+ }
+ result += '\n';
+ }
+ return result;
+}
+
} // namespace
namespace Util {
}
}
+ if (ctx.config.absolute_paths_in_stderr()) {
+ modified_text = rewrite_stderr_to_absolute_paths(*text_to_send);
+ text_to_send = &modified_text;
+ }
+
try {
write_fd(STDERR_FILENO, text_to_send->data(), text_to_send->length());
} catch (Error& e) {
const std::string& canonical_program_name);
// Send `text` to STDERR_FILENO, optionally stripping ANSI color sequences if
-// `ctx.args_info.strip_diagnostics_colors` is true. Throws `Error` on error.
+// `ctx.args_info.strip_diagnostics_colors` is true and rewriting paths to
+// absolute if `ctx.config.absolute_paths_in_stderr` is true. Throws `Error` on
+// error.
void send_to_stderr(const Context& ctx, const std::string& text);
// Set the FD_CLOEXEC on file descriptor `fd`. This is a NOP on Windows.
expect_stat 'cache miss' 1
cd ..
done
+
+ # -------------------------------------------------------------------------
+ TEST "Absolute paths in stderr"
+
+ cat <<EOF >test.c
+#include "test.h"
+#warning test.c
+EOF
+ cat <<EOF >test.h
+#warning test.h
+EOF
+ backdate test.h
+
+ pwd=$PWD.real
+ $REAL_COMPILER -c $pwd/test.c 2>reference.stderr
+
+ CCACHE_ABSSTDERR=1 CCACHE_BASEDIR="$pwd" $CCACHE_COMPILE -c $pwd/test.c 2>ccache.stderr
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_files reference.stderr ccache.stderr
+
+ CCACHE_ABSSTDERR=1 CCACHE_BASEDIR="$pwd" $CCACHE_COMPILE -c $pwd/test.c 2>ccache.stderr
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_files reference.stderr ccache.stderr
+
+ if $REAL_COMPILER -fdiagnostics-color=always -c test.c 2>/dev/null; then
+ $REAL_COMPILER -fdiagnostics-color=always -c $pwd/test.c 2>reference.stderr
+
+ CCACHE_ABSSTDERR=1 CCACHE_BASEDIR="$pwd" $CCACHE_COMPILE -fdiagnostics-color=always -c $pwd/test.c 2>ccache.stderr
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_files reference.stderr ccache.stderr
+
+ CCACHE_ABSSTDERR=1 CCACHE_BASEDIR="$pwd" $CCACHE_COMPILE -fdiagnostics-color=always -c $pwd/test.c 2>ccache.stderr
+ expect_stat 'cache hit (direct)' 3
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_files reference.stderr ccache.stderr
+ fi
}
Util::write_file(
"test.conf",
+ "absolute_paths_in_stderr = true\n"
#ifndef _WIN32
"base_dir = /bd\n"
#else
});
std::vector<std::string> expected = {
+ "(test.conf) absolute_paths_in_stderr = true",
#ifndef _WIN32
"(test.conf) base_dir = /bd",
#else