Closes #578.
change. The list separator is semicolon on Windows systems and colon on
other systems.
+[[config_ignore_options]] *ignore_options* (*CCACHE_IGNOREOPTIONS*)::
+
+ This setting is a space-delimited list of options that ccache will exclude
+ from the hash. Excluding a compiler option from the hash can be useful when
+ you know it doesn't affect the result (but ccache doesn't know that), or
+ when it does and you don't care. If an option in the list is suffixed with
+ an asterisk (`*`), the option is matched as a prefix. For example,
+ `-fmessage-length=*` will match both `-fmessage-length=20` and
+ `-fmessage-length=70`.
+
[[config_inode_cache]] *inode_cache* (*CCACHE_INODECACHE* or *CCACHE_NOINODECACHE*, see <<_boolean_values,Boolean values>> above)::
If true, enables caching of source file hashes based on device, inode and
hard_link,
hash_dir,
ignore_headers_in_manifest,
+ ignore_options,
inode_cache,
keep_comments_cpp,
limit_multiple,
{"hard_link", ConfigItem::hard_link},
{"hash_dir", ConfigItem::hash_dir},
{"ignore_headers_in_manifest", ConfigItem::ignore_headers_in_manifest},
+ {"ignore_options", ConfigItem::ignore_options},
{"inode_cache", ConfigItem::inode_cache},
{"keep_comments_cpp", ConfigItem::keep_comments_cpp},
{"limit_multiple", ConfigItem::limit_multiple},
{"HARDLINK", "hard_link"},
{"HASHDIR", "hash_dir"},
{"IGNOREHEADERS", "ignore_headers_in_manifest"},
+ {"IGNOREOPTIONS", "ignore_options"},
{"INODECACHE", "inode_cache"},
{"LIMIT_MULTIPLE", "limit_multiple"},
{"LOGFILE", "log_file"},
case ConfigItem::ignore_headers_in_manifest:
return m_ignore_headers_in_manifest;
+ case ConfigItem::ignore_options:
+ return m_ignore_options;
+
case ConfigItem::inode_cache:
return format_bool(m_inode_cache);
m_ignore_headers_in_manifest = parse_env_string(value);
break;
+ case ConfigItem::ignore_options:
+ m_ignore_options = parse_env_string(value);
+ break;
+
case ConfigItem::inode_cache:
m_inode_cache = parse_bool(value, env_var_key, negate);
break;
bool hard_link() const;
bool hash_dir() const;
const std::string& ignore_headers_in_manifest() const;
+ const std::string& ignore_options() const;
bool inode_cache() const;
bool keep_comments_cpp() const;
double limit_multiple() const;
void set_depend_mode(bool value);
void set_debug(bool value);
void set_direct_mode(bool value);
+ void set_ignore_options(const std::string& value);
void set_inode_cache(bool value);
void set_limit_multiple(double value);
void set_max_files(uint32_t value);
bool m_hard_link = false;
bool m_hash_dir = true;
std::string m_ignore_headers_in_manifest = "";
+ std::string m_ignore_options = "";
bool m_inode_cache = false;
bool m_keep_comments_cpp = false;
double m_limit_multiple = 0.8;
return m_ignore_headers_in_manifest;
}
+inline const std::string&
+Config::ignore_options() const
+{
+ return m_ignore_options;
+}
+
inline bool
Config::inode_cache() const
{
m_direct_mode = value;
}
+inline void
+Config::set_ignore_options(const std::string& value)
+{
+ m_ignore_options = value;
+}
+
inline void
Config::set_inode_cache(bool value)
{
#include "logging.hpp"
#include "stats.hpp"
+#include <algorithm>
+#include <string>
+#include <vector>
+
using nonstd::string_view;
Context::Context()
}
m_pending_tmp_files.clear();
}
+
+void
+Context::set_ignore_options(const std::vector<std::string>& options)
+{
+ for (const std::string& option : options) {
+ std::size_t n_wildcards = std::count(option.cbegin(), option.cend(), '*');
+ if (n_wildcards == 0 || (n_wildcards == 1 && option.back() == '*')) {
+ m_ignore_options.push_back(option);
+ } else {
+ cc_log("Skipping malformed ignore_options item: %s", option.c_str());
+ continue;
+ }
+ }
+}
// Files used by the hash debugging functionality.
std::vector<File> hash_debug_files;
+ // Options to ignore for the hash.
+ const std::vector<std::string>& ignore_options() const;
+ void set_ignore_options(const std::vector<std::string>& options);
+
#ifdef MTR_ENABLED
// Internal tracing.
std::unique_ptr<MiniTrace> mini_trace;
std::string m_result_path;
mutable std::string m_result_stats_file;
+ // Options to ignore for the hash.
+ std::vector<std::string> m_ignore_options;
+
// [Start of variables touched by the signal handler]
// Temporary files to remove at program exit.
assert(m_result_name); // set_result_name must have been called
return m_result_path;
}
+
+inline const std::vector<std::string>&
+Context::ignore_options() const
+{
+ return m_ignore_options;
+}
# include "third_party/getopt_long.h"
#endif
+#include <algorithm>
#include <limits>
using nonstd::nullopt;
return found;
}
+static bool
+option_should_be_ignored(const std::string& arg,
+ const std::vector<std::string>& ignore_options)
+{
+ auto pred = [&arg](const std::string& option) {
+ const auto& prefix = string_view(option).substr(0, option.length() - 1);
+ return (
+ option == arg
+ || (Util::ends_with(option, "*") && Util::starts_with(arg, prefix)));
+ };
+ return std::any_of(ignore_options.cbegin(), ignore_options.cend(), pred);
+}
+
// Update a hash sum with information specific to the direct and preprocessor
// modes and calculate the result name. Returns the result name on success,
// otherwise NULL. Caller frees.
// First the arguments.
for (size_t i = 1; i < args.size(); i++) {
+ // Trust the user if they've said we should not hash a given option.
+ if (option_should_be_ignored(args[i], ctx.ignore_options())) {
+ cc_log("Not hashing ignored option: %s", args[i].c_str());
+ if (i + 1 < args.size() && compopt_takes_arg(args[i])) {
+ i++;
+ cc_log("Not hashing argument of ignored option: %s", args[i].c_str());
+ }
+ continue;
+ }
+
// -L doesn't affect compilation (except for clang).
if (i < args.size() - 1 && args[i] == "-L" && !is_clang) {
i++;
ctx.orig_args = Args::from_argv(argc, argv);
ctx.ignore_header_paths = Util::split_into_strings(
ctx.config.ignore_headers_in_manifest(), PATH_DELIM);
+ ctx.set_ignore_options(
+ Util::split_into_strings(ctx.config.ignore_options(), " "));
}
// Initialize ccache, must be called once before anything else is run.
#include "Args.hpp"
#include "Counters.hpp"
+#include "Digest.hpp"
#include "stats.hpp"
#include "third_party/nonstd/optional.hpp"
if [ -n "$data" ]; then
test_failed "$manifest contained ignored header: $data"
fi
+
+ # -------------------------------------------------------------------------
+ TEST "CCACHE_IGNOREOPTIONS"
+
+ CCACHE_IGNOREOPTIONS="-DTEST=1" $CCACHE_COMPILE -DTEST=1 -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ CCACHE_IGNOREOPTIONS="-DTEST=1*" $CCACHE_COMPILE -DTEST=1 -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ CCACHE_IGNOREOPTIONS="-DTEST=1*" $CCACHE_COMPILE -DTEST=12 -c test.c
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ CCACHE_IGNOREOPTIONS="-DTEST=2*" $CCACHE_COMPILE -DTEST=12 -c test.c
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
}
CHECK(!config.hard_link());
CHECK(config.hash_dir());
CHECK(config.ignore_headers_in_manifest().empty());
+ CHECK(config.ignore_options().empty());
CHECK_FALSE(config.keep_comments_cpp());
CHECK(config.limit_multiple() == Approx(0.8));
CHECK(config.log_file().empty());
"hard_link = true\n"
"hash_dir = false\n"
"ignore_headers_in_manifest = a:b/c\n"
+ "ignore_options = -a=* -b\n"
"keep_comments_cpp = true\n"
"limit_multiple = 1.0\n"
"log_file = $USER${USER} \n"
CHECK(config.hard_link());
CHECK_FALSE(config.hash_dir());
CHECK(config.ignore_headers_in_manifest() == "a:b/c");
+ CHECK(config.ignore_options() == "-a=* -b");
CHECK(config.keep_comments_cpp());
CHECK(config.limit_multiple() == Approx(1.0));
CHECK(config.log_file() == fmt::format("{0}{0}", user));
"hard_link = true\n"
"hash_dir = false\n"
"ignore_headers_in_manifest = ihim\n"
+ "ignore_options = -a=* -b\n"
"inode_cache = false\n"
"keep_comments_cpp = true\n"
"limit_multiple = 0.0\n"
"(test.conf) hard_link = true",
"(test.conf) hash_dir = false",
"(test.conf) ignore_headers_in_manifest = ihim",
+ "(test.conf) ignore_options = -a=* -b",
"(test.conf) inode_cache = false",
"(test.conf) keep_comments_cpp = true",
"(test.conf) limit_multiple = 0.0",