dev_mode_disabled
doc/ccache.1
perfdir.*
-src/*_lookup.cpp
src/version.cpp
testdir.*
unittest/run
apt:
packages:
- elfutils
- - gperf
- libzstd1-dev
- libb2-dev
apt:
packages:
- elfutils
- - gperf
- libzstd1-dev
- libb2-dev
apt:
packages:
- gcc-multilib
- - gperf
- lib32stdc++-5-dev
# Job 5: Linux cross-compiled 32-bit MinGW
apt:
packages:
- elfutils
- - gperf
# Job 6: Linux cross-compiled 64-bit MinGW
- os: linux
apt:
packages:
- elfutils
- - gperf
# Job 7: Clang's undefined behavior sanitizer (UBSan)
- os: linux
apt:
packages:
- elfutils
- - gperf
- libzstd1-dev
- libb2-dev
apt:
packages:
- elfutils
- - gperf
- libzstd1-dev
- libb2-dev
addons:
apt:
packages:
- - gperf
- libzstd1-dev
- libb2-dev
apt:
packages:
- elfutils
- - gperf
- libzstd1-dev
- libb2-dev
before_install:
# ccache specific
RUN apt-get -qq update && apt-get install -y --no-install-recommends \
- gperf \
elfutils \
libzstd1-dev \
libb2-dev \
non_third_party_sources = \
src/AtomicFile.cpp \
+ src/Config.cpp \
src/args.cpp \
src/ccache.cpp \
src/cleanup.cpp \
src/compr_zstd.cpp \
src/compress.cpp \
src/compression.cpp \
- src/conf.cpp \
- src/confitems.cpp \
src/counters.cpp \
src/decompr_none.cpp \
src/decompr_zstd.cpp \
src/unify.cpp \
src/util.cpp
generated_sources = \
- src/confitems_lookup.cpp \
- src/envtoconfitems_lookup.cpp \
src/version.cpp
third_party_sources = \
src/third_party/format.cpp \
ccache_sources = src/main.cpp $(base_sources)
ccache_objs = $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(ccache_sources)))
+test_suites += unittest/test_Config.cpp
test_suites += unittest/test_args.cpp
test_suites += unittest/test_argument_processing.cpp
test_suites += unittest/test_compopt.cpp
test_suites += unittest/test_compr_none.cpp
test_suites += unittest/test_compr_zstd.cpp
-test_suites += unittest/test_conf.cpp
test_suites += unittest/test_counters.cpp
test_suites += unittest/test_hash.cpp
test_suites += unittest/test_hashutil.cpp
bash \
asciidoc \
autoconf \
- gperf \
zstd-dev \
##
bash \
asciidoc \
autoconf \
- gperf \
libzstd-devel \
&& rpm -e --nodeps graphviz \
&& yum autoremove -y \
bash \
asciidoc xsltproc docbook-xml docbook-xsl \
autoconf \
- gperf \
libzstd-dev \
&& rm -rf /var/lib/apt/lists/*
bash \
asciidoc \
autoconf \
- gperf \
libzstd-devel \
&& rpm -e --nodeps graphviz \
&& dnf autoremove -y \
bash \
asciidoc xsltproc docbook-xml docbook-xsl \
autoconf \
- gperf \
libzstd-dev \
&& rm -rf /var/lib/apt/lists/*
more_warnings="$more_warnings -Wno-documentation-unknown-command"
more_warnings="$more_warnings -Wno-exit-time-destructors"
more_warnings="$more_warnings -Wno-format-nonliteral"
+ more_warnings="$more_warnings -Wno-global-constructors"
more_warnings="$more_warnings -Wno-implicit-fallthrough"
more_warnings="$more_warnings -Wno-padded"
more_warnings="$more_warnings -Wno-shorten-64-to-32"
include_dev_mk='include dev.mk'
version=`(git --git-dir=$srcdir/.git describe --dirty 2>/dev/null || echo vunknown) | sed -e 's/v//' -e 's/-/+/' -e 's/-/_/g'`
echo "extern const char CCACHE_VERSION@<:@@:>@; const char CCACHE_VERSION@<:@@:>@ = \"$version\";" >src/version.cpp
- AC_CHECK_TOOL(GPERF, gperf)
- if test -z "$GPERF"; then
- AC_MSG_ERROR(please install gperf)
- fi
else
AC_MSG_NOTICE(developer mode disabled)
fi
CPPCHECK = cppcheck
CPPCHECK_SUPPRESSIONS = misc/cppcheck-suppressions.txt
DOCKER = docker
-GPERF = @GPERF@
SCAN_BUILD = scan-build
SHELLCHECK = shellcheck
SHELLCHECK_EXCLUDES = misc/shellcheck-excludes.txt
non_third_party_headers = \
src/AtomicFile.hpp \
+ src/Config.hpp \
src/Error.hpp \
src/ccache.hpp \
src/common_header.hpp \
src/compopt.hpp \
src/compression.hpp \
- src/conf.hpp \
- src/confitems.hpp \
src/counters.hpp \
- src/envtoconfitems.hpp \
src/hash.hpp \
src/hashutil.hpp \
src/int_bytes_conversion.hpp \
doc/NEWS.adoc \
install-sh \
m4 \
- src/confitems.gperf \
- src/confitems_lookup.cpp \
- src/envtoconfitems.gperf \
- src/envtoconfitems_lookup.cpp \
src/main.cpp \
src/third_party/minitrace.c \
test/run \
endif
src/version.o: src/version.cpp
-# $(1): Name.
-# $(2): Command for fixing up source file before the gperf call.
-define generate_gperf_lookup
-src/$(1)_lookup.cpp: src/$(1).gperf
- $$(if $$(quiet),@echo " GEN $$@")
- $$(Q)$(2) $$< | tr -d '\r' | $$(GPERF) | sed -e 's/#error/#warning/' -e 's/register//g' >$$@.tmp
-# Fix for gperf < 3.1 (fix parameter type and remove inlining of the get function):
- $$(Q)perl -00 -pi -e 's/unsigned int len/size_t len/; s/#ifdef __GNUC__.*?gnu_inline.*?#endif\n#endif\n//sg' $$@.tmp
- $$(Q)echo "size_t $(1)_count(void) { return $$$$(perl -ne '/TOTAL_KEYWORDS = (.+?),/ && print $$$$1' $$@.tmp); }" >>$$@.tmp
- $$(Q)mv $$@.tmp $$@
-endef
-
-add_confitems_numbers = \
- perl -pae '$$$$s = 1 if /^%%/; s/ITEM/$$$$n++ . ", ITEM"/e if $$$$s == 1'
-
-$(eval $(call generate_gperf_lookup,confitems,$(add_confitems_numbers)))
-$(eval $(call generate_gperf_lookup,envtoconfitems,cat))
-
.PHONY: dist
dist: $(dist_archives)
$(CPPCHECK) --suppressions-list=$(CPPCHECK_SUPPRESSIONS) \
--inline-suppr -q --enable=all --force -I . \
--template='cppcheck: warning: {id}:{file}:{line}: {message}' \
- $(non_third_party_sources) src/confitems_lookup.cpp src/main.cpp $(test_sources)
+ $(non_third_party_sources) src/main.cpp $(test_sources)
.PHONY: shellcheck
shellcheck: test/suites/*.bash
- [xsltproc](http://xmlsoft.org/XSLT/xsltproc2.html) to build the man page.
- [Autoconf](https://www.gnu.org/software/autoconf/) to generate the configure
script and related files.
-- [gperf](https://www.gnu.org/software/gperf/) to create lookup tables.
- [libb2](https://github.com/BLAKE2/libb2). If you don't have libb2 installed
and can't or don't want to install it on your system, you can pass
`--with-libb2-from-internet` to the configure script, which will make the
--- /dev/null
+// Copyright (C) 2019 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 "Config.hpp"
+
+#include "AtomicFile.hpp"
+#include "Error.hpp"
+#include "util.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <fstream>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+Config g_config;
+
+namespace {
+
+enum class ConfigItem {
+ base_dir,
+ cache_dir,
+ cache_dir_levels,
+ compiler,
+ compiler_check,
+ compression,
+ compression_level,
+ cpp_extension,
+ debug,
+ depend_mode,
+ direct_mode,
+ disable,
+ extra_files_to_hash,
+ file_clone,
+ hard_link,
+ hash_dir,
+ ignore_headers_in_manifest,
+ keep_comments_cpp,
+ limit_multiple,
+ log_file,
+ max_files,
+ max_size,
+ path,
+ pch_external_checksum,
+ prefix_command,
+ prefix_command_cpp,
+ read_only,
+ read_only_direct,
+ recache,
+ run_second_cpp,
+ sloppiness,
+ stats,
+ temporary_dir,
+ umask,
+ unify,
+};
+
+const std::unordered_map<std::string, ConfigItem> k_config_key_table = {
+ {"base_dir", ConfigItem::base_dir},
+ {"cache_dir", ConfigItem::cache_dir},
+ {"cache_dir_levels", ConfigItem::cache_dir_levels},
+ {"compiler", ConfigItem::compiler},
+ {"compiler_check", ConfigItem::compiler_check},
+ {"compression", ConfigItem::compression},
+ {"compression_level", ConfigItem::compression_level},
+ {"cpp_extension", ConfigItem::cpp_extension},
+ {"debug", ConfigItem::debug},
+ {"depend_mode", ConfigItem::depend_mode},
+ {"direct_mode", ConfigItem::direct_mode},
+ {"disable", ConfigItem::disable},
+ {"extra_files_to_hash", ConfigItem::extra_files_to_hash},
+ {"file_clone", ConfigItem::file_clone},
+ {"hard_link", ConfigItem::hard_link},
+ {"hash_dir", ConfigItem::hash_dir},
+ {"ignore_headers_in_manifest", ConfigItem::ignore_headers_in_manifest},
+ {"keep_comments_cpp", ConfigItem::keep_comments_cpp},
+ {"limit_multiple", ConfigItem::limit_multiple},
+ {"log_file", ConfigItem::log_file},
+ {"max_files", ConfigItem::max_files},
+ {"max_size", ConfigItem::max_size},
+ {"path", ConfigItem::path},
+ {"pch_external_checksum", ConfigItem::pch_external_checksum},
+ {"prefix_command", ConfigItem::prefix_command},
+ {"prefix_command_cpp", ConfigItem::prefix_command_cpp},
+ {"read_only", ConfigItem::read_only},
+ {"read_only_direct", ConfigItem::read_only_direct},
+ {"recache", ConfigItem::recache},
+ {"run_second_cpp", ConfigItem::run_second_cpp},
+ {"sloppiness", ConfigItem::sloppiness},
+ {"stats", ConfigItem::stats},
+ {"temporary_dir", ConfigItem::temporary_dir},
+ {"umask", ConfigItem::umask},
+ {"unify", ConfigItem::unify},
+};
+
+const std::unordered_map<std::string, std::string> k_env_variable_table = {
+ {"BASEDIR", "base_dir"},
+ {"CC", "compiler"}, // Alias for CCACHE_COMPILER
+ {"COMMENTS", "keep_comments_cpp"},
+ {"COMPILER", "compiler"},
+ {"COMPILERCHECK", "compiler_check"},
+ {"COMPRESS", "compression"},
+ {"COMPRESSLEVEL", "compression_level"},
+ {"CPP2", "run_second_cpp"},
+ {"DEBUG", "debug"},
+ {"DEPEND", "depend_mode"},
+ {"DIR", "cache_dir"},
+ {"DIRECT", "direct_mode"},
+ {"DISABLE", "disable"},
+ {"EXTENSION", "cpp_extension"},
+ {"EXTRAFILES", "extra_files_to_hash"},
+ {"FILECLONE", "file_clone"},
+ {"HARDLINK", "hard_link"},
+ {"HASHDIR", "hash_dir"},
+ {"IGNOREHEADERS", "ignore_headers_in_manifest"},
+ {"LIMIT_MULTIPLE", "limit_multiple"},
+ {"LOGFILE", "log_file"},
+ {"MAXFILES", "max_files"},
+ {"MAXSIZE", "max_size"},
+ {"NLEVELS", "cache_dir_levels"},
+ {"PATH", "path"},
+ {"PCH_EXTSUM", "pch_external_checksum"},
+ {"PREFIX", "prefix_command"},
+ {"PREFIX_CPP", "prefix_command_cpp"},
+ {"READONLY", "read_only"},
+ {"READONLY_DIRECT", "read_only_direct"},
+ {"RECACHE", "recache"},
+ {"SLOPPINESS", "sloppiness"},
+ {"STATS", "stats"},
+ {"TEMPDIR", "temporary_dir"},
+ {"UMASK", "umask"},
+ {"UNIFY", "unify"},
+};
+
+typedef std::function<void(
+ const std::string& line, const std::string& key, const std::string& value)>
+ ConfigLineHandler;
+
+bool
+parse_bool(const std::string& value, bool from_env_variable, bool negate)
+{
+ if (from_env_variable) {
+ // Special rule for boolean settings from the environment: "0", "false",
+ // "disable" and "no" (case insensitive) are invalid, and all other values
+ // mean true.
+ //
+ // Previously any value meant true, but this was surprising to users, who
+ // might do something like CCACHE_DISABLE=0 and expect ccache to be
+ // enabled.
+ std::string lower_value = util::to_lowercase(value);
+ if (value == "0" || lower_value == "false" || lower_value == "disable"
+ || lower_value == "no") {
+ throw Error(fmt::format(
+ "invalid boolean environment variable value \"{}\"", value));
+ }
+ return !negate;
+ } else if (value == "true") {
+ return true;
+ } else if (value == "false") {
+ return false;
+ } else {
+ throw Error(fmt::format("not a boolean value: \"{}\"", value));
+ }
+}
+
+std::string
+format_bool(bool value)
+{
+ return value ? "true" : "false";
+}
+
+std::string
+parse_env_string(const std::string& value)
+{
+ char* errmsg = nullptr;
+ char* substituted = subst_env_in_string(value.c_str(), &errmsg);
+ if (!substituted) {
+ std::string error_message = errmsg;
+ free(errmsg);
+ throw Error(error_message);
+ }
+ std::string result = substituted;
+ free(substituted);
+ return result;
+}
+
+double
+parse_double(const std::string& value)
+{
+ size_t end;
+ double result;
+ try {
+ result = std::stod(value, &end);
+ } catch (std::exception& e) {
+ throw Error(e.what());
+ }
+ if (end != value.size()) {
+ throw Error(fmt::format("invalid floating point: \"{}\"", value));
+ }
+ return result;
+}
+
+uint64_t
+parse_cache_size(const std::string& value)
+{
+ uint64_t result;
+ if (!parse_size_with_suffix(value.c_str(), &result)) {
+ throw Error(fmt::format("invalid size: \"{}\"", value));
+ }
+ return result;
+}
+
+std::string
+format_cache_size(uint64_t value)
+{
+ char* string = format_parsable_size_with_suffix(value);
+ std::string result = string;
+ free(string);
+ return result;
+}
+
+uint32_t
+parse_sloppiness(const std::string& value)
+{
+ size_t start = 0;
+ size_t end = 0;
+ uint32_t result = 0;
+ while (end != std::string::npos) {
+ end = value.find_first_of(", ", start);
+ std::string token =
+ util::strip_whitespace(value.substr(start, end - start));
+ if (token == "file_macro") {
+ result |= SLOPPY_FILE_MACRO;
+ } else if (token == "file_stat_matches") {
+ result |= SLOPPY_FILE_STAT_MATCHES;
+ } else if (token == "file_stat_matches_ctime") {
+ result |= SLOPPY_FILE_STAT_MATCHES_CTIME;
+ } else if (token == "include_file_ctime") {
+ result |= SLOPPY_INCLUDE_FILE_CTIME;
+ } else if (token == "include_file_mtime") {
+ result |= SLOPPY_INCLUDE_FILE_MTIME;
+ } else if (token == "system_headers" || token == "no_system_headers") {
+ result |= SLOPPY_SYSTEM_HEADERS;
+ } else if (token == "pch_defines") {
+ result |= SLOPPY_PCH_DEFINES;
+ } else if (token == "time_macros") {
+ result |= SLOPPY_TIME_MACROS;
+ } else if (token == "clang_index_store") {
+ result |= SLOPPY_CLANG_INDEX_STORE;
+ } else if (token == "locale") {
+ result |= SLOPPY_LOCALE;
+ } else if (token != "") {
+ throw Error(fmt::format("unknown sloppiness: \"{}\"", token));
+ }
+ start = value.find_first_not_of(", ", end);
+ }
+ return result;
+}
+
+std::string
+format_sloppiness(uint32_t sloppiness)
+{
+ std::string result;
+ if (sloppiness & SLOPPY_FILE_MACRO) {
+ result += "file_macro, ";
+ }
+ if (sloppiness & SLOPPY_INCLUDE_FILE_MTIME) {
+ result += "include_file_mtime, ";
+ }
+ if (sloppiness & SLOPPY_INCLUDE_FILE_CTIME) {
+ result += "include_file_ctime, ";
+ }
+ if (sloppiness & SLOPPY_TIME_MACROS) {
+ result += "time_macros, ";
+ }
+ if (sloppiness & SLOPPY_PCH_DEFINES) {
+ result += "pch_defines, ";
+ }
+ if (sloppiness & SLOPPY_FILE_STAT_MATCHES) {
+ result += "file_stat_matches, ";
+ }
+ if (sloppiness & SLOPPY_FILE_STAT_MATCHES_CTIME) {
+ result += "file_stat_matches_ctime, ";
+ }
+ if (sloppiness & SLOPPY_SYSTEM_HEADERS) {
+ result += "system_headers, ";
+ }
+ if (sloppiness & SLOPPY_CLANG_INDEX_STORE) {
+ result += "clang_index_store, ";
+ }
+ if (sloppiness & SLOPPY_LOCALE) {
+ result += "locale, ";
+ }
+ if (!result.empty()) {
+ // Strip last ", ".
+ result.resize(result.size() - 2);
+ }
+ return result;
+}
+
+uint32_t
+parse_umask(const std::string& value)
+{
+ if (value.empty()) {
+ return std::numeric_limits<uint32_t>::max();
+ }
+
+ size_t end;
+ uint32_t result = std::stoul(value, &end, 8);
+ if (end != value.size()) {
+ throw Error(fmt::format("not an octal integer: \"{}\"", value));
+ }
+ return result;
+}
+
+std::string
+format_umask(uint32_t umask)
+{
+ if (umask == std::numeric_limits<uint32_t>::max()) {
+ return {};
+ } else {
+ return fmt::format("{:03o}", umask);
+ }
+}
+
+int
+parse_int(const std::string& value)
+{
+ size_t end;
+ long result;
+ bool failed = false;
+ try {
+ result = std::stol(value, &end, 10);
+ } catch (std::exception&) {
+ failed = true;
+ }
+ if (failed || end != value.size() || result < std::numeric_limits<int>::min()
+ || result > std::numeric_limits<int>::max()) {
+ throw Error(fmt::format("invalid integer: \"{}\"", value));
+ }
+ return result;
+}
+
+unsigned
+parse_unsigned(const std::string& value)
+{
+ size_t end;
+ long result;
+ bool failed = false;
+ try {
+ result = std::stol(value, &end, 10);
+ } catch (std::exception&) {
+ failed = true;
+ }
+ if (failed || end != value.size() || result < 0) {
+ throw Error(fmt::format("invalid unsigned integer: \"{}\"", value));
+ }
+ return result;
+}
+
+void
+verify_absolute_path(const std::string& value)
+{
+ if (!is_absolute_path(value.c_str())) {
+ throw Error(fmt::format("not an absolute path: \"{}\"", value));
+ }
+}
+
+bool
+parse_line(const std::string& line,
+ std::string* key,
+ std::string* value,
+ std::string* error_message)
+{
+ std::string stripped_line = util::strip_whitespace(line);
+ if (stripped_line.empty() || stripped_line[0] == '#') {
+ return true;
+ }
+ size_t equal_pos = stripped_line.find("=");
+ if (equal_pos == std::string::npos) {
+ *error_message = "missing equal sign";
+ return false;
+ }
+ *key = stripped_line.substr(0, equal_pos);
+ *value = stripped_line.substr(equal_pos + 1);
+ *key = util::strip_whitespace(*key);
+ *value = util::strip_whitespace(*value);
+ return true;
+}
+
+bool
+parse_config_file(const std::string& path,
+ const ConfigLineHandler& config_line_handler)
+{
+ std::ifstream file(path);
+ if (!file) {
+ return false;
+ }
+
+ std::string line;
+
+ size_t line_number = 0;
+ while (std::getline(file, line)) {
+ ++line_number;
+
+ try {
+ std::string key;
+ std::string value;
+ std::string error_message;
+ if (!parse_line(line, &key, &value, &error_message)) {
+ throw Error(error_message);
+ }
+ if (!key.empty()) {
+ // key is empty if comment or blank line.
+ config_line_handler(line, key, value);
+ }
+ } catch (const Error& e) {
+ throw Error(fmt::format("{}:{}: {}", path, line_number, e.what()));
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+bool
+Config::update_from_file(const std::string& file_path)
+{
+ return parse_config_file(file_path,
+ [&](const std::string& /*line*/,
+ const std::string& key,
+ const std::string& value) {
+ set_item(key, value, false, false, file_path);
+ });
+}
+
+void
+Config::update_from_environment()
+{
+ for (char** env = environ; *env; ++env) {
+ std::string setting = *env;
+ const std::string prefix = "CCACHE_";
+ if (!util::starts_with(setting, prefix)) {
+ continue;
+ }
+ size_t equal_pos = setting.find('=');
+ if (equal_pos == std::string::npos) {
+ continue;
+ }
+
+ std::string key = setting.substr(prefix.size(), equal_pos - prefix.size());
+ std::string value = setting.substr(equal_pos + 1);
+ bool negate = util::starts_with(key, "NO");
+ if (negate) {
+ key = key.substr(2);
+ }
+
+ auto it = k_env_variable_table.find(key);
+ if (it == k_env_variable_table.end()) {
+ // Ignore unknown keys.
+ continue;
+ }
+ const auto& config_key = it->second;
+
+ try {
+ set_item(config_key, value, true, negate, "environment");
+ } catch (const Error& e) {
+ throw Error(fmt::format("CCACHE_{}: {}", key, e.what()));
+ }
+ }
+}
+
+std::string
+Config::get_string_value(const std::string& key) const
+{
+ auto it = k_config_key_table.find(key);
+ if (it == k_config_key_table.end()) {
+ throw Error(fmt::format("unknown configuration option \"{}\"", key));
+ }
+
+ switch (it->second) {
+ case ConfigItem::base_dir:
+ return m_base_dir;
+
+ case ConfigItem::cache_dir:
+ return m_cache_dir;
+
+ case ConfigItem::cache_dir_levels:
+ return fmt::format("{}", m_cache_dir_levels);
+
+ case ConfigItem::compiler:
+ return m_compiler;
+
+ case ConfigItem::compiler_check:
+ return m_compiler_check;
+
+ case ConfigItem::compression:
+ return format_bool(m_compression);
+
+ case ConfigItem::compression_level:
+ return fmt::format("{}", m_compression_level);
+
+ case ConfigItem::cpp_extension:
+ return m_cpp_extension;
+
+ case ConfigItem::debug:
+ return format_bool(m_debug);
+
+ case ConfigItem::depend_mode:
+ return format_bool(m_depend_mode);
+
+ case ConfigItem::direct_mode:
+ return format_bool(m_direct_mode);
+
+ case ConfigItem::disable:
+ return format_bool(m_disable);
+
+ case ConfigItem::extra_files_to_hash:
+ return m_extra_files_to_hash;
+
+ case ConfigItem::file_clone:
+ return format_bool(m_file_clone);
+
+ case ConfigItem::hard_link:
+ return format_bool(m_hard_link);
+
+ case ConfigItem::hash_dir:
+ return format_bool(m_hash_dir);
+
+ case ConfigItem::ignore_headers_in_manifest:
+ return m_ignore_headers_in_manifest;
+
+ case ConfigItem::keep_comments_cpp:
+ return format_bool(m_keep_comments_cpp);
+
+ case ConfigItem::limit_multiple:
+ return fmt::format("{:.1f}", m_limit_multiple);
+
+ case ConfigItem::log_file:
+ return m_log_file;
+
+ case ConfigItem::max_files:
+ return fmt::format("{}", m_max_files);
+
+ case ConfigItem::max_size:
+ return format_cache_size(m_max_size);
+
+ case ConfigItem::path:
+ return m_path;
+
+ case ConfigItem::pch_external_checksum:
+ return format_bool(m_pch_external_checksum);
+
+ case ConfigItem::prefix_command:
+ return m_prefix_command;
+
+ case ConfigItem::prefix_command_cpp:
+ return m_prefix_command_cpp;
+
+ case ConfigItem::read_only:
+ return format_bool(m_read_only);
+
+ case ConfigItem::read_only_direct:
+ return format_bool(m_read_only_direct);
+
+ case ConfigItem::recache:
+ return format_bool(m_recache);
+
+ case ConfigItem::run_second_cpp:
+ return format_bool(m_run_second_cpp);
+
+ case ConfigItem::sloppiness:
+ return format_sloppiness(m_sloppiness);
+
+ case ConfigItem::stats:
+ return format_bool(m_stats);
+
+ case ConfigItem::temporary_dir:
+ return m_temporary_dir;
+
+ case ConfigItem::umask:
+ return format_umask(m_umask);
+
+ case ConfigItem::unify:
+ return format_bool(m_unify);
+ }
+
+ assert(false);
+ return {}; // Never reached
+}
+
+void
+Config::set_value_in_file(const std::string& path,
+ const std::string& key,
+ const std::string& value)
+{
+ if (k_config_key_table.find(key) == k_config_key_table.end()) {
+ throw Error(fmt::format("unknown configuration option \"{}\"", key));
+ }
+
+ // Verify that the value is valid; set_item will throw if not.
+ Config dummy_config;
+ dummy_config.set_item(key, value, false, false, "");
+
+ std::string content = util::read_file(path);
+
+ AtomicFile output(path, AtomicFile::Mode::Text);
+ bool found = false;
+
+ if (!parse_config_file(path,
+ [&](const std::string& c_line,
+ const std::string& c_key,
+ const std::string& /*c_value*/) {
+ if (c_key == key) {
+ output.write(fmt::format("{} = {}\n", key, value));
+ found = true;
+ } else {
+ output.write(fmt::format("{}\n", c_line));
+ }
+ })) {
+ throw Error(fmt::format("failed to open {}: {}", path, strerror(errno)));
+ }
+
+ if (!found) {
+ output.write(fmt::format("{} = {}\n", key, value));
+ }
+
+ output.close();
+}
+
+void
+Config::visit_items(const ItemVisitor& item_visitor) const
+{
+ std::vector<std::string> keys;
+ for (const auto& item : k_config_key_table) {
+ keys.emplace_back(item.first);
+ }
+ std::sort(keys.begin(), keys.end());
+ for (const auto& key : keys) {
+ auto it = m_origins.find(key);
+ std::string origin = it != m_origins.end() ? it->second : "default";
+ item_visitor(key, get_string_value(key), origin);
+ }
+}
+
+void
+Config::set_item(const std::string& key,
+ const std::string& value,
+ bool from_env_variable,
+ bool negate,
+ const std::string& origin)
+{
+ auto it = k_config_key_table.find(key);
+ if (it == k_config_key_table.end()) {
+ // Ignore unknown keys.
+ return;
+ }
+
+ switch (it->second) {
+ case ConfigItem::base_dir:
+ m_base_dir = parse_env_string(value);
+ if (!m_base_dir.empty()) { // The empty string means "disable"
+ verify_absolute_path(m_base_dir);
+ }
+ break;
+
+ case ConfigItem::cache_dir:
+ m_cache_dir = parse_env_string(value);
+ break;
+
+ case ConfigItem::cache_dir_levels:
+ m_cache_dir_levels = parse_unsigned(value);
+ if (m_cache_dir_levels < 1 || m_cache_dir_levels > 8) {
+ throw Error("cache directory levels must be between 1 and 8");
+ }
+ break;
+
+ case ConfigItem::compiler:
+ m_compiler = value;
+ break;
+
+ case ConfigItem::compiler_check:
+ m_compiler_check = value;
+ break;
+
+ case ConfigItem::compression:
+ m_compression = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::compression_level: {
+ auto level = parse_int(value);
+ if (level < -128 || level > 127) {
+ throw Error("compression level must be between -128 and 127");
+ }
+ m_compression_level = level;
+ break;
+ }
+
+ case ConfigItem::cpp_extension:
+ m_cpp_extension = value;
+ break;
+
+ case ConfigItem::debug:
+ m_debug = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::depend_mode:
+ m_depend_mode = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::direct_mode:
+ m_direct_mode = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::disable:
+ m_disable = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::extra_files_to_hash:
+ m_extra_files_to_hash = parse_env_string(value);
+ break;
+
+ case ConfigItem::file_clone:
+ m_file_clone = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::hard_link:
+ m_hard_link = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::hash_dir:
+ m_hash_dir = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::ignore_headers_in_manifest:
+ m_ignore_headers_in_manifest = parse_env_string(value);
+ break;
+
+ case ConfigItem::keep_comments_cpp:
+ m_keep_comments_cpp = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::limit_multiple:
+ m_limit_multiple = parse_double(value);
+ break;
+
+ case ConfigItem::log_file:
+ m_log_file = parse_env_string(value);
+ break;
+
+ case ConfigItem::max_files:
+ m_max_files = parse_unsigned(value);
+ break;
+
+ case ConfigItem::max_size:
+ m_max_size = parse_cache_size(value);
+ break;
+
+ case ConfigItem::path:
+ m_path = parse_env_string(value);
+ break;
+
+ case ConfigItem::pch_external_checksum:
+ m_pch_external_checksum = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::prefix_command:
+ m_prefix_command = parse_env_string(value);
+ break;
+
+ case ConfigItem::prefix_command_cpp:
+ m_prefix_command_cpp = parse_env_string(value);
+ break;
+
+ case ConfigItem::read_only:
+ m_read_only = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::read_only_direct:
+ m_read_only_direct = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::recache:
+ m_recache = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::run_second_cpp:
+ m_run_second_cpp = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::sloppiness:
+ m_sloppiness = parse_sloppiness(value);
+ break;
+
+ case ConfigItem::stats:
+ m_stats = parse_bool(value, from_env_variable, negate);
+ break;
+
+ case ConfigItem::temporary_dir:
+ m_temporary_dir = parse_env_string(value);
+ break;
+
+ case ConfigItem::umask:
+ m_umask = parse_umask(value);
+ break;
+
+ case ConfigItem::unify:
+ m_unify = parse_bool(value, from_env_variable, negate);
+ break;
+ }
+
+ m_origins.emplace(key, origin);
+}
--- /dev/null
+// Copyright (C) 2019 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 "system.hpp"
+
+#include "ccache.hpp"
+
+#include <fmt/core.h>
+#include <functional>
+#include <limits>
+#include <string>
+#include <unordered_map>
+
+class Config;
+extern Config g_config;
+
+class Config
+{
+public:
+ const std::string& base_dir() const;
+ const std::string& cache_dir() const;
+ uint32_t cache_dir_levels() const;
+ const std::string& compiler() const;
+ const std::string& compiler_check() const;
+ bool compression() const;
+ int8_t compression_level() const;
+ const std::string& cpp_extension() const;
+ bool debug() const;
+ bool depend_mode() const;
+ bool direct_mode() const;
+ bool disable() const;
+ const std::string& extra_files_to_hash() const;
+ bool file_clone() const;
+ bool hard_link() const;
+ bool hash_dir() const;
+ const std::string& ignore_headers_in_manifest() const;
+ bool keep_comments_cpp() const;
+ double limit_multiple() const;
+ const std::string& log_file() const;
+ uint32_t max_files() const;
+ uint64_t max_size() const;
+ const std::string& path() const;
+ bool pch_external_checksum() const;
+ const std::string& prefix_command() const;
+ const std::string& prefix_command_cpp() const;
+ bool read_only() const;
+ bool read_only_direct() const;
+ bool recache() const;
+ bool run_second_cpp() const;
+ uint32_t sloppiness() const;
+ bool stats() const;
+ const std::string& temporary_dir() const;
+ uint32_t umask() const;
+ bool unify() const;
+
+ void set_base_dir(const std::string& value);
+ void set_cache_dir(const std::string& value);
+ void set_cpp_extension(const std::string& value);
+ void set_depend_mode(bool value);
+ void set_direct_mode(bool value);
+ void set_limit_multiple(double value);
+ void set_max_files(uint32_t value);
+ void set_max_size(uint64_t value);
+ void set_run_second_cpp(bool value);
+ void set_unify(bool value);
+
+ typedef std::function<void(const std::string& key,
+ const std::string& value,
+ const std::string& origin)>
+ ItemVisitor;
+
+ // Set config values from a configuration file.
+ //
+ // 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);
+
+ // Set config values from environment variables.
+ //
+ // Throws Error on invalid configuration values.
+ void update_from_environment();
+
+ // Get a config value in string form given a key.
+ std::string get_string_value(const std::string& key) const;
+
+ void visit_items(const ItemVisitor& item_visitor) const;
+
+ static void set_value_in_file(const std::string& path,
+ const std::string& key,
+ const std::string& value);
+
+private:
+ std::string m_base_dir = "";
+ std::string m_cache_dir = fmt::format("{}/.ccache", get_home_directory());
+ uint32_t m_cache_dir_levels = 2;
+ std::string m_compiler = "";
+ std::string m_compiler_check = "mtime";
+ bool m_compression = true;
+ int8_t m_compression_level = 0; // Use default level
+ std::string m_cpp_extension = "";
+ bool m_debug = false;
+ bool m_depend_mode = false;
+ bool m_direct_mode = true;
+ bool m_disable = false;
+ std::string m_extra_files_to_hash = "";
+ bool m_file_clone = false;
+ bool m_hard_link = false;
+ bool m_hash_dir = true;
+ std::string m_ignore_headers_in_manifest = "";
+ bool m_keep_comments_cpp = false;
+ double m_limit_multiple = 0.8;
+ std::string m_log_file = "";
+ uint32_t m_max_files = 0;
+ uint64_t m_max_size = 5ULL * 1000 * 1000 * 1000;
+ std::string m_path = "";
+ bool m_pch_external_checksum = false;
+ std::string m_prefix_command = "";
+ std::string m_prefix_command_cpp = "";
+ bool m_read_only = false;
+ bool m_read_only_direct = false;
+ bool m_recache = false;
+ bool m_run_second_cpp = true;
+ uint32_t m_sloppiness = 0;
+ bool m_stats = true;
+ std::string m_temporary_dir = "";
+ uint32_t m_umask = std::numeric_limits<uint32_t>::max(); // Don't set umask
+ bool m_unify = false;
+
+ std::unordered_map<std::string /*key*/, std::string /*origin*/> m_origins;
+
+ void set_item(const std::string& key,
+ const std::string& value,
+ bool from_env_variable,
+ bool negate,
+ const std::string& origin);
+};
+
+inline const std::string&
+Config::base_dir() const
+{
+ return m_base_dir;
+}
+
+inline const std::string&
+Config::cache_dir() const
+{
+ return m_cache_dir;
+}
+
+inline uint32_t
+Config::cache_dir_levels() const
+{
+ return m_cache_dir_levels;
+}
+
+inline const std::string&
+Config::compiler() const
+{
+ return m_compiler;
+}
+
+inline const std::string&
+Config::compiler_check() const
+{
+ return m_compiler_check;
+}
+
+inline bool
+Config::compression() const
+{
+ return m_compression;
+}
+
+inline int8_t
+Config::compression_level() const
+{
+ return m_compression_level;
+}
+
+inline const std::string&
+Config::cpp_extension() const
+{
+ return m_cpp_extension;
+}
+
+inline bool
+Config::debug() const
+{
+ return m_debug;
+}
+
+inline bool
+Config::depend_mode() const
+{
+ return m_depend_mode;
+}
+
+inline bool
+Config::direct_mode() const
+{
+ return m_direct_mode;
+}
+
+inline bool
+Config::disable() const
+{
+ return m_disable;
+}
+
+inline const std::string&
+Config::extra_files_to_hash() const
+{
+ return m_extra_files_to_hash;
+}
+
+inline bool
+Config::file_clone() const
+{
+ return m_file_clone;
+}
+
+inline bool
+Config::hard_link() const
+{
+ return m_hard_link;
+}
+
+inline bool
+Config::hash_dir() const
+{
+ return m_hash_dir;
+}
+
+inline const std::string&
+Config::ignore_headers_in_manifest() const
+{
+ return m_ignore_headers_in_manifest;
+}
+
+inline bool
+Config::keep_comments_cpp() const
+{
+ return m_keep_comments_cpp;
+}
+
+inline double
+Config::limit_multiple() const
+{
+ return m_limit_multiple;
+}
+
+inline const std::string&
+Config::log_file() const
+{
+ return m_log_file;
+}
+
+inline uint32_t
+Config::max_files() const
+{
+ return m_max_files;
+}
+
+inline uint64_t
+Config::max_size() const
+{
+ return m_max_size;
+}
+
+inline const std::string&
+Config::path() const
+{
+ return m_path;
+}
+
+inline bool
+Config::pch_external_checksum() const
+{
+ return m_pch_external_checksum;
+}
+
+inline const std::string&
+Config::prefix_command() const
+{
+ return m_prefix_command;
+}
+
+inline const std::string&
+Config::prefix_command_cpp() const
+{
+ return m_prefix_command_cpp;
+}
+
+inline bool
+Config::read_only() const
+{
+ return m_read_only;
+}
+
+inline bool
+Config::read_only_direct() const
+{
+ return m_read_only_direct;
+}
+
+inline bool
+Config::recache() const
+{
+ return m_recache;
+}
+
+inline bool
+Config::run_second_cpp() const
+{
+ return m_run_second_cpp;
+}
+
+inline uint32_t
+Config::sloppiness() const
+{
+ return m_sloppiness;
+}
+
+inline bool
+Config::stats() const
+{
+ return m_stats;
+}
+
+inline const std::string&
+Config::temporary_dir() const
+{
+ return m_temporary_dir;
+}
+
+inline uint32_t
+Config::umask() const
+{
+ return m_umask;
+}
+
+inline bool
+Config::unify() const
+{
+ return m_unify;
+}
+
+inline void
+Config::set_base_dir(const std::string& value)
+{
+ m_base_dir = value;
+}
+
+inline void
+Config::set_cache_dir(const std::string& value)
+{
+ m_cache_dir = value;
+}
+
+inline void
+Config::set_cpp_extension(const std::string& value)
+{
+ m_cpp_extension = value;
+}
+
+inline void
+Config::set_depend_mode(bool value)
+{
+ m_depend_mode = value;
+}
+
+inline void
+Config::set_direct_mode(bool value)
+{
+ m_direct_mode = value;
+}
+
+inline void
+Config::set_limit_multiple(double value)
+{
+ m_limit_multiple = value;
+}
+
+inline void
+Config::set_max_files(uint32_t value)
+{
+ m_max_files = value;
+}
+
+inline void
+Config::set_max_size(uint64_t value)
+{
+ m_max_size = value;
+}
+
+inline void
+Config::set_run_second_cpp(bool value)
+{
+ m_run_second_cpp = value;
+}
+
+inline void
+Config::set_unify(bool value)
+{
+ m_unify = value;
+}
#include "ccache.hpp"
+#include "Error.hpp"
#include "compopt.hpp"
+#include "util.hpp"
+
+#include <fmt/core.h>
+#include <limits>
+
#ifdef HAVE_GETOPT_LONG
# include <getopt.h>
#else
#include "third_party/hashtable.h"
#include "third_party/hashtable_itr.h"
-#define STRINGIFY(x) #x
-#define TO_STRING(x) STRINGIFY(x)
-
// Global variables used by other compilation units.
-extern struct conf* conf;
extern char* primary_config_path;
extern char* secondary_config_path;
extern char* current_working_dir;
"\n"
"See also <https://ccache.dev>.\n";
-// Global configuration data.
-struct conf* conf = NULL;
-
// Where to write configuration changes.
char* primary_config_path = NULL;
static const char HASH_PREFIX[] = "3";
static void
-add_prefix(struct args* args, char* prefix_command)
+add_prefix(struct args* args, const char* prefix_command)
{
if (str_eq(prefix_command, "")) {
return;
assert(orig_args);
args_strip(orig_args, "--ccache-");
- add_prefix(orig_args, conf->prefix_command);
+ add_prefix(orig_args, g_config.prefix_command().c_str());
cc_log("Failed; falling back to running the real compiler");
cc_log_argv("Executing ", orig_args->argv);
static const char*
temp_dir()
{
- static char* path = NULL;
+ static const char* path = NULL;
if (path) {
return path; // Memoize
}
- path = conf->temporary_dir;
+ path = g_config.temporary_dir().c_str();
if (str_eq(path, "")) {
- path = format("%s/tmp", conf->cache_dir);
+ path = format("%s/tmp", g_config.cache_dir().c_str());
}
return path;
}
{
time_t now = time(NULL);
struct stat st;
- if (x_stat(conf->cache_dir, &st) != 0 || st.st_mtime + 3600 >= now) {
+ if (x_stat(g_config.cache_dir().c_str(), &st) != 0
+ || st.st_mtime + 3600 >= now) {
// No cleanup needed.
return;
}
- update_mtime(conf->cache_dir);
+ update_mtime(g_config.cache_dir().c_str());
DIR* dir = opendir(temp_dir());
if (!dir) {
static void
dump_debug_log_buffer_exitfn(void* context)
{
- if (!conf->debug) {
+ if (!g_config.debug()) {
return;
}
const char* section_name,
FILE* debug_text_file)
{
- if (!conf->debug) {
+ if (!g_config.debug()) {
return;
}
goto out;
}
- if (system && (conf->sloppiness & SLOPPY_SYSTEM_HEADERS)) {
+ if (system && (g_config.sloppiness() & SLOPPY_SYSTEM_HEADERS)) {
// Don't remember this system header.
goto out;
}
// The comparison using >= is intentional, due to a possible race between
// starting compilation and writing the include file. See also the notes
// under "Performance" in doc/MANUAL.adoc.
- if (!(conf->sloppiness & SLOPPY_INCLUDE_FILE_MTIME)
+ if (!(g_config.sloppiness() & SLOPPY_INCLUDE_FILE_MTIME)
&& st.st_mtime >= time_of_compilation) {
cc_log("Include file %s too new", path);
goto failure;
}
// The same >= logic as above applies to the change time of the file.
- if (!(conf->sloppiness & SLOPPY_INCLUDE_FILE_CTIME)
+ if (!(g_config.sloppiness() & SLOPPY_INCLUDE_FILE_CTIME)
&& st.st_ctime >= time_of_compilation) {
cc_log("Include file %s ctime too new", path);
goto failure;
cc_log("Detected use of precompiled header: %s", path);
}
bool using_pch_sum = false;
- if (conf->pch_external_checksum) {
+ if (g_config.pch_external_checksum()) {
// hash pch.sum instead of pch when it exists
// to prevent hashing a very large .pch file every time
char* pch_sum_path = format("%s.sum", path);
hash_string(cpp_hash, pch_digest);
}
- if (conf->direct_mode) {
+ if (g_config.direct_mode()) {
if (!is_pch) { // else: the file has already been hashed.
char* source = NULL;
size_t size;
size = 0;
}
- int result = hash_source_code_string(conf, fhash, source, size, path);
+ int result = hash_source_code_string(g_config, fhash, source, size, path);
free(source);
if (result & HASH_SOURCE_CODE_ERROR
|| result & HASH_SOURCE_CODE_FOUND_TIME) {
goto out;
failure:
- if (conf->direct_mode) {
+ if (g_config.direct_mode()) {
cc_log("Disabling direct mode");
- conf->direct_mode = false;
+ g_config.set_direct_mode(false);
}
// Fall through.
out:
static char*
make_relative_path(char* path)
{
- if (str_eq(conf->base_dir, "") || !str_startswith(path, conf->base_dir)) {
+ if (g_config.base_dir().empty()
+ || !str_startswith(path, g_config.base_dir().c_str())) {
return path;
}
ignore_headers = NULL;
ignore_headers_len = 0;
- if (!str_eq(conf->ignore_headers_in_manifest, "")) {
+ if (!g_config.ignore_headers_in_manifest().empty()) {
char *header, *p, *q, *saveptr = NULL;
- p = x_strdup(conf->ignore_headers_in_manifest);
+ p = x_strdup(g_config.ignore_headers_in_manifest().c_str());
q = p;
while ((header = strtok_r(q, PATH_DELIM, &saveptr))) {
ignore_headers = static_cast<char**>(
inc_path = make_relative_path(inc_path);
bool should_hash_inc_path = true;
- if (!conf->hash_dir) {
+ if (!g_config.hash_dir()) {
if (str_startswith(inc_path, cwd) && str_endswith(inc_path, "//")) {
// When compiling with -g or similar, GCC adds the absolute path to
// CWD like this:
static void
use_relative_paths_in_depfile(const char* depfile)
{
- if (str_eq(conf->base_dir, "")) {
+ if (g_config.base_dir().empty()) {
cc_log("Base dir not set, skip using relative paths");
return; // nothing to do
}
char* token = strtok_r(buf, " \t", &saveptr);
while (token) {
char* relpath;
- if (is_absolute_path(token) && str_startswith(token, conf->base_dir)) {
+ if (is_absolute_path(token)
+ && str_startswith(token, g_config.base_dir().c_str())) {
relpath = make_relative_path(x_strdup(token));
result = true;
} else {
static void
update_manifest_file(void)
{
- if (!conf->direct_mode || !included_files || conf->read_only
- || conf->read_only_direct) {
+ if (!g_config.direct_mode() || !included_files || g_config.read_only()
+ || g_config.read_only_direct()) {
return;
}
digest_as_string(result_name, result_name_string);
cached_result_name = result_name;
cached_result_path = get_path_in_cache(result_name_string, ".result");
- stats_file = format("%s/%c/stats", conf->cache_dir, result_name_string[0]);
+ stats_file =
+ format("%s/%c/stats", g_config.cache_dir().c_str(), result_name_string[0]);
}
// Run the real compiler and put the result in cache.
args_add(args, "-o");
args_add(args, output_obj);
- if (conf->hard_link) {
+ if (g_config.hard_link()) {
// Workaround for Clang bug where it overwrites an existing object file
// when it's compiling an assembler file, see
// <https://bugs.llvm.org/show_bug.cgi?id=39782>.
x_unsetenv("DEPENDENCIES_OUTPUT");
x_unsetenv("SUNPRO_DEPENDENCIES");
- if (conf->run_second_cpp) {
+ if (g_config.run_second_cpp()) {
args_add(args, input_file);
} else {
args_add(args, i_tmpfile);
char* tmp_stderr;
int tmp_stderr_fd;
int status;
- if (!conf->depend_mode) {
+ if (!g_config.depend_mode()) {
tmp_stdout = format("%s/tmp.stdout", temp_dir());
tmp_stdout_fd = create_tmp_fd(&tmp_stdout);
tmp_stderr = format("%s/tmp.stderr", temp_dir());
assert(orig_args);
struct args* depend_mode_args = args_copy(orig_args);
args_strip(depend_mode_args, "--ccache-");
- add_prefix(depend_mode_args, conf->prefix_command);
+ add_prefix(depend_mode_args, g_config.prefix_command().c_str());
time_of_compilation = time(NULL);
status = execute(
failed();
}
- if (conf->depend_mode) {
+ if (g_config.depend_mode()) {
struct digest* result_name =
result_name_from_depfile(output_dep, depend_mode_hash);
if (!result_name) {
// Remove any CACHEDIR.TAG on the cache_dir level where it was located in
// previous ccache versions.
if (getpid() % 1000 == 0) {
- char* path = format("%s/CACHEDIR.TAG", conf->cache_dir);
+ char* path = format("%s/CACHEDIR.TAG", g_config.cache_dir().c_str());
x_unlink(path);
free(path);
}
int args_added = 2;
args_add(args, "-E");
- if (conf->keep_comments_cpp) {
+ if (g_config.keep_comments_cpp()) {
args_add(args, "-C");
args_added = 3;
}
args_add(args, input_file);
- add_prefix(args, conf->prefix_command_cpp);
+ add_prefix(args, g_config.prefix_command_cpp().c_str());
cc_log("Running preprocessor");
MTR_BEGIN("execute", "preprocessor");
status = execute(args->argv, path_stdout_fd, path_stderr_fd, &compiler_pid);
failed();
}
- if (conf->unify) {
+ if (g_config.unify()) {
// When we are doing the unifying tricks we need to include the input file
// name in the hash to get the warnings right.
hash_delimiter(hash, "unifyfilename");
} else {
// i_tmpfile needs the proper cpp_extension for the compiler to do its
// thing correctly
- i_tmpfile = format("%s.%s", path_stdout, conf->cpp_extension);
+ i_tmpfile = format("%s.%s", path_stdout, g_config.cpp_extension().c_str());
x_rename(path_stdout, i_tmpfile);
add_pending_tmp_file(i_tmpfile);
}
- if (conf->run_second_cpp) {
+ if (g_config.run_second_cpp()) {
free(path_stderr);
} else {
// If we are using the CPP trick, we need to remember this stderr data and
const char* path,
bool allow_command)
{
- if (str_eq(conf->compiler_check, "none")) {
+ if (g_config.compiler_check() == "none") {
// Do nothing.
- } else if (str_eq(conf->compiler_check, "mtime")) {
+ } else if (g_config.compiler_check() == "mtime") {
hash_delimiter(hash, "cc_mtime");
hash_int(hash, st->st_size);
hash_int(hash, st->st_mtime);
- } else if (str_startswith(conf->compiler_check, "string:")) {
+ } else if (util::starts_with(g_config.compiler_check(), "string:")) {
hash_delimiter(hash, "cc_hash");
- hash_string(hash, conf->compiler_check + strlen("string:"));
- } else if (str_eq(conf->compiler_check, "content") || !allow_command) {
+ hash_string(hash, g_config.compiler_check().c_str() + strlen("string:"));
+ } else if (g_config.compiler_check() == "content" || !allow_command) {
hash_delimiter(hash, "cc_content");
hash_file(hash, path);
} else { // command string
- bool ok =
- hash_multicommand_output(hash, conf->compiler_check, orig_args->argv[0]);
+ bool ok = hash_multicommand_output(
+ hash, g_config.compiler_check().c_str(), orig_args->argv[0]);
if (!ok) {
- fatal("Failure running compiler check command: %s", conf->compiler_check);
+ fatal("Failure running compiler check command: %s",
+ g_config.compiler_check().c_str());
}
}
}
// We have to hash the extension, as a .i file isn't treated the same by the
// compiler as a .ii file.
hash_delimiter(hash, "ext");
- hash_string(hash, conf->cpp_extension);
+ hash_string(hash, g_config.cpp_extension().c_str());
#ifdef _WIN32
const char* ext = strrchr(args->argv[0], '.');
hash_string(hash, base);
free(base);
- if (!(conf->sloppiness & SLOPPY_LOCALE)) {
+ if (!(g_config.sloppiness() & SLOPPY_LOCALE)) {
// Hash environment variables that may affect localization of compiler
// warning messages.
const char* envvars[] = {"LANG", "LC_ALL", "LC_CTYPE", "LC_MESSAGES", NULL};
}
// Possibly hash the current working directory.
- if (generating_debuginfo && conf->hash_dir) {
+ if (generating_debuginfo && g_config.hash_dir()) {
char* cwd = gnu_getcwd();
for (size_t i = 0; i < debug_prefix_maps_len; i++) {
char* map = debug_prefix_maps[i];
}
}
- if (!str_eq(conf->extra_files_to_hash, "")) {
- char* p = x_strdup(conf->extra_files_to_hash);
+ if (!g_config.extra_files_to_hash().empty()) {
+ char* p = x_strdup(g_config.extra_files_to_hash().c_str());
char* q = p;
char* path;
char* saveptr = NULL;
}
}
- if (!(conf->sloppiness & SLOPPY_FILE_MACRO)) {
+ if (!(g_config.sloppiness() & SLOPPY_FILE_MACRO)) {
// The source code file or an include file may contain __FILE__, so make
// sure that the hash is unique for the file name.
hash_delimiter(hash, "inputfile");
}
hash_delimiter(hash, "sourcecode");
- int result = hash_source_code_file(conf, hash, input_file);
+ int result = hash_source_code_file(g_config, hash, input_file);
if (result & HASH_SOURCE_CODE_ERROR) {
failed();
}
if (result & HASH_SOURCE_CODE_FOUND_TIME) {
cc_log("Disabling direct mode");
- conf->direct_mode = false;
+ g_config.set_direct_mode(false);
return NULL;
}
char manifest_name_string[DIGEST_STRING_BUFFER_SIZE];
hash_result_as_string(hash, manifest_name_string);
manifest_path = get_path_in_cache(manifest_name_string, ".manifest");
- manifest_stats_file =
- format("%s/%c/stats", conf->cache_dir, manifest_name_string[0]);
+ manifest_stats_file = format(
+ "%s/%c/stats", g_config.cache_dir().c_str(), manifest_name_string[0]);
cc_log("Looking for result name in %s", manifest_path);
MTR_BEGIN("manifest", "manifest_get");
- result_name = manifest_get(conf, manifest_path);
+ result_name = manifest_get(g_config, manifest_path);
MTR_END("manifest", "manifest_get");
if (result_name) {
cc_log("Got result name from manifest");
from_cache(enum fromcache_call_mode mode, bool put_result_in_manifest)
{
// The user might be disabling cache hits.
- if (conf->recache) {
+ if (g_config.recache()) {
return;
}
}
// Support user override of the compiler.
- if (!str_eq(conf->compiler, "")) {
- base = conf->compiler;
+ if (!g_config.compiler().empty()) {
+ base = x_strdup(g_config.compiler().c_str());
}
char* compiler = find_executable(base, MYNAME);
}
// These are too hard in direct mode.
- if (conf->direct_mode && compopt_too_hard_for_direct_mode(argv[i])) {
+ if (g_config.direct_mode() && compopt_too_hard_for_direct_mode(argv[i])) {
cc_log("Unsupported compiler option for direct mode: %s", argv[i]);
- conf->direct_mode = false;
+ g_config.set_direct_mode(false);
}
// -Xarch_* options are too hard.
arch_args[arch_args_size] = x_strdup(argv[i]); // It will leak.
++arch_args_size;
if (arch_args_size == 2) {
- conf->run_second_cpp = true;
+ g_config.set_run_second_cpp(true);
}
continue;
}
// TODO: Make argument to MF/MQ/MT relative.
args_add(dep_args, argv[i]);
continue;
- } else if (conf->direct_mode) {
+ } else if (g_config.direct_mode()) {
// -Wp, can be used to pass too hard options to the preprocessor.
// Hence, disable direct mode.
cc_log("Unsupported compiler option for direct mode: %s", argv[i]);
- conf->direct_mode = false;
+ g_config.set_direct_mode(false);
}
// Any other -Wp,* arguments are only relevant for the preprocessor.
continue;
}
- if (conf->sloppiness & SLOPPY_CLANG_INDEX_STORE
+ if (g_config.sloppiness() & SLOPPY_CLANG_INDEX_STORE
&& str_eq(argv[i], "-index-store-path")) {
// Xcode 9 or later calls Clang with this option. The given path includes
// a UUID that might lead to cache misses, especially when cache is
}
} // for
- if (generating_debuginfo && conf->unify) {
+ if (generating_debuginfo && g_config.unify()) {
cc_log("Generating debug info; disabling unify mode");
- conf->unify = false;
+ g_config.set_unify(false);
}
- if (generating_debuginfo_level_3 && !conf->run_second_cpp) {
+ if (generating_debuginfo_level_3 && !g_config.run_second_cpp()) {
cc_log("Generating debug info level 3; not compiling preprocessed code");
- conf->run_second_cpp = true;
+ g_config.set_run_second_cpp(true);
}
// See <http://gcc.gnu.org/onlinedocs/cpp/Environment-Variables.html>.
if (found_pch || found_fpch_preprocess) {
using_precompiled_header = true;
- if (!(conf->sloppiness & SLOPPY_TIME_MACROS)) {
+ if (!(g_config.sloppiness() & SLOPPY_TIME_MACROS)) {
cc_log(
"You have to specify \"time_macros\" sloppiness when using"
" precompiled headers to get direct hits");
actual_language && strstr(actual_language, "-header");
if (output_is_precompiled_header
- && !(conf->sloppiness & SLOPPY_PCH_DEFINES)) {
+ && !(g_config.sloppiness() & SLOPPY_PCH_DEFINES)) {
cc_log(
"You have to specify \"pch_defines,time_macros\" sloppiness when"
" creating precompiled headers");
goto out;
}
- if (!conf->run_second_cpp && str_eq(actual_language, "cu")) {
+ if (!g_config.run_second_cpp() && str_eq(actual_language, "cu")) {
cc_log("Using CUDA compiler; not compiling preprocessed code");
- conf->run_second_cpp = true;
+ g_config.set_run_second_cpp(true);
}
direct_i_file = language_is_preprocessed(actual_language);
- if (output_is_precompiled_header && !conf->run_second_cpp) {
+ if (output_is_precompiled_header && !g_config.run_second_cpp()) {
// It doesn't work to create the .gch from preprocessed source.
cc_log("Creating precompiled header; not compiling preprocessed code");
- conf->run_second_cpp = true;
+ g_config.set_run_second_cpp(true);
}
- if (str_eq(conf->cpp_extension, "")) {
+ if (g_config.cpp_extension().empty()) {
const char* p_language = p_language_for_language(actual_language);
- free(conf->cpp_extension);
- conf->cpp_extension = x_strdup(extension_for_language(p_language) + 1);
+ g_config.set_cpp_extension(extension_for_language(p_language) + 1);
}
// Don't try to second guess the compilers heuristics for stdout handling.
*compiler_args = args_copy(common_args);
args_extend(*compiler_args, compiler_only_args);
- if (conf->run_second_cpp) {
+ if (g_config.run_second_cpp()) {
args_extend(*compiler_args, cpp_args);
} else if (found_directives_only || found_rewrite_includes) {
// Need to pass the macros and any other preprocessor directives again.
unsigned max_files;
uint64_t max_size;
- char* stats_dir = format("%s/0", conf->cache_dir);
+ char* stats_dir = format("%s/0", g_config.cache_dir().c_str());
struct stat st;
if (stat(stats_dir, &st) == 0) {
stats_get_obsolete_limits(stats_dir, &max_files, &max_size);
max_size *= 16;
} else {
max_files = 0;
- max_size = conf->max_size;
+ max_size = g_config.max_size();
}
free(stats_dir);
}
if (max_files != 0) {
fprintf(f, "max_files = %u\n", max_files);
- conf->max_files = max_files;
+ g_config.set_max_files(max_files);
}
if (max_size != 0) {
char* size = format_parsable_size_with_suffix(max_size);
fprintf(f, "max_size = %s\n", size);
free(size);
- conf->max_size = max_size;
+ g_config.set_max_size(max_size);
}
fclose(f);
}
#endif
}
- conf_free(conf);
- MTR_BEGIN("config", "conf_create");
- conf = conf_create();
- MTR_END("config", "conf_create");
-
- char* errmsg;
char* p = getenv("CCACHE_CONFIGPATH");
if (p) {
primary_config_path = x_strdup(p);
} else {
secondary_config_path = format("%s/ccache.conf", TO_STRING(SYSCONFDIR));
MTR_BEGIN("config", "conf_read_secondary");
- if (!conf_read(conf, secondary_config_path, &errmsg)) {
- if (errno == 0) {
- // We could read the file but it contained errors.
- fatal("%s", errmsg);
- }
- // A missing config file in SYSCONFDIR is OK.
- free(errmsg);
- }
+ // A missing config file in SYSCONFDIR is OK so don't check return value.
+ g_config.update_from_file(secondary_config_path);
MTR_END("config", "conf_read_secondary");
- if (str_eq(conf->cache_dir, "")) {
+ if (g_config.cache_dir().empty()) {
fatal("configuration setting \"cache_dir\" must not be the empty string");
}
if ((p = getenv("CCACHE_DIR"))) {
- free(conf->cache_dir);
- conf->cache_dir = strdup(p);
+ g_config.set_cache_dir(p);
}
- if (str_eq(conf->cache_dir, "")) {
+ if (g_config.cache_dir().empty()) {
fatal("CCACHE_DIR must not be the empty string");
}
- primary_config_path = format("%s/ccache.conf", conf->cache_dir);
+ primary_config_path =
+ format("%s/ccache.conf", g_config.cache_dir().c_str());
}
bool should_create_initial_config = false;
MTR_BEGIN("config", "conf_read_primary");
- if (!conf_read(conf, primary_config_path, &errmsg)) {
- if (errno == 0) {
- // We could read the file but it contained errors.
- fatal("%s", errmsg);
- }
- if (!conf->disable) {
- should_create_initial_config = true;
- }
+ if (!g_config.update_from_file(primary_config_path) && !g_config.disable()) {
+ should_create_initial_config = true;
}
MTR_END("config", "conf_read_primary");
MTR_BEGIN("config", "conf_update_from_environment");
- if (!conf_update_from_environment(conf, &errmsg)) {
- fatal("%s", errmsg);
- }
+ g_config.update_from_environment();
MTR_END("config", "conf_update_from_environment");
if (should_create_initial_config) {
cc_log("=== CCACHE %s STARTED =========================================",
CCACHE_VERSION);
- if (conf->umask != UINT_MAX) {
- umask(conf->umask);
+ if (g_config.umask() != std::numeric_limits<uint32_t>::max()) {
+ umask(g_config.umask());
}
if (enable_internal_trace) {
void
cc_reset(void)
{
- conf_free(conf);
- conf = NULL;
+ Config new_config;
+ std::swap(g_config, new_config);
+
free(primary_config_path);
primary_config_path = NULL;
free(secondary_config_path);
stats_file = NULL;
output_is_precompiled_header = false;
- conf = conf_create();
seen_split_dwarf = false;
}
}
static void
-configuration_logger(const char* descr, const char* origin, void* context)
+configuration_logger(const std::string& key,
+ const std::string& value,
+ const std::string& origin)
+{
+ cc_bulklog(
+ "Config: (%s) %s = %s", origin.c_str(), key.c_str(), value.c_str());
+}
+
+static void
+configuration_printer(const std::string& key,
+ const std::string& value,
+ const std::string& origin)
{
- (void)context;
- cc_bulklog("Config: (%s) %s", origin, descr);
+ fmt::print("({}) {} = {}\n", origin, key, value);
}
static void ccache(int argc, char* argv[]) ATTR_NORETURN;
MTR_END("main", "find_compiler");
MTR_BEGIN("main", "clean_up_internal_tempdir");
- if (str_eq(conf->temporary_dir, "")) {
+ if (g_config.temporary_dir().empty()) {
clean_up_internal_tempdir();
}
MTR_END("main", "clean_up_internal_tempdir");
- if (!str_eq(conf->log_file, "") || conf->debug) {
- conf_print_items(conf, configuration_logger, NULL);
+ if (!g_config.log_file().empty() || g_config.debug()) {
+ g_config.visit_items(configuration_logger);
}
- if (conf->disable) {
+ if (g_config.disable()) {
cc_log("ccache is disabled");
failed();
}
cc_log("Hostname: %s", get_hostname());
cc_log("Working directory: %s", get_current_working_dir());
- conf->limit_multiple = MIN(MAX(conf->limit_multiple, 0.0), 1.0);
+ g_config.set_limit_multiple(MIN(MAX(g_config.limit_multiple(), 0.0), 1.0));
MTR_BEGIN("main", "guess_compiler");
guessed_compiler = guess_compiler(orig_args->argv[0]);
}
MTR_END("main", "process_args");
- if (conf->depend_mode
+ if (g_config.depend_mode()
&& (!generating_dependencies || str_eq(output_dep, "/dev/null")
- || !conf->run_second_cpp || conf->unify)) {
+ || !g_config.run_second_cpp() || g_config.unify())) {
cc_log("Disabling depend mode");
- conf->depend_mode = false;
+ g_config.set_depend_mode(false);
}
cc_log("Source file: %s", input_file);
exitfn_add_last(dump_debug_log_buffer_exitfn, output_obj);
FILE* debug_text_file = NULL;
- if (conf->debug) {
+ if (g_config.debug()) {
char* path = format("%s.ccache-input-text", output_obj);
debug_text_file = fopen(path, "w");
if (debug_text_file) {
bool put_result_in_manifest = false;
struct digest* result_name = NULL;
struct digest* result_name_from_manifest = NULL;
- if (conf->direct_mode) {
+ if (g_config.direct_mode()) {
cc_log("Trying direct lookup");
MTR_BEGIN("hash", "direct_hash");
result_name = calculate_result_name(preprocessor_args, direct_hash, 1);
}
}
- if (conf->read_only_direct) {
+ if (g_config.read_only_direct()) {
cc_log("Read-only direct mode; running real compiler");
failed();
}
- if (!conf->depend_mode) {
+ if (!g_config.depend_mode()) {
// Find the hash using the preprocessed output. Also updates
// included_files.
struct hash* cpp_hash = hash_copy(common_hash);
from_cache(FROMCACHE_CPP_MODE, put_result_in_manifest);
}
- if (conf->read_only) {
+ if (g_config.read_only()) {
cc_log("Read-only mode; running real compiler");
failed();
}
- add_prefix(compiler_args, conf->prefix_command);
+ add_prefix(compiler_args, g_config.prefix_command().c_str());
// In depend_mode, extend the direct hash.
- struct hash* depend_mode_hash = conf->depend_mode ? direct_hash : NULL;
+ struct hash* depend_mode_hash = g_config.depend_mode() ? direct_hash : NULL;
// Run real compiler, sending output to cache.
MTR_BEGIN("cache", "to_cache");
x_exit(0);
}
-static void
-configuration_printer(const char* descr, const char* origin, void* context)
-{
- assert(context);
- auto f = static_cast<FILE*>(context);
- fprintf(f, "(%s) %s\n", origin, descr);
-}
-
// The main program when not doing a compile.
static int
ccache_main_options(int argc, char* argv[])
case 'c': // --cleanup
initialize();
- clean_up_all(conf);
+ clean_up_all(g_config);
printf("Cleaned cache\n");
break;
case 'C': // --clear
initialize();
- wipe_all(conf);
+ wipe_all(g_config);
printf("Cleared cache\n");
break;
x_exit(0);
case 'k': // --get-config
- {
initialize();
- char* errmsg;
- if (!conf_print_value(conf, optarg, stdout, &errmsg)) {
- fatal("%s", errmsg);
- }
- } break;
+ fmt::print("{}\n", g_config.get_string_value(optarg));
+ break;
- case 'F': // --max-files
- {
+ case 'F': { // --max-files
initialize();
- char* errmsg;
- if (conf_set_value_in_file(
- primary_config_path, "max_files", optarg, &errmsg)) {
- unsigned files = atoi(optarg);
- if (files == 0) {
- printf("Unset cache file limit\n");
- } else {
- printf("Set cache file limit to %u\n", files);
- }
+ g_config.set_value_in_file(primary_config_path, "max_files", optarg);
+ unsigned files = atoi(optarg);
+ if (files == 0) {
+ printf("Unset cache file limit\n");
} else {
- fatal("could not set cache file limit: %s", errmsg);
+ printf("Set cache file limit to %u\n", files);
}
- } break;
+ break;
+ }
- case 'M': // --max-size
- {
+ case 'M': { // --max-size
initialize();
uint64_t size;
if (!parse_size_with_suffix(optarg, &size)) {
fatal("invalid size: %s", optarg);
}
- char* errmsg;
- if (conf_set_value_in_file(
- primary_config_path, "max_size", optarg, &errmsg)) {
- if (size == 0) {
- printf("Unset cache size limit\n");
- } else {
- char* s = format_human_readable_size(size);
- printf("Set cache size limit to %s\n", s);
- free(s);
- }
+ g_config.set_value_in_file(primary_config_path, "max_size", optarg);
+ if (size == 0) {
+ printf("Unset cache size limit\n");
} else {
- fatal("could not set cache size limit: %s", errmsg);
+ char* s = format_human_readable_size(size);
+ printf("Set cache size limit to %s\n", s);
+ free(s);
}
- } break;
+ break;
+ }
- case 'o': // --set-config
- {
+ case 'o': { // --set-config
initialize();
char* p = strchr(optarg, '=');
if (!p) {
}
char* key = x_strndup(optarg, p - optarg);
char* value = p + 1;
- char* errmsg;
- if (!conf_set_value_in_file(primary_config_path, key, value, &errmsg)) {
- fatal("%s", errmsg);
- }
+ g_config.set_value_in_file(primary_config_path, key, value);
free(key);
- } break;
+ break;
+ }
case 'p': // --show-config
initialize();
- conf_print_items(conf, configuration_printer, stdout);
+ g_config.visit_items(configuration_printer);
break;
case 's': // --show-stats
case 'x': // --show-compression
initialize();
- compress_stats(conf);
+ compress_stats(g_config);
break;
case 'z': // --zero-stats
int
ccache_main(int argc, char* argv[])
{
- // Check if we are being invoked as "ccache".
- char* program_name = x_basename(argv[0]);
- if (same_executable_name(program_name, MYNAME)) {
- if (argc < 2) {
- fputs(USAGE_TEXT, stderr);
- x_exit(1);
- }
- // If the first argument isn't an option, then assume we are being passed a
- // compiler name and options.
- if (argv[1][0] == '-') {
- return ccache_main_options(argc, argv);
+ try {
+ // Check if we are being invoked as "ccache".
+ char* program_name = x_basename(argv[0]);
+ if (same_executable_name(program_name, MYNAME)) {
+ if (argc < 2) {
+ fputs(USAGE_TEXT, stderr);
+ x_exit(1);
+ }
+ // If the first argument isn't an option, then assume we are being passed
+ // a compiler name and options.
+ if (argv[1][0] == '-') {
+ return ccache_main_options(argc, argv);
+ }
}
- }
- free(program_name);
+ free(program_name);
- ccache(argc, argv);
+ ccache(argc, argv);
+ } catch (const Error& e) {
+ fmt::print("ccache: error: {}\n", e.what());
+ return 1;
+ }
}
#include "system.hpp"
-#include "conf.hpp"
#include "counters.hpp"
#include "third_party/minitrace.h"
# define MYNAME "ccache"
#endif
+#define STRINGIFY(x) #x
+#define TO_STRING(x) STRINGIFY(x)
+
extern const char CCACHE_VERSION[];
// Statistics fields in storage order.
// Buffer size for I/O operations. Should be a multiple of 4 KiB.
#define READ_BUFFER_SIZE 65536
+class Config;
+
// ----------------------------------------------------------------------------
// args.c
// ----------------------------------------------------------------------------
// cleanup.c
-void clean_up_dir(struct conf* conf, const char* dir, double limit_multiple);
-void clean_up_all(struct conf* conf);
-void wipe_all(struct conf* conf);
+void clean_up_dir(const Config& config, const char* dir, double limit_multiple);
+void clean_up_all(const Config& config);
+void wipe_all(const Config& config);
// ----------------------------------------------------------------------------
// compress.c
-void compress_stats(struct conf* conf);
+void compress_stats(const Config& config);
// ----------------------------------------------------------------------------
// execute.c
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#include "Config.hpp"
#include "ccache.hpp"
#include <math.h>
// Clean up one cache subdirectory.
void
-clean_up_dir(struct conf* conf, const char* dir, double limit_multiple)
+clean_up_dir(const Config& config, const char* dir, double limit_multiple)
{
cc_log("Cleaning up cache directory %s", dir);
// When "max files" or "max cache size" is reached, one of the 16 cache
// subdirectories is cleaned up. When doing so, files are deleted (in LRU
// order) until the levels are below limit_multiple.
- double cache_size_float = round(conf->max_size * limit_multiple / 16);
+ double cache_size_float = round(config.max_size() * limit_multiple / 16);
cache_size_threshold = (uint64_t)cache_size_float;
- double files_in_cache_float = round(conf->max_files * limit_multiple / 16);
+ double files_in_cache_float = round(config.max_files() * limit_multiple / 16);
files_in_cache_threshold = (size_t)files_in_cache_float;
num_files = 0;
// Clean up all cache subdirectories.
void
-clean_up_all(struct conf* conf)
+clean_up_all(const Config& config)
{
for (int i = 0; i <= 0xF; i++) {
- char* dname = format("%s/%1x", conf->cache_dir, i);
- clean_up_dir(conf, dname, 1.0);
+ char* dname = format("%s/%1x", config.cache_dir().c_str(), i);
+ clean_up_dir(config, dname, 1.0);
free(dname);
}
}
// Wipe all cached files in all subdirectories.
void
-wipe_all(struct conf* conf)
+wipe_all(const Config& config)
{
for (int i = 0; i <= 0xF; i++) {
- char* dname = format("%s/%1x", conf->cache_dir, i);
+ char* dname = format("%s/%1x", config.cache_dir().c_str(), i);
wipe_dir(dname);
free(dname);
}
// Fix the counters.
- clean_up_all(conf);
+ clean_up_all(config);
}
// Process up all cache subdirectories.
void
-compress_stats(struct conf* conf)
+compress_stats(const Config& config)
{
on_disk_size = 0;
compr_size = 0;
incompr_size = 0;
for (int i = 0; i <= 0xF; i++) {
- char* dname = format("%s/%1x", conf->cache_dir, i);
+ char* dname = format("%s/%1x", config.cache_dir().c_str(), i);
traverse(dname, measure_fn);
free(dname);
}
#include "compression.hpp"
-#include "conf.hpp"
-
-extern struct conf* conf;
+#include "Config.hpp"
int8_t
compression_level_from_config(void)
{
- return conf->compression ? conf->compression_level : 0;
+ return g_config.compression() ? g_config.compression_level() : 0;
}
enum compression_type
compression_type_from_config(void)
{
- return conf->compression ? COMPR_TYPE_ZSTD : COMPR_TYPE_NONE;
+ return g_config.compression() ? COMPR_TYPE_ZSTD : COMPR_TYPE_NONE;
}
const char*
+++ /dev/null
-// Copyright (C) 2011-2019 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 "conf.hpp"
-
-#include "ccache.hpp"
-#include "confitems.hpp"
-#include "envtoconfitems.hpp"
-
-enum handle_conf_result {
- HANDLE_CONF_OK,
- HANDLE_CONF_UNKNOWN,
- HANDLE_CONF_FAIL
-};
-
-static const struct conf_item*
-find_conf(const char* name)
-{
- return confitems_get(name, strlen(name));
-}
-
-static const struct env_to_conf_item*
-find_env_to_conf(const char* name)
-{
- return envtoconfitems_get(name, strlen(name));
-}
-
-static enum handle_conf_result
-handle_conf_setting(struct conf* conf,
- const char* key,
- const char* value,
- char** errmsg,
- bool from_env_variable,
- bool negate_boolean,
- const char* origin)
-{
- const struct conf_item* item = find_conf(key);
- if (!item) {
- return HANDLE_CONF_UNKNOWN;
- }
-
- if (from_env_variable && item->parser == confitem_parse_bool) {
- // Special rule for boolean settings from the environment: "0", "false",
- // "disable" and "no" (case insensitive) are invalid, and all other values
- // mean true.
- //
- // Previously any value meant true, but this was surprising to users, who
- // might do something like CCACHE_DISABLE=0 and expect ccache to be
- // enabled.
- if (str_eq(value, "0") || strcasecmp(value, "false") == 0
- || strcasecmp(value, "disable") == 0 || strcasecmp(value, "no") == 0) {
- fatal("invalid boolean environment variable value \"%s\"", value);
- }
-
- bool* boolvalue = (bool*)((char*)conf + item->offset);
- *boolvalue = !negate_boolean;
- goto out;
- }
-
- if (!item->parser(value, (char*)conf + item->offset, errmsg)) {
- return HANDLE_CONF_FAIL;
- }
- if (item->verifier && !item->verifier((char*)conf + item->offset, errmsg)) {
- return HANDLE_CONF_FAIL;
- }
-
-out:
- conf->item_origins[item->number] = origin;
- return HANDLE_CONF_OK;
-}
-
-static bool
-parse_line(const char* line, char** key, char** value, char** errmsg)
-{
-#define SKIP_WS(x) \
- do { \
- while (isspace(*x)) { \
- ++x; \
- } \
- } while (false)
-
- *key = NULL;
- *value = NULL;
-
- const char* p = line;
- SKIP_WS(p);
- if (*p == '\0' || *p == '#') {
- return true;
- }
- const char* q = p;
- while (isalpha(*q) || *q == '_') {
- ++q;
- }
- *key = x_strndup(p, q - p);
- p = q;
- SKIP_WS(p);
- if (*p != '=') {
- *errmsg = x_strdup("missing equal sign");
- free(*key);
- *key = NULL;
- return false;
- }
- ++p;
-
- // Skip leading whitespace.
- SKIP_WS(p);
- q = p;
- while (*q) {
- ++q;
- }
- // Skip trailing whitespace.
- while (isspace(q[-1])) {
- --q;
- }
- *value = x_strndup(p, q - p);
-
- return true;
-
-#undef SKIP_WS
-}
-
-// Create a conf struct with default values.
-struct conf*
-conf_create(void)
-{
- auto conf = static_cast<struct conf*>(x_malloc(sizeof(struct conf)));
-
- conf->base_dir = x_strdup("");
- conf->cache_dir = format("%s/.ccache", get_home_directory());
- conf->cache_dir_levels = 2;
- conf->compiler = x_strdup("");
- conf->compiler_check = x_strdup("mtime");
- conf->compression = true;
- conf->compression_level = 0;
- conf->cpp_extension = x_strdup("");
- conf->debug = false;
- conf->depend_mode = false;
- conf->direct_mode = true;
- conf->disable = false;
- conf->extra_files_to_hash = x_strdup("");
- conf->file_clone = false;
- conf->hard_link = false;
- conf->hash_dir = true;
- conf->ignore_headers_in_manifest = x_strdup("");
- conf->keep_comments_cpp = false;
- conf->limit_multiple = 0.8;
- conf->log_file = x_strdup("");
- conf->max_files = 0;
- conf->max_size = (uint64_t)5 * 1000 * 1000 * 1000;
- conf->path = x_strdup("");
- conf->pch_external_checksum = false;
- conf->prefix_command = x_strdup("");
- conf->prefix_command_cpp = x_strdup("");
- conf->read_only = false;
- conf->read_only_direct = false;
- conf->recache = false;
- conf->run_second_cpp = true;
- conf->sloppiness = 0;
- conf->stats = true;
- conf->temporary_dir = x_strdup("");
- conf->umask = UINT_MAX; // Default: don't set umask.
- conf->unify = false;
-
- conf->item_origins =
- static_cast<const char**>(x_malloc(confitems_count() * sizeof(char*)));
- for (size_t i = 0; i < confitems_count(); ++i) {
- conf->item_origins[i] = "default";
- }
- return conf;
-}
-
-void
-conf_free(struct conf* conf)
-{
- if (!conf) {
- return;
- }
- free(conf->base_dir);
- free(conf->cache_dir);
- free(conf->compiler);
- free(conf->compiler_check);
- free(conf->cpp_extension);
- free(conf->extra_files_to_hash);
- free(conf->ignore_headers_in_manifest);
- free(conf->log_file);
- free(conf->path);
- free(conf->prefix_command);
- free(conf->prefix_command_cpp);
- free(conf->temporary_dir);
- free((void*)conf->item_origins); // Workaround for MSVC warning
- free(conf);
-}
-
-// Note: The path pointer is stored in conf, so path must outlive conf.
-//
-// On failure, if an I/O error occurred errno is set appropriately, otherwise
-// errno is set to zero indicating that config itself was invalid.
-bool
-conf_read(struct conf* conf, const char* path, char** errmsg)
-{
- assert(errmsg);
- *errmsg = NULL;
-
- FILE* f = fopen(path, "r");
- if (!f) {
- *errmsg = format("%s: %s", path, strerror(errno));
- return false;
- }
-
- unsigned line_number = 0;
- bool result = true;
- char buf[10000];
- while (fgets(buf, sizeof(buf), f)) {
- ++line_number;
-
- char* key;
- char* value;
- char* errmsg2;
- enum handle_conf_result hcr = HANDLE_CONF_OK;
- bool ok = parse_line(buf, &key, &value, &errmsg2);
- if (ok && key) { // key == NULL if comment or blank line.
- hcr = handle_conf_setting(conf, key, value, &errmsg2, false, false, path);
- ok = hcr != HANDLE_CONF_FAIL; // unknown is OK
- }
- free(key);
- free(value);
- if (!ok) {
- *errmsg = format("%s:%u: %s", path, line_number, errmsg2);
- free(errmsg2);
- errno = 0;
- result = false;
- goto out;
- }
- }
- if (ferror(f)) {
- *errmsg = x_strdup(strerror(errno));
- result = false;
- }
-
-out:
- fclose(f);
- return result;
-}
-
-bool
-conf_update_from_environment(struct conf* conf, char** errmsg)
-{
- for (char** p = environ; *p; ++p) {
- if (!str_startswith(*p, "CCACHE_")) {
- continue;
- }
- char* q = strchr(*p, '=');
- if (!q) {
- continue;
- }
-
- bool negate;
- size_t key_start;
- if (str_startswith(*p + 7, "NO")) {
- negate = true;
- key_start = 9;
- } else {
- negate = false;
- key_start = 7;
- }
- char* key = x_strndup(*p + key_start, q - *p - key_start);
-
- ++q; // Now points to the value.
-
- const struct env_to_conf_item* env_to_conf_item = find_env_to_conf(key);
- if (!env_to_conf_item) {
- free(key);
- continue;
- }
-
- char* errmsg2 = NULL;
- enum handle_conf_result hcr =
- handle_conf_setting(conf,
- env_to_conf_item->conf_name,
- q,
- &errmsg2,
- true,
- negate,
- "environment");
- if (hcr != HANDLE_CONF_OK) {
- *errmsg = format("%s: %s", key, errmsg2);
- free(errmsg2);
- free(key);
- return false;
- }
-
- free(key);
- }
-
- return true;
-}
-
-bool
-conf_set_value_in_file(const char* path,
- const char* key,
- const char* value,
- char** errmsg)
-{
- const struct conf_item* item = find_conf(key);
- if (!item) {
- *errmsg = format("unknown configuration option \"%s\"", key);
- return false;
- }
-
- char dummy[8] = {0}; // The maximum entry size in struct conf.
- if (!item->parser(value, (void*)dummy, errmsg)
- || (item->verifier && !item->verifier(value, errmsg))) {
- return false;
- }
-
- FILE* infile = fopen(path, "r");
- if (!infile) {
- *errmsg = format("%s: %s", path, strerror(errno));
- return false;
- }
-
- char* outpath = format("%s.tmp", path);
- FILE* outfile = create_tmp_file(&outpath, "w");
- if (!outfile) {
- *errmsg = format("%s: %s", outpath, strerror(errno));
- free(outpath);
- fclose(infile);
- return false;
- }
-
- bool found = false;
- char buf[10000];
- while (fgets(buf, sizeof(buf), infile)) {
- char* key2;
- char* value2;
- char* errmsg2;
- bool ok = parse_line(buf, &key2, &value2, &errmsg2);
- if (ok && key2 && str_eq(key2, key)) {
- found = true;
- fprintf(outfile, "%s = %s\n", key, value);
- } else {
- fputs(buf, outfile);
- }
- free(key2);
- free(value2);
- }
-
- if (!found) {
- fprintf(outfile, "%s = %s\n", key, value);
- }
-
- fclose(infile);
- fclose(outfile);
- if (x_rename(outpath, path) != 0) {
- *errmsg = format("rename %s to %s: %s", outpath, path, strerror(errno));
- return false;
- }
- free(outpath);
-
- return true;
-}
-
-bool
-conf_print_value(struct conf* conf, const char* key, FILE* file, char** errmsg)
-{
- const struct conf_item* item = find_conf(key);
- if (!item) {
- *errmsg = format("unknown configuration option \"%s\"", key);
- return false;
- }
- void* value = (char*)conf + item->offset;
- char* str = item->formatter(value);
- fprintf(file, "%s\n", str);
- free(str);
- return true;
-}
-
-static bool
-print_item(struct conf* conf,
- const char* key,
- void (*printer)(const char* descr,
- const char* origin,
- void* context),
- void* context)
-{
- const struct conf_item* item = find_conf(key);
- if (!item) {
- return false;
- }
- void* value = (char*)conf + item->offset;
- char* str = item->formatter(value);
- char* buf = x_strdup("");
- reformat(&buf, "%s = %s", key, str);
- printer(buf, conf->item_origins[item->number], context);
- free(buf);
- free(str);
- return true;
-}
-
-bool
-conf_print_items(struct conf* conf,
- void (*printer)(const char* descr,
- const char* origin,
- void* context),
- void* context)
-{
- bool ok = true;
- ok &= print_item(conf, "base_dir", printer, context);
- ok &= print_item(conf, "cache_dir", printer, context);
- ok &= print_item(conf, "cache_dir_levels", printer, context);
- ok &= print_item(conf, "compiler", printer, context);
- ok &= print_item(conf, "compiler_check", printer, context);
- ok &= print_item(conf, "compression", printer, context);
- ok &= print_item(conf, "compression_level", printer, context);
- ok &= print_item(conf, "cpp_extension", printer, context);
- ok &= print_item(conf, "debug", printer, context);
- ok &= print_item(conf, "depend_mode", printer, context);
- ok &= print_item(conf, "direct_mode", printer, context);
- ok &= print_item(conf, "disable", printer, context);
- ok &= print_item(conf, "extra_files_to_hash", printer, context);
- ok &= print_item(conf, "file_clone", printer, context);
- ok &= print_item(conf, "hard_link", printer, context);
- ok &= print_item(conf, "hash_dir", printer, context);
- ok &= print_item(conf, "ignore_headers_in_manifest", printer, context);
- ok &= print_item(conf, "keep_comments_cpp", printer, context);
- ok &= print_item(conf, "limit_multiple", printer, context);
- ok &= print_item(conf, "log_file", printer, context);
- ok &= print_item(conf, "max_files", printer, context);
- ok &= print_item(conf, "max_size", printer, context);
- ok &= print_item(conf, "path", printer, context);
- ok &= print_item(conf, "pch_external_checksum", printer, context);
- ok &= print_item(conf, "prefix_command", printer, context);
- ok &= print_item(conf, "prefix_command_cpp", printer, context);
- ok &= print_item(conf, "read_only", printer, context);
- ok &= print_item(conf, "read_only_direct", printer, context);
- ok &= print_item(conf, "recache", printer, context);
- ok &= print_item(conf, "run_second_cpp", printer, context);
- ok &= print_item(conf, "sloppiness", printer, context);
- ok &= print_item(conf, "stats", printer, context);
- ok &= print_item(conf, "temporary_dir", printer, context);
- ok &= print_item(conf, "umask", printer, context);
- ok &= print_item(conf, "unify", printer, context);
- return ok;
-}
+++ /dev/null
-// Copyright (C) 2011-2019 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 "system.hpp"
-
-struct conf
-{
- char* base_dir;
- char* cache_dir;
- unsigned cache_dir_levels;
- char* compiler;
- char* compiler_check;
- bool compression;
- int compression_level;
- char* cpp_extension;
- bool debug;
- bool depend_mode;
- bool direct_mode;
- bool disable;
- char* extra_files_to_hash;
- bool file_clone;
- bool hard_link;
- bool hash_dir;
- char* ignore_headers_in_manifest;
- bool keep_comments_cpp;
- double limit_multiple;
- char* log_file;
- unsigned max_files;
- uint64_t max_size;
- char* path;
- bool pch_external_checksum;
- char* prefix_command;
- char* prefix_command_cpp;
- bool read_only;
- bool read_only_direct;
- bool recache;
- bool run_second_cpp;
- unsigned sloppiness;
- bool stats;
- char* temporary_dir;
- unsigned umask;
- bool unify;
-
- const char** item_origins;
-};
-
-struct conf* conf_create(void);
-void conf_free(struct conf* conf);
-bool conf_read(struct conf* conf, const char* path, char** errmsg);
-bool conf_update_from_environment(struct conf* conf, char** errmsg);
-bool
-conf_print_value(struct conf* conf, const char* key, FILE* file, char** errmsg);
-bool conf_set_value_in_file(const char* path,
- const char* key,
- const char* value,
- char** errmsg);
-bool conf_print_items(struct conf* conf,
- void (*printer)(const char* descr,
- const char* origin,
- void* context),
- void* context);
+++ /dev/null
-// Copyright (C) 2018-2019 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 "confitems.hpp"
-
-#include "ccache.hpp"
-
-static char*
-format_string(const void* value)
-{
- const char* const* str = (const char* const*)value;
- return x_strdup(*str);
-}
-
-bool
-confitem_parse_bool(const char* str, void* result, char** errmsg)
-{
- bool* value = (bool*)result;
-
- if (str_eq(str, "true")) {
- *value = true;
- return true;
- } else if (str_eq(str, "false")) {
- *value = false;
- return true;
- } else {
- *errmsg = format("not a boolean value: \"%s\"", str);
- return false;
- }
-}
-
-char*
-confitem_format_bool(const void* value)
-{
- const bool* b = (const bool*)value;
- return x_strdup(*b ? "true" : "false");
-}
-
-bool
-confitem_parse_env_string(const char* str, void* result, char** errmsg)
-{
- char** value = (char**)result;
- free(*value);
- *value = subst_env_in_string(str, errmsg);
- return *value != NULL;
-}
-
-char*
-confitem_format_env_string(const void* value)
-{
- return format_string(value);
-}
-
-bool
-confitem_parse_double(const char* str, void* result, char** errmsg)
-{
- double* value = (double*)result;
- errno = 0;
- char* endptr;
- double x = strtod(str, &endptr);
- if (errno == 0 && *str != '\0' && *endptr == '\0') {
- *value = x;
- return true;
- } else {
- *errmsg = format("invalid floating point: \"%s\"", str);
- return false;
- }
-}
-
-char*
-confitem_format_double(const void* value)
-{
- const double* x = (const double*)value;
- return format("%.1f", *x);
-}
-
-bool
-confitem_parse_size(const char* str, void* result, char** errmsg)
-{
- uint64_t* value = (uint64_t*)result;
- uint64_t size;
- if (parse_size_with_suffix(str, &size)) {
- *value = size;
- return true;
- } else {
- *errmsg = format("invalid size: \"%s\"", str);
- return false;
- }
-}
-
-char*
-confitem_format_size(const void* value)
-{
- const uint64_t* size = (const uint64_t*)value;
- return format_parsable_size_with_suffix(*size);
-}
-
-bool
-confitem_parse_sloppiness(const char* str, void* result, char** errmsg)
-{
- unsigned* value = (unsigned*)result;
- if (!str) {
- return *value;
- }
-
- char* p = x_strdup(str);
- char* q = p;
- char* word;
- char* saveptr = NULL;
- while ((word = strtok_r(q, ", ", &saveptr))) {
- if (str_eq(word, "file_macro")) {
- *value |= SLOPPY_FILE_MACRO;
- } else if (str_eq(word, "file_stat_matches")) {
- *value |= SLOPPY_FILE_STAT_MATCHES;
- } else if (str_eq(word, "file_stat_matches_ctime")) {
- *value |= SLOPPY_FILE_STAT_MATCHES_CTIME;
- } else if (str_eq(word, "include_file_ctime")) {
- *value |= SLOPPY_INCLUDE_FILE_CTIME;
- } else if (str_eq(word, "include_file_mtime")) {
- *value |= SLOPPY_INCLUDE_FILE_MTIME;
- } else if (str_eq(word, "system_headers")
- || str_eq(word, "no_system_headers")) {
- *value |= SLOPPY_SYSTEM_HEADERS;
- } else if (str_eq(word, "pch_defines")) {
- *value |= SLOPPY_PCH_DEFINES;
- } else if (str_eq(word, "time_macros")) {
- *value |= SLOPPY_TIME_MACROS;
- } else if (str_eq(word, "clang_index_store")) {
- *value |= SLOPPY_CLANG_INDEX_STORE;
- } else if (str_eq(word, "locale")) {
- *value |= SLOPPY_LOCALE;
- } else {
- *errmsg = format("unknown sloppiness: \"%s\"", word);
- free(p);
- return false;
- }
- q = NULL;
- }
- free(p);
- return true;
-}
-
-char*
-confitem_format_sloppiness(const void* value)
-{
- const unsigned* sloppiness = (const unsigned*)value;
- char* s = x_strdup("");
- if (*sloppiness & SLOPPY_FILE_MACRO) {
- reformat(&s, "%sfile_macro, ", s);
- }
- if (*sloppiness & SLOPPY_INCLUDE_FILE_MTIME) {
- reformat(&s, "%sinclude_file_mtime, ", s);
- }
- if (*sloppiness & SLOPPY_INCLUDE_FILE_CTIME) {
- reformat(&s, "%sinclude_file_ctime, ", s);
- }
- if (*sloppiness & SLOPPY_TIME_MACROS) {
- reformat(&s, "%stime_macros, ", s);
- }
- if (*sloppiness & SLOPPY_PCH_DEFINES) {
- reformat(&s, "%spch_defines, ", s);
- }
- if (*sloppiness & SLOPPY_FILE_STAT_MATCHES) {
- reformat(&s, "%sfile_stat_matches, ", s);
- }
- if (*sloppiness & SLOPPY_FILE_STAT_MATCHES_CTIME) {
- reformat(&s, "%sfile_stat_matches_ctime, ", s);
- }
- if (*sloppiness & SLOPPY_SYSTEM_HEADERS) {
- reformat(&s, "%ssystem_headers, ", s);
- }
- if (*sloppiness & SLOPPY_CLANG_INDEX_STORE) {
- reformat(&s, "%sclang_index_store, ", s);
- }
- if (*sloppiness & SLOPPY_LOCALE) {
- reformat(&s, "%slocale, ", s);
- }
- if (*sloppiness) {
- // Strip last ", ".
- s[strlen(s) - 2] = '\0';
- }
- return s;
-}
-
-bool
-confitem_parse_string(const char* str, void* result, char** errmsg)
-{
- (void)errmsg;
-
- char** value = (char**)result;
- free(*value);
- *value = x_strdup(str);
- return true;
-}
-
-char*
-confitem_format_string(const void* value)
-{
- return format_string(value);
-}
-
-bool
-confitem_parse_umask(const char* str, void* result, char** errmsg)
-{
- unsigned* value = (unsigned*)result;
- if (str_eq(str, "")) {
- *value = UINT_MAX;
- return true;
- }
-
- errno = 0;
- char* endptr;
- *value = strtoul(str, &endptr, 8);
- if (errno == 0 && *str != '\0' && *endptr == '\0') {
- return true;
- } else {
- *errmsg = format("not an octal integer: \"%s\"", str);
- return false;
- }
-}
-
-char*
-confitem_format_umask(const void* value)
-{
- const unsigned* umask = (const unsigned*)value;
- if (*umask == UINT_MAX) {
- return x_strdup("");
- } else {
- return format("%03o", *umask);
- }
-}
-
-bool
-confitem_parse_int(const char* str, void* result, char** errmsg)
-{
- int* value = (int*)result;
- errno = 0;
- char* endptr;
- long x = strtol(str, &endptr, 10);
- if (errno == 0 && *str != '\0' && *endptr == '\0') {
- *value = x;
- return true;
- } else {
- *errmsg = format("invalid integer: \"%s\"", str);
- return false;
- }
-}
-
-char*
-confitem_format_int(const void* value)
-{
- const int* i = (const int*)value;
- return format("%d", *i);
-}
-
-bool
-confitem_parse_unsigned(const char* str, void* result, char** errmsg)
-{
- unsigned* value = (unsigned*)result;
- errno = 0;
- char* endptr;
- long x = strtol(str, &endptr, 10);
- if (errno == 0 && x >= 0 && *str != '\0' && *endptr == '\0') {
- *value = x;
- return true;
- } else {
- *errmsg = format("invalid unsigned integer: \"%s\"", str);
- return false;
- }
-}
-
-char*
-confitem_format_unsigned(const void* value)
-{
- const unsigned* i = (const unsigned*)value;
- return format("%u", *i);
-}
-
-bool
-confitem_verify_absolute_path(const void* value, char** errmsg)
-{
- const char* const* path = (const char* const*)value;
- assert(*path);
- if (str_eq(*path, "")) {
- // The empty string means "disable" in this case.
- return true;
- } else if (is_absolute_path(*path)) {
- return true;
- } else {
- *errmsg = format("not an absolute path: \"%s\"", *path);
- return false;
- }
-}
-
-bool
-confitem_verify_compression_level(const void* value, char** errmsg)
-{
- const int* level = (const int*)value;
- assert(level);
- if (*level >= -128 && *level <= 127) {
- return true;
- } else {
- *errmsg = format("compression level must be between -128 and 127");
- return false;
- }
-}
-
-bool
-confitem_verify_dir_levels(const void* value, char** errmsg)
-{
- const unsigned* levels = (const unsigned*)value;
- assert(levels);
- if (*levels >= 1 && *levels <= 8) {
- return true;
- } else {
- *errmsg = format("cache directory levels must be between 1 and 8");
- return false;
- }
-}
+++ /dev/null
-%language=ANSI-C
-%enum
-%struct-type
-%readonly-tables
-%define hash-function-name confitems_hash
-%define lookup-function-name confitems_get
-%define initializer-suffix ,0,0,NULL,NULL,NULL
-%{
-#include "confitems.hpp"
-#include "conf.hpp"
-
-#undef bool
-#define ITEM_ENTRY(name, type, verify_fn) \
- offsetof(struct conf, name), confitem_parse_ ## type, \
- confitem_format_ ## type, verify_fn
-#define ITEM(name, type) \
- ITEM_ENTRY(name, type, NULL)
-#define ITEM_V(name, type, verification) \
- ITEM_ENTRY(name, type, confitem_verify_ ## verification)
-%}
-struct conf_item;
-%%
-base_dir, ITEM_V(base_dir, env_string, absolute_path)
-cache_dir, ITEM(cache_dir, env_string)
-cache_dir_levels, ITEM_V(cache_dir_levels, unsigned, dir_levels)
-compiler, ITEM(compiler, string)
-compiler_check, ITEM(compiler_check, string)
-compression, ITEM(compression, bool)
-compression_level, ITEM_V(compression_level, int, compression_level)
-cpp_extension, ITEM(cpp_extension, string)
-debug, ITEM(debug, bool)
-depend_mode, ITEM(depend_mode, bool)
-direct_mode, ITEM(direct_mode, bool)
-disable, ITEM(disable, bool)
-extra_files_to_hash, ITEM(extra_files_to_hash, env_string)
-file_clone, ITEM(file_clone, bool)
-hard_link, ITEM(hard_link, bool)
-hash_dir, ITEM(hash_dir, bool)
-ignore_headers_in_manifest, ITEM(ignore_headers_in_manifest, env_string)
-keep_comments_cpp, ITEM(keep_comments_cpp, bool)
-limit_multiple, ITEM(limit_multiple, double)
-log_file, ITEM(log_file, env_string)
-max_files, ITEM(max_files, unsigned)
-max_size, ITEM(max_size, size)
-path, ITEM(path, env_string)
-pch_external_checksum, ITEM(pch_external_checksum, bool)
-prefix_command, ITEM(prefix_command, env_string)
-prefix_command_cpp, ITEM(prefix_command_cpp, env_string)
-read_only, ITEM(read_only, bool)
-read_only_direct, ITEM(read_only_direct, bool)
-recache, ITEM(recache, bool)
-run_second_cpp, ITEM(run_second_cpp, bool)
-sloppiness, ITEM(sloppiness, sloppiness)
-stats, ITEM(stats, bool)
-temporary_dir, ITEM(temporary_dir, env_string)
-umask, ITEM(umask, umask)
-unify, ITEM(unify, bool)
+++ /dev/null
-// Copyright (C) 2018-2019 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 "system.hpp"
-
-typedef bool (*conf_item_parser)(const char* str, void* result, char** errmsg);
-typedef bool (*conf_item_verifier)(const void* value, char** errmsg);
-typedef char* (*conf_item_formatter)(const void* value);
-
-struct conf_item
-{
- const char* name;
- size_t number;
- size_t offset;
- conf_item_parser parser;
- conf_item_formatter formatter;
- conf_item_verifier verifier;
-};
-
-bool confitem_parse_bool(const char* str, void* result, char** errmsg);
-char* confitem_format_bool(const void* value);
-
-bool confitem_parse_env_string(const char* str, void* result, char** errmsg);
-char* confitem_format_env_string(const void* value);
-
-bool confitem_parse_double(const char* str, void* result, char** errmsg);
-char* confitem_format_double(const void* value);
-
-bool confitem_parse_size(const char* str, void* result, char** errmsg);
-char* confitem_format_size(const void* value);
-
-bool confitem_parse_sloppiness(const char* str, void* result, char** errmsg);
-char* confitem_format_sloppiness(const void* value);
-
-bool confitem_parse_string(const char* str, void* result, char** errmsg);
-char* confitem_format_string(const void* value);
-
-bool confitem_parse_umask(const char* str, void* result, char** errmsg);
-char* confitem_format_umask(const void* value);
-
-bool confitem_parse_int(const char* str, void* result, char** errmsg);
-char* confitem_format_int(const void* value);
-
-bool confitem_parse_unsigned(const char* str, void* result, char** errmsg);
-char* confitem_format_unsigned(const void* value);
-
-bool confitem_verify_absolute_path(const void* value, char** errmsg);
-bool confitem_verify_compression_level(const void* value, char** errmsg);
-bool confitem_verify_dir_levels(const void* value, char** errmsg);
-
-const struct conf_item* confitems_get(const char* str, size_t len);
-size_t confitems_count(void);
+++ /dev/null
-%language=ANSI-C
-%enum
-%struct-type
-%readonly-tables
-%define hash-function-name envtoconfitems_hash
-%define lookup-function-name envtoconfitems_get
-%define slot-name env_name
-%define initializer-suffix ,""
-%{
-#include "envtoconfitems.hpp"
-%}
-struct env_to_conf_item;
-%%
-BASEDIR, "base_dir"
-CC, "compiler"
-COMMENTS, "keep_comments_cpp"
-COMPILER, "compiler"
-COMPILERCHECK, "compiler_check"
-COMPRESS, "compression"
-COMPRESSLEVEL, "compression_level"
-CPP2, "run_second_cpp"
-DEBUG, "debug"
-DEPEND, "depend_mode"
-DIR, "cache_dir"
-DIRECT, "direct_mode"
-DISABLE, "disable"
-EXTENSION, "cpp_extension"
-EXTRAFILES, "extra_files_to_hash"
-FILECLONE, "file_clone"
-HARDLINK, "hard_link"
-HASHDIR, "hash_dir"
-IGNOREHEADERS, "ignore_headers_in_manifest"
-LIMIT_MULTIPLE, "limit_multiple"
-LOGFILE, "log_file"
-MAXFILES, "max_files"
-MAXSIZE, "max_size"
-NLEVELS, "cache_dir_levels"
-PATH, "path"
-PCH_EXTSUM, "pch_external_checksum"
-PREFIX, "prefix_command"
-PREFIX_CPP, "prefix_command_cpp"
-READONLY, "read_only"
-READONLY_DIRECT, "read_only_direct"
-RECACHE, "recache"
-SLOPPINESS, "sloppiness"
-STATS, "stats"
-TEMPDIR, "temporary_dir"
-UMASK, "umask"
-UNIFY, "unify"
+++ /dev/null
-// Copyright (C) 2018-2019 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 "system.hpp"
-
-struct env_to_conf_item
-{
- const char* env_name;
- const char* conf_name;
-};
-
-const struct env_to_conf_item* envtoconfitems_get(const char* str, size_t len);
-
-size_t envtoconfitems_count(void);
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#include "Config.hpp"
#include "ccache.hpp"
-extern struct conf* conf;
-
-static char*
-find_executable_in_path(const char* name, const char* exclude_name, char* path);
+static char* find_executable_in_path(const char* name,
+ const char* exclude_name,
+ const char* path);
#ifdef _WIN32
// Re-create a win32 command line string based on **argv.
return x_strdup(name);
}
- char* path = conf->path;
+ const char* path = g_config.path().c_str();
if (str_eq(path, "")) {
path = getenv("PATH");
}
}
static char*
-find_executable_in_path(const char* name, const char* exclude_name, char* path)
+find_executable_in_path(const char* name,
+ const char* exclude_name,
+ const char* path)
{
- path = x_strdup(path);
+ char* path_buf = x_strdup(path);
// Search the path looking for the first compiler of the right name that
// isn't us.
char* saveptr = NULL;
- for (char* tok = strtok_r(path, PATH_DELIM, &saveptr); tok;
+ for (char* tok = strtok_r(path_buf, PATH_DELIM, &saveptr); tok;
tok = strtok_r(NULL, PATH_DELIM, &saveptr)) {
#ifdef _WIN32
char namebuf[MAX_PATH];
}
(void)exclude_name;
if (ret) {
- free(path);
+ free(path_buf);
return x_strdup(namebuf);
}
#else
}
// Found it!
- free(path);
+ free(path_buf);
return fname;
}
free(fname);
#endif
}
- free(path);
+ free(path_buf);
return NULL;
}
// Hash a string. Returns a bitmask of HASH_SOURCE_CODE_* results.
int
-hash_source_code_string(struct conf* conf,
+hash_source_code_string(const Config& config,
struct hash* hash,
const char* str,
size_t len,
// Check for __DATE__ and __TIME__ if the sloppiness configuration tells us
// we should.
- if (!(conf->sloppiness & SLOPPY_TIME_MACROS)) {
+ if (!(config.sloppiness() & SLOPPY_TIME_MACROS)) {
result |= check_for_temporal_macros(str, len);
}
// Hash a file ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_*
// results.
int
-hash_source_code_file(struct conf* conf, struct hash* hash, const char* path)
+hash_source_code_file(const Config& config, struct hash* hash, const char* path)
{
if (is_precompiled_header(path)) {
if (hash_file(hash, path)) {
if (!read_file(path, 0, &data, &size)) {
return HASH_SOURCE_CODE_ERROR;
}
- int result = hash_source_code_string(conf, hash, data, size, path);
+ int result = hash_source_code_string(config, hash, data, size, path);
free(data);
return result;
}
#include "system.hpp"
-#include "conf.hpp"
+#include "Config.hpp"
#include "hash.hpp"
#include <inttypes.h>
#define HASH_SOURCE_CODE_FOUND_TIME 4
int check_for_temporal_macros(const char* str, size_t len);
-int hash_source_code_string(struct conf* conf,
+int hash_source_code_string(const Config& config,
struct hash* hash,
const char* str,
size_t len,
const char* path);
-int
-hash_source_code_file(struct conf* conf, struct hash* hash, const char* path);
+int hash_source_code_file(const Config& config,
+ struct hash* hash,
+ const char* path);
bool hash_command_output(struct hash* hash,
const char* command,
const char* compiler);
}
static bool
-verify_result(struct conf* conf,
+verify_result(const Config& config,
struct manifest* mf,
struct result* result,
struct hashtable* stated_files,
return false;
}
- if (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) {
- if (!(conf->sloppiness & SLOPPY_FILE_STAT_MATCHES_CTIME)) {
+ if (config.sloppiness() & SLOPPY_FILE_STAT_MATCHES) {
+ if (!(config.sloppiness() & SLOPPY_FILE_STAT_MATCHES_CTIME)) {
if (fi->mtime == st->mtime && fi->ctime == st->ctime) {
cc_log("mtime/ctime hit for %s", path);
continue;
auto actual = static_cast<digest*>(hashtable_search(hashed_files, path));
if (!actual) {
struct hash* hash = hash_init();
- int ret = hash_source_code_file(conf, hash, path);
+ int ret = hash_source_code_file(config, hash, path);
if (ret & HASH_SOURCE_CODE_ERROR) {
cc_log("Failed hashing %s", path);
hash_free(hash);
// Try to get the result name from a manifest file. Caller frees. Returns NULL
// on failure.
struct digest*
-manifest_get(struct conf* conf, const char* manifest_path)
+manifest_get(const Config& config, const char* manifest_path)
{
char* errmsg;
struct manifest* mf = read_manifest(manifest_path, &errmsg);
struct digest* name = NULL;
for (uint32_t i = mf->n_results; i > 0; i--) {
if (verify_result(
- conf, mf, &mf->results[i - 1], stated_files, hashed_files)) {
+ config, mf, &mf->results[i - 1], stated_files, hashed_files)) {
name = static_cast<digest*>(x_malloc(sizeof(digest)));
*name = mf->results[i - 1].name;
goto out;
#include "system.hpp"
-#include "conf.hpp"
+#include "Config.hpp"
#include "hashutil.hpp"
#include "third_party/hashtable.h"
extern const char MANIFEST_MAGIC[4];
#define MANIFEST_VERSION 2
-struct digest* manifest_get(struct conf* conf, const char* manifest_path);
+struct digest* manifest_get(const Config& config, const char* manifest_path);
bool manifest_put(const char* manifest_path,
struct digest* result_digest,
struct hashtable* included_files);
#include "result.hpp"
+#include "Config.hpp"
#include "ccache.hpp"
#include "common_header.hpp"
#include "compression.hpp"
//
// 1: Introduced in ccache 4.0.
-extern const struct conf* conf;
extern char* stats_file;
const char RESULT_MAGIC[4] = {'c', 'C', 'r', 'S'};
static bool
copy_raw_file(const char* source, const char* dest, bool to_cache)
{
- if (conf->file_clone) {
+ if (g_config.file_clone()) {
cc_log("Cloning %s to %s", source, dest);
if (clone_file(source, dest, to_cache)) {
return true;
}
cc_log("Failed to clone: %s", strerror(errno));
}
- if (conf->hard_link) {
+ if (g_config.hard_link()) {
x_try_unlink(dest);
cc_log("Hard linking %s to %s", source, dest);
int ret = link(source, dest);
static bool
should_store_raw_file(const char* suffix)
{
- if (!conf->file_clone && !conf->hard_link) {
+ if (!g_config.file_clone() && !g_config.hard_link()) {
return false;
}
#include "system.hpp"
-#include "conf.hpp"
+#include <cstdio>
extern const char RESULT_MAGIC[4];
#define RESULT_VERSION 1
#include <unistd.h>
extern char* stats_file;
-extern struct conf* conf;
extern unsigned lock_staleness_limit;
extern char* primary_config_path;
extern char* secondary_config_path;
char* fname;
if (dir == -1) {
- fname = format("%s/stats", conf->cache_dir);
+ fname = format("%s/stats", g_config.cache_dir().c_str());
} else {
- fname = format("%s/%1x/stats", conf->cache_dir, dir);
+ fname = format("%s/%1x/stats", g_config.cache_dir().c_str(), dir);
}
counters->data[STATS_ZEROTIMESTAMP] = 0; // Don't add
static void
stats_flush_to_file(const char* sfile, struct counters* updates)
{
- assert(conf);
-
- if (!conf->stats) {
+ if (!g_config.stats()) {
return;
}
// A NULL sfile means that we didn't get past calculate_object_hash(), so
// we just choose one of stats files in the 16 subdirectories.
- stats_dir = format("%s/%x", conf->cache_dir, hash_from_int(getpid()) % 16);
+ stats_dir = format(
+ "%s/%x", g_config.cache_dir().c_str(), hash_from_int(getpid()) % 16);
sfile = format("%s/stats", stats_dir);
free(stats_dir);
}
stats_write(sfile, counters);
lockfile_release(sfile);
- if (!str_eq(conf->log_file, "") || conf->debug) {
+ if (!g_config.log_file().empty() || g_config.debug()) {
for (int i = 0; i < STATS_END; ++i) {
if (updates->data[stats_info[i].stat] != 0
&& !(stats_info[i].flags & FLAG_NOZERO)) {
char* subdir = x_dirname(sfile);
bool need_cleanup = false;
- if (conf->max_files != 0
- && counters->data[STATS_NUMFILES] > conf->max_files / 16) {
+ if (g_config.max_files() != 0
+ && counters->data[STATS_NUMFILES] > g_config.max_files() / 16) {
cc_log("Need to clean up %s since it holds %u files (limit: %u files)",
subdir,
counters->data[STATS_NUMFILES],
- conf->max_files / 16);
+ g_config.max_files() / 16);
need_cleanup = true;
}
- if (conf->max_size != 0
- && counters->data[STATS_TOTALSIZE] > conf->max_size / 1024 / 16) {
+ if (g_config.max_size() != 0
+ && counters->data[STATS_TOTALSIZE] > g_config.max_size() / 1024 / 16) {
cc_log("Need to clean up %s since it holds %u KiB (limit: %lu KiB)",
subdir,
counters->data[STATS_TOTALSIZE],
- (unsigned long)conf->max_size / 1024 / 16);
+ (unsigned long)g_config.max_size() / 1024 / 16);
need_cleanup = true;
}
if (need_cleanup) {
- clean_up_dir(conf, subdir, conf->limit_multiple);
+ clean_up_dir(g_config, subdir, g_config.limit_multiple());
}
free(subdir);
void
stats_summary(void)
{
- assert(conf);
-
struct counters* counters = counters_init(STATS_END);
time_t last_updated;
stats_collect(counters, &last_updated);
- printf("cache directory %s\n", conf->cache_dir);
+ printf("cache directory %s\n",
+ g_config.cache_dir().c_str());
printf("primary config %s\n",
primary_config_path ? primary_config_path : "");
printf("secondary config (readonly) %s\n",
}
}
- if (conf->max_files != 0) {
- printf("max files %8u\n", conf->max_files);
+ if (g_config.max_files() != 0) {
+ printf("max files %8u\n", g_config.max_files());
}
- if (conf->max_size != 0) {
- char* value = format_size(conf->max_size);
+ if (g_config.max_size() != 0) {
+ char* value = format_size(g_config.max_size());
printf("max cache size %s\n", value);
free(value);
}
void
stats_print(void)
{
- assert(conf);
-
struct counters* counters = counters_init(STATS_END);
time_t last_updated;
stats_collect(counters, &last_updated);
void
stats_zero(void)
{
- assert(conf);
-
- char* fname = format("%s/stats", conf->cache_dir);
+ char* fname = format("%s/stats", g_config.cache_dir().c_str());
x_unlink(fname);
free(fname);
for (int dir = 0; dir <= 0xF; dir++) {
struct counters* counters = counters_init(STATS_END);
struct stat st;
- fname = format("%s/%1x/stats", conf->cache_dir, dir);
+ fname = format("%s/%1x/stats", g_config.cache_dir().c_str(), dir);
if (stat(fname, &st) != 0) {
// No point in trying to reset the stats file if it doesn't exist.
free(fname);
#include "util.hpp"
+#include "Config.hpp"
#include "Error.hpp"
#include "ccache.hpp"
# include <windows.h>
#endif
-extern const struct conf* conf;
-
-// Destination for conf->log_file.
+// Destination for g_config.log_file.
static FILE* logfile;
// Whether to use syslog() instead.
static bool use_syslog;
-// Buffer used for logs in conf->debug mode.
+// Buffer used for logs in debug mode.
static char* debug_log_buffer;
// Allocated debug_log_buffer size.
if (debug_log_buffer || logfile || use_syslog) {
return true;
}
- assert(conf);
- if (conf->debug) {
+ if (g_config.debug()) {
debug_log_buffer_capacity = DEBUG_LOG_BUFFER_MARGIN;
debug_log_buffer = static_cast<char*>(x_malloc(debug_log_buffer_capacity));
debug_log_size = 0;
}
- if (str_eq(conf->log_file, "")) {
- return conf->debug;
+ if (g_config.log_file().empty()) {
+ return g_config.debug();
}
#ifdef HAVE_SYSLOG
- if (str_eq(conf->log_file, "syslog")) {
+ if (g_config.log_file() == "syslog") {
use_syslog = true;
openlog("ccache", LOG_PID, LOG_USER);
return true;
}
#endif
- logfile = fopen(conf->log_file, "a");
+ logfile = fopen(g_config.log_file().c_str(), "a");
if (logfile) {
#ifndef _WIN32
set_cloexec_flag(fileno(logfile));
// Note: Can't call fatal() since that would lead to recursion.
fprintf(stderr,
"ccache: error: Failed to write to %s: %s\n",
- conf->log_file,
+ g_config.log_file().c_str(),
strerror(errno));
x_exit(EXIT_FAILURE);
}
char*
get_path_in_cache(const char* name, const char* suffix)
{
- char* path = x_strdup(conf->cache_dir);
- for (unsigned i = 0; i < conf->cache_dir_levels; ++i) {
+ char* path = x_strdup(g_config.cache_dir().c_str());
+ for (unsigned i = 0; i < g_config.cache_dir_levels(); ++i) {
char* p = format("%s/%c", path, name[i]);
free(path);
path = p;
}
- char* result = format("%s/%s%s", path, name + conf->cache_dir_levels, suffix);
+ char* result =
+ format("%s/%s%s", path, name + g_config.cache_dir_levels(), suffix);
free(path);
return result;
}
static const char COLOR_END[] = "\x1b[m";
static const char COLOR_GREEN[] = "\x1b[1;32m";
static const char COLOR_RED[] = "\x1b[1;31m";
-static char CONFIG_PATH_ENV[] = "CCACHE_CONFIG_PATH=/dev/null";
#define COLOR(tty, color) ((tty) ? COLOR_##color : "")
cct_chdir(name);
current_test = name;
- putenv(CONFIG_PATH_ENV);
+ x_setenv("CCACHE_CONFIG_PATH", "/dev/null");
cc_reset();
}
#include "framework.hpp"
#define CATCH_CONFIG_RUNNER
-#include "third_party/catch.hpp"
+#include <catch.hpp>
unsigned suite_args(unsigned);
unsigned suite_argument_processing(unsigned);
&suite_compopt,
&suite_compr_type_none,
&suite_compr_type_zstd,
- &suite_conf,
&suite_counters,
&suite_hash,
&suite_hashutil,
NULL};
#ifdef _WIN32
- putenv("CCACHE_DETECT_SHEBANG=1");
+ x_setenv("CCACHE_DETECT_SHEBANG", "1");
#endif
char* testdir = format("testdir.%d", (int)getpid());
--- /dev/null
+// Copyright (C) 2011-2019 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 "../src/Config.hpp"
+#include "../src/Error.hpp"
+#include "../src/ccache.hpp"
+#include "../src/util.hpp"
+
+#include <catch.hpp>
+#include <fmt/core.h>
+#include <limits>
+#include <string>
+#include <vector>
+
+using Catch::Equals;
+
+TEST_CASE("Config: default values")
+{
+ Config config;
+ CHECK(config.base_dir().empty());
+ CHECK(config.cache_dir() == std::string(get_home_directory()) + "/.ccache");
+ CHECK(config.cache_dir_levels() == 2);
+ CHECK(config.compiler().empty());
+ CHECK(config.compiler_check() == "mtime");
+ CHECK(config.compression());
+ CHECK(config.compression_level() == 0);
+ CHECK(config.cpp_extension().empty());
+ CHECK(!config.debug());
+ CHECK(!config.depend_mode());
+ CHECK(config.direct_mode());
+ CHECK(!config.disable());
+ CHECK(config.extra_files_to_hash().empty());
+ CHECK(!config.file_clone());
+ CHECK(!config.hard_link());
+ CHECK(config.hash_dir());
+ CHECK(config.ignore_headers_in_manifest().empty());
+ CHECK_FALSE(config.keep_comments_cpp());
+ CHECK(config.limit_multiple() == Approx(0.8));
+ CHECK(config.log_file().empty());
+ CHECK(config.max_files() == 0);
+ CHECK(config.max_size() == static_cast<uint64_t>(5) * 1000 * 1000 * 1000);
+ CHECK(config.path().empty());
+ CHECK_FALSE(config.pch_external_checksum());
+ CHECK(config.prefix_command().empty());
+ CHECK(config.prefix_command_cpp().empty());
+ CHECK_FALSE(config.read_only());
+ CHECK_FALSE(config.read_only_direct());
+ CHECK_FALSE(config.recache());
+ CHECK(config.run_second_cpp());
+ CHECK(config.sloppiness() == 0);
+ CHECK(config.stats());
+ CHECK(config.temporary_dir().empty());
+ CHECK(config.umask() == std::numeric_limits<uint32_t>::max());
+ CHECK_FALSE(config.unify());
+}
+
+TEST_CASE("Config::update_from_file")
+{
+ const char user[] = "rabbit";
+ x_setenv("USER", user);
+
+#ifndef _WIN32
+ std::string base_dir = fmt::format("/{0}/foo/{0}", user);
+#else
+ std::string base_dir = fmt::format("C:/{0}/foo/{0}", user);
+#endif
+
+ util::write_file(
+ "ccache.conf",
+ "base_dir = " + base_dir + "\n"
+ "cache_dir=\n"
+ "cache_dir = $USER$/${USER}/.ccache\n"
+ "\n"
+ "\n"
+ " #A comment\n"
+ " cache_dir_levels = 4\n"
+ "\t compiler = foo\n"
+ "compiler_check = none\n"
+ "compression=false\n"
+ "compression_level= 2\n"
+ "cpp_extension = .foo\n"
+ "depend_mode = true\n"
+ "direct_mode = false\n"
+ "disable = true\n"
+ "extra_files_to_hash = a:b c:$USER\n"
+ "file_clone = true\n"
+ "hard_link = true\n"
+ "hash_dir = false\n"
+ "ignore_headers_in_manifest = a:b/c\n"
+ "keep_comments_cpp = true\n"
+ "limit_multiple = 1.0\n"
+ "log_file = $USER${USER} \n"
+ "max_files = 17\n"
+ "max_size = 123M\n"
+ "path = $USER.x\n"
+ "pch_external_checksum = true\n"
+ "prefix_command = x$USER\n"
+ "prefix_command_cpp = y\n"
+ "read_only = true\n"
+ "read_only_direct = true\n"
+ "recache = true\n"
+ "run_second_cpp = false\n"
+ "sloppiness = file_macro ,time_macros, "
+ "include_file_mtime,include_file_ctime,file_stat_matches,file_stat_"
+ "matches_ctime,pch_defines , "
+ "no_system_headers,system_headers,clang_index_store\n"
+ "stats = false\n"
+ "temporary_dir = ${USER}_foo\n"
+ "umask = 777\n"
+ "unify = true"); // Note: no newline.
+
+ Config config;
+ REQUIRE(config.update_from_file("ccache.conf"));
+ CHECK(config.base_dir() == base_dir);
+ CHECK(config.cache_dir() == fmt::format("{0}$/{0}/.ccache", user));
+ CHECK(config.cache_dir_levels() == 4);
+ CHECK(config.compiler() == "foo");
+ CHECK(config.compiler_check() == "none");
+ CHECK_FALSE(config.compression());
+ CHECK(config.compression_level() == 2);
+ CHECK(config.cpp_extension() == ".foo");
+ CHECK(config.depend_mode());
+ CHECK_FALSE(config.direct_mode());
+ CHECK(config.disable());
+ CHECK(config.extra_files_to_hash() == fmt::format("a:b c:{}", user));
+ CHECK(config.file_clone());
+ CHECK(config.hard_link());
+ CHECK_FALSE(config.hash_dir());
+ CHECK(config.ignore_headers_in_manifest() == "a:b/c");
+ CHECK(config.keep_comments_cpp());
+ CHECK(config.limit_multiple() == Approx(1.0));
+ CHECK(config.log_file() == fmt::format("{0}{0}", user));
+ CHECK(config.max_files() == 17);
+ CHECK(config.max_size() == 123 * 1000 * 1000);
+ CHECK(config.path() == fmt::format("{}.x", user));
+ CHECK(config.pch_external_checksum());
+ CHECK(config.prefix_command() == fmt::format("x{}", user));
+ CHECK(config.prefix_command_cpp() == "y");
+ CHECK(config.read_only());
+ CHECK(config.read_only_direct());
+ CHECK(config.recache());
+ CHECK_FALSE(config.run_second_cpp());
+ CHECK(config.sloppiness()
+ == (SLOPPY_INCLUDE_FILE_MTIME | SLOPPY_INCLUDE_FILE_CTIME
+ | SLOPPY_FILE_MACRO | SLOPPY_TIME_MACROS | SLOPPY_FILE_STAT_MATCHES
+ | SLOPPY_FILE_STAT_MATCHES_CTIME | SLOPPY_SYSTEM_HEADERS
+ | SLOPPY_PCH_DEFINES | SLOPPY_CLANG_INDEX_STORE));
+ CHECK_FALSE(config.stats());
+ CHECK(config.temporary_dir() == fmt::format("{}_foo", user));
+ CHECK(config.umask() == 0777);
+ CHECK(config.unify());
+}
+
+TEST_CASE("Config::update_from_file, error handling")
+{
+ Config config;
+ unlink("ccache.conf"); // Make sure it doesn't exist.
+
+ SECTION("missing equal sign")
+ {
+ util::write_file("ccache.conf", "no equal sign");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ Equals("ccache.conf:1: missing equal sign"));
+ }
+
+ SECTION("unknown key")
+ {
+ util::write_file("ccache.conf", "# Comment\nfoo = bar");
+ CHECK(config.update_from_file("ccache.conf"));
+ }
+
+ SECTION("invalid bool")
+ {
+ util::write_file("ccache.conf", "disable=");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ Equals("ccache.conf:1: not a boolean value: \"\""));
+
+ util::write_file("ccache.conf", "disable=foo");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ Equals("ccache.conf:1: not a boolean value: \"foo\""));
+ }
+
+ SECTION("invalid variable reference")
+ {
+ util::write_file("ccache.conf", "base_dir = ${foo");
+ REQUIRE_THROWS_WITH(
+ config.update_from_file("ccache.conf"),
+ Equals("ccache.conf:1: syntax error: missing '}' after \"foo\""));
+ // Other cases tested in test_util.c.
+ }
+
+ SECTION("empty umask")
+ {
+ util::write_file("ccache.conf", "umask = ");
+ CHECK(config.update_from_file("ccache.conf"));
+ CHECK(config.umask() == std::numeric_limits<uint32_t>::max());
+ }
+
+ SECTION("invalid size")
+ {
+ util::write_file("ccache.conf", "max_size = foo");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ Equals("ccache.conf:1: invalid size: \"foo\""));
+ // Other cases tested in test_util.c.
+ }
+
+ SECTION("invalid sloppiness")
+ {
+ util::write_file("ccache.conf", "sloppiness = file_macro, foo");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ Equals("ccache.conf:1: unknown sloppiness: \"foo\""));
+ }
+
+ SECTION("invalid unsigned")
+ {
+ util::write_file("ccache.conf", "max_files =");
+ REQUIRE_THROWS_WITH(
+ config.update_from_file("ccache.conf"),
+ Equals("ccache.conf:1: invalid unsigned integer: \"\""));
+
+ util::write_file("ccache.conf", "max_files = -42");
+ REQUIRE_THROWS_WITH(
+ config.update_from_file("ccache.conf"),
+ Equals("ccache.conf:1: invalid unsigned integer: \"-42\""));
+
+ util::write_file("ccache.conf", "max_files = foo");
+ REQUIRE_THROWS_WITH(
+ config.update_from_file("ccache.conf"),
+ Equals("ccache.conf:1: invalid unsigned integer: \"foo\""));
+ }
+
+ SECTION("missing file")
+ {
+ CHECK(!config.update_from_file("ccache.conf"));
+ }
+
+ SECTION("relative base dir")
+ {
+ util::write_file("ccache.conf", "base_dir = relative/path");
+ REQUIRE_THROWS_WITH(
+ config.update_from_file("ccache.conf"),
+ Equals("ccache.conf:1: not an absolute path: \"relative/path\""));
+
+ util::write_file("ccache.conf", "base_dir =");
+ CHECK(config.update_from_file("ccache.conf"));
+ }
+
+ SECTION("bad dir levels")
+ {
+ util::write_file("ccache.conf", "cache_dir_levels = 0");
+ try {
+ config.update_from_file("ccache.conf");
+ CHECK(false);
+ } catch (const Error& e) {
+ CHECK(std::string(e.what())
+ == "ccache.conf:1: cache directory levels must be between 1 and 8");
+ }
+
+ util::write_file("ccache.conf", "cache_dir_levels = 9");
+ try {
+ config.update_from_file("ccache.conf");
+ CHECK(false);
+ } catch (const Error& e) {
+ CHECK(std::string(e.what())
+ == "ccache.conf:1: cache directory levels must be between 1 and 8");
+ }
+ }
+}
+
+TEST_CASE("Config::update_from_environment")
+{
+ Config config;
+
+ x_setenv("CCACHE_COMPRESS", "1");
+ config.update_from_environment();
+ CHECK(config.compression());
+
+ x_unsetenv("CCACHE_COMPRESS");
+
+ x_setenv("CCACHE_NOCOMPRESS", "1");
+ config.update_from_environment();
+ CHECK(!config.compression());
+}
+
+TEST_CASE("Config::set_value_in_file")
+{
+ SECTION("set new value")
+ {
+ util::write_file("ccache.conf", "path = vanilla\n");
+ Config::set_value_in_file("ccache.conf", "compiler", "chocolate");
+ std::string content = util::read_file("ccache.conf");
+ CHECK(content == "path = vanilla\ncompiler = chocolate\n");
+ }
+
+ SECTION("existing value")
+ {
+ util::write_file("ccache.conf", "path = chocolate\nstats = chocolate\n");
+ Config::set_value_in_file("ccache.conf", "path", "vanilla");
+ std::string content = util::read_file("ccache.conf");
+ CHECK(content == "path = vanilla\nstats = chocolate\n");
+ }
+
+ SECTION("unknown option")
+ {
+ util::write_file("ccache.conf", "path = chocolate\nstats = chocolate\n");
+ try {
+ Config::set_value_in_file("ccache.conf", "foo", "bar");
+ CHECK(false);
+ } catch (const Error& e) {
+ CHECK(std::string(e.what()) == "unknown configuration option \"foo\"");
+ }
+
+ std::string content = util::read_file("ccache.conf");
+ CHECK(content == "path = chocolate\nstats = chocolate\n");
+ }
+}
+
+TEST_CASE("Config::get_string_value")
+{
+ Config config;
+
+ SECTION("base case")
+ {
+ config.set_max_files(42);
+ CHECK(config.get_string_value("max_files") == "42");
+ }
+
+ SECTION("unknown key")
+ {
+ try {
+ config.get_string_value("foo");
+ CHECK(false);
+ } catch (const Error& e) {
+ CHECK(std::string(e.what()) == "unknown configuration option \"foo\"");
+ }
+ }
+}
+
+TEST_CASE("Config::visit_items")
+{
+ util::write_file(
+ "test.conf",
+ "base_dir = /bd\n"
+ "cache_dir = cd\n"
+ "cache_dir_levels = 7\n"
+ "compiler = c\n"
+ "compiler_check = cc\n"
+ "compression = true\n"
+ "compression_level = 8\n"
+ "cpp_extension = ce\n"
+ "debug = false\n"
+ "depend_mode = true\n"
+ "direct_mode = false\n"
+ "disable = true\n"
+ "extra_files_to_hash = efth\n"
+ "file_clone = true\n"
+ "hard_link = true\n"
+ "hash_dir = false\n"
+ "ignore_headers_in_manifest = ihim\n"
+ "keep_comments_cpp = true\n"
+ "limit_multiple = 0.0\n"
+ "log_file = lf\n"
+ "max_files = 4711\n"
+ "max_size = 98.7M\n"
+ "path = p\n"
+ "pch_external_checksum = true\n"
+ "prefix_command = pc\n"
+ "prefix_command_cpp = pcc\n"
+ "read_only = true\n"
+ "read_only_direct = true\n"
+ "recache = true\n"
+ "run_second_cpp = false\n"
+ "sloppiness = file_macro, include_file_mtime, include_file_ctime,"
+ " time_macros, file_stat_matches, file_stat_matches_ctime, pch_defines,"
+ " system_headers, clang_index_store\n"
+ "stats = false\n"
+ "temporary_dir = td\n"
+ "umask = 022\n"
+ "unify = true\n");
+
+ Config config;
+ config.update_from_file("test.conf");
+
+ std::vector<std::string> received_items;
+
+ config.visit_items([&](const std::string& key,
+ const std::string& value,
+ const std::string& origin) {
+ received_items.push_back(fmt::format("({}) {} = {}", origin, key, value));
+ });
+
+ std::vector<std::string> expected = {
+ "(test.conf) base_dir = /bd",
+ "(test.conf) cache_dir = cd",
+ "(test.conf) cache_dir_levels = 7",
+ "(test.conf) compiler = c",
+ "(test.conf) compiler_check = cc",
+ "(test.conf) compression = true",
+ "(test.conf) compression_level = 8",
+ "(test.conf) cpp_extension = ce",
+ "(test.conf) debug = false",
+ "(test.conf) depend_mode = true",
+ "(test.conf) direct_mode = false",
+ "(test.conf) disable = true",
+ "(test.conf) extra_files_to_hash = efth",
+ "(test.conf) file_clone = true",
+ "(test.conf) hard_link = true",
+ "(test.conf) hash_dir = false",
+ "(test.conf) ignore_headers_in_manifest = ihim",
+ "(test.conf) keep_comments_cpp = true",
+ "(test.conf) limit_multiple = 0.0",
+ "(test.conf) log_file = lf",
+ "(test.conf) max_files = 4711",
+ "(test.conf) max_size = 98.7M",
+ "(test.conf) path = p",
+ "(test.conf) pch_external_checksum = true",
+ "(test.conf) prefix_command = pc",
+ "(test.conf) prefix_command_cpp = pcc",
+ "(test.conf) read_only = true",
+ "(test.conf) read_only_direct = true",
+ "(test.conf) recache = true",
+ "(test.conf) run_second_cpp = false",
+ "(test.conf) sloppiness = file_macro, include_file_mtime,"
+ " include_file_ctime, time_macros, pch_defines, file_stat_matches,"
+ " file_stat_matches_ctime, system_headers, clang_index_store",
+ "(test.conf) stats = false",
+ "(test.conf) temporary_dir = td",
+ "(test.conf) umask = 022",
+ "(test.conf) unify = true",
+ };
+
+ REQUIRE(received_items.size() == expected.size());
+ for (size_t i = 0; i < expected.size(); ++i) {
+ CHECK(received_items[i] == expected[i]);
+ }
+}
+
+// TODO Test that values in k_env_variable_table map to keys in
+// k_config_item_table.;
// This file contains tests for the processing of compiler arguments.
+#include "../src/Config.hpp"
#include "../src/ccache.hpp"
-#include "../src/conf.hpp"
#include "framework.hpp"
#include "util.hpp"
-extern struct conf* conf;
-
-static char*
-get_root(void)
+static std::string
+get_root()
{
#ifndef _WIN32
- return x_strdup("/");
+ return "/";
#else
char volume[4]; // "C:\"
GetVolumePathName(get_cwd(), volume, sizeof(volume));
- return x_strdup(volume);
+ return volume;
#endif
}
struct args *act_cpp = NULL, *act_cc = NULL;
create_file("foo.c", "");
- conf->run_second_cpp = false;
+ g_config.set_run_second_cpp(false);
CHECK(cc_process_args(orig, &act_cpp, &act_cc));
CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
struct args *act_cpp = NULL, *act_cc = NULL;
create_file("foo.c", "");
- conf->run_second_cpp = true;
+ g_config.set_run_second_cpp(true);
CHECK(cc_process_args(orig, &act_cpp, &act_cc));
CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
struct args *act_cpp = NULL, *act_cc = NULL;
create_file("foo.c", "");
- free(conf->base_dir);
- conf->base_dir = get_root();
+ g_config.set_base_dir(get_root());
current_working_dir = get_cwd();
arg_string = format("cc --sysroot=%s/foo/bar -c foo.c", current_working_dir);
orig = args_init_from_string(arg_string);
struct args *act_cpp = NULL, *act_cc = NULL;
create_file("foo.c", "");
- free(conf->base_dir);
- conf->base_dir = get_root();
+ g_config.set_base_dir(get_root());
current_working_dir = get_cwd();
arg_string = format("cc --sysroot %s/foo -c foo.c", current_working_dir);
orig = args_init_from_string(arg_string);
struct args *act_cpp = NULL, *act_cc = NULL;
create_file("foo.c", "");
- free(conf->base_dir);
- conf->base_dir = get_root();
+ g_config.set_base_dir(get_root());
current_working_dir = get_cwd();
arg_string = format("cc -isystem %s/foo -c foo.c", current_working_dir);
orig = args_init_from_string(arg_string);
struct args *act_cpp = NULL, *act_cc = NULL;
create_file("foo.c", "");
- free(conf->base_dir);
- conf->base_dir = x_strdup("/"); // posix
+ g_config.set_base_dir("/"); // posix
current_working_dir = get_cwd();
// Windows path doesn't work concatenated.
cwd = get_posix_path(current_working_dir);
struct args *act_cpp = NULL, *act_cc = NULL;
create_file("foo.c", "");
- free(conf->base_dir);
- conf->base_dir = x_strdup("/"); // posix
+ g_config.set_base_dir(x_strdup("/")); // posix
current_working_dir = get_cwd();
// Windows path doesn't work concatenated.
cwd = get_posix_path(current_working_dir);
+++ /dev/null
-// Copyright (C) 2011-2019 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 "../src/conf.hpp"
-#include "framework.hpp"
-#include "util.hpp"
-
-#define N_CONFIG_ITEMS 35
-static struct
-{
- char* descr;
- char* origin;
-} received_conf_items[N_CONFIG_ITEMS];
-static size_t n_received_conf_items = 0;
-
-static char USER_ENV[] = "USER=rabbit";
-static char COMPRESS_1_ENV[] = "CCACHE_COMPRESS=1";
-static char NOCOMPRESS_1_ENV[] = "CCACHE_NOCOMPRESS=1";
-
-static void
-conf_item_receiver(const char* descr, const char* origin, void* context)
-{
- (void)context;
- received_conf_items[n_received_conf_items].descr = x_strdup(descr);
- received_conf_items[n_received_conf_items].origin = x_strdup(origin);
- ++n_received_conf_items;
-}
-
-static void
-free_received_conf_items(void)
-{
- while (n_received_conf_items > 0) {
- --n_received_conf_items;
- free(received_conf_items[n_received_conf_items].descr);
- free(received_conf_items[n_received_conf_items].origin);
- }
-}
-
-TEST_SUITE(conf)
-
-TEST(conf_create)
-{
- struct conf* conf = conf_create();
- CHECK_STR_EQ("", conf->base_dir);
- CHECK_STR_EQ_FREE1(format("%s/.ccache", get_home_directory()),
- conf->cache_dir);
- CHECK_INT_EQ(2, conf->cache_dir_levels);
- CHECK_STR_EQ("", conf->compiler);
- CHECK_STR_EQ("mtime", conf->compiler_check);
- CHECK(conf->compression);
- CHECK_INT_EQ(0, conf->compression_level);
- CHECK_STR_EQ("", conf->cpp_extension);
- CHECK(!conf->debug);
- CHECK(!conf->depend_mode);
- CHECK(conf->direct_mode);
- CHECK(!conf->disable);
- CHECK_STR_EQ("", conf->extra_files_to_hash);
- CHECK(!conf->file_clone);
- CHECK(!conf->hard_link);
- CHECK(conf->hash_dir);
- CHECK_STR_EQ("", conf->ignore_headers_in_manifest);
- CHECK(!conf->keep_comments_cpp);
- CHECK_DOUBLE_EQ(0.8, conf->limit_multiple);
- CHECK_STR_EQ("", conf->log_file);
- CHECK_INT_EQ(0, conf->max_files);
- CHECK_INT_EQ((uint64_t)5 * 1000 * 1000 * 1000, conf->max_size);
- CHECK_STR_EQ("", conf->path);
- CHECK(!conf->pch_external_checksum);
- CHECK_STR_EQ("", conf->prefix_command);
- CHECK_STR_EQ("", conf->prefix_command_cpp);
- CHECK(!conf->read_only);
- CHECK(!conf->read_only_direct);
- CHECK(!conf->recache);
- CHECK(conf->run_second_cpp);
- CHECK_INT_EQ(0, conf->sloppiness);
- CHECK(conf->stats);
- CHECK_STR_EQ("", conf->temporary_dir);
- CHECK_INT_EQ(UINT_MAX, conf->umask);
- CHECK(!conf->unify);
- conf_free(conf);
-}
-
-TEST(conf_read_valid_config)
-{
- struct conf* conf = conf_create();
- char *errmsg, *user;
- putenv(USER_ENV);
- user = getenv("USER");
- CHECK_STR_EQ("rabbit", user);
- create_file(
- "ccache.conf",
-#ifndef _WIN32
- "base_dir = /$USER/foo/${USER} \n"
-#else
- "base_dir = C:/$USER/foo/${USER}\n"
-#endif
- "cache_dir=\n"
- "cache_dir = $USER$/${USER}/.ccache\n"
- "\n"
- "\n"
- " #A comment\n"
- " cache_dir_levels = 4\n"
- "\t compiler = foo\n"
- "compiler_check = none\n"
- "compression=false\n"
- "compression_level= 2\n"
- "cpp_extension = .foo\n"
- "depend_mode = true\n"
- "direct_mode = false\n"
- "disable = true\n"
- "extra_files_to_hash = a:b c:$USER\n"
- "file_clone = true\n"
- "hard_link = true\n"
- "hash_dir = false\n"
- "ignore_headers_in_manifest = a:b/c\n"
- "keep_comments_cpp = true\n"
- "limit_multiple = 1.0\n"
- "log_file = $USER${USER} \n"
- "max_files = 17\n"
- "max_size = 123M\n"
- "path = $USER.x\n"
- "pch_external_checksum = true\n"
- "prefix_command = x$USER\n"
- "prefix_command_cpp = y\n"
- "read_only = true\n"
- "read_only_direct = true\n"
- "recache = true\n"
- "run_second_cpp = false\n"
- "sloppiness = file_macro ,time_macros, "
- "include_file_mtime,include_file_ctime,file_stat_matches,file_stat_"
- "matches_ctime,pch_defines , "
- "no_system_headers,system_headers,clang_index_store\n"
- "stats = false\n"
- "temporary_dir = ${USER}_foo\n"
- "umask = 777\n"
- "unify = true"); // Note: no newline.
- CHECK(conf_read(conf, "ccache.conf", &errmsg));
- CHECK(!errmsg);
-
-#ifndef _WIN32
- CHECK_STR_EQ_FREE1(format("/%s/foo/%s", user, user), conf->base_dir);
-#else
- CHECK_STR_EQ_FREE1(format("C:/%s/foo/%s", user, user), conf->base_dir);
-#endif
- CHECK_STR_EQ_FREE1(format("%s$/%s/.ccache", user, user), conf->cache_dir);
- CHECK_INT_EQ(4, conf->cache_dir_levels);
- CHECK_STR_EQ("foo", conf->compiler);
- CHECK_STR_EQ("none", conf->compiler_check);
- CHECK(!conf->compression);
- CHECK_INT_EQ(2, conf->compression_level);
- CHECK_STR_EQ(".foo", conf->cpp_extension);
- CHECK(conf->depend_mode);
- CHECK(!conf->direct_mode);
- CHECK(conf->disable);
- CHECK_STR_EQ_FREE1(format("a:b c:%s", user), conf->extra_files_to_hash);
- CHECK(conf->file_clone);
- CHECK(conf->hard_link);
- CHECK(!conf->hash_dir);
- CHECK_STR_EQ("a:b/c", conf->ignore_headers_in_manifest);
- CHECK(conf->keep_comments_cpp);
- CHECK_DOUBLE_EQ(1.0, conf->limit_multiple);
- CHECK_STR_EQ_FREE1(format("%s%s", user, user), conf->log_file);
- CHECK_INT_EQ(17, conf->max_files);
- CHECK_INT_EQ(123 * 1000 * 1000, conf->max_size);
- CHECK_STR_EQ_FREE1(format("%s.x", user), conf->path);
- CHECK(conf->pch_external_checksum);
- CHECK_STR_EQ_FREE1(format("x%s", user), conf->prefix_command);
- CHECK_STR_EQ("y", conf->prefix_command_cpp);
- CHECK(conf->read_only);
- CHECK(conf->read_only_direct);
- CHECK(conf->recache);
- CHECK(!conf->run_second_cpp);
- CHECK_INT_EQ(SLOPPY_INCLUDE_FILE_MTIME | SLOPPY_INCLUDE_FILE_CTIME
- | SLOPPY_FILE_MACRO | SLOPPY_TIME_MACROS
- | SLOPPY_FILE_STAT_MATCHES | SLOPPY_FILE_STAT_MATCHES_CTIME
- | SLOPPY_SYSTEM_HEADERS | SLOPPY_PCH_DEFINES
- | SLOPPY_CLANG_INDEX_STORE,
- conf->sloppiness);
- CHECK(!conf->stats);
- CHECK_STR_EQ_FREE1(format("%s_foo", user), conf->temporary_dir);
- CHECK_INT_EQ(0777, conf->umask);
- CHECK(conf->unify);
-
- conf_free(conf);
-}
-
-TEST(conf_read_with_missing_equal_sign)
-{
- struct conf* conf = conf_create();
- char* errmsg;
- create_file("ccache.conf", "no equal sign");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: missing equal sign", errmsg);
- conf_free(conf);
-}
-
-TEST(conf_read_with_unknown_config_key)
-{
- struct conf* conf = conf_create();
- char* errmsg;
- create_file("ccache.conf", "# Comment\nfoo = bar");
- CHECK(conf_read(conf, "ccache.conf", &errmsg));
- conf_free(conf);
-}
-
-TEST(conf_read_invalid_bool)
-{
- struct conf* conf = conf_create();
- char* errmsg;
-
- create_file("ccache.conf", "disable=");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: not a boolean value: \"\"", errmsg);
-
- create_file("ccache.conf", "disable=foo");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: not a boolean value: \"foo\"", errmsg);
- conf_free(conf);
-}
-
-TEST(conf_read_invalid_env_string)
-{
- struct conf* conf = conf_create();
- char* errmsg;
- create_file("ccache.conf", "base_dir = ${foo");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: syntax error: missing '}' after \"foo\"",
- errmsg);
- // Other cases tested in test_util.c.
- conf_free(conf);
-}
-
-TEST(conf_read_empty_umask)
-{
- struct conf* conf = conf_create();
- char* errmsg;
- create_file("ccache.conf", "umask = ");
- CHECK(conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(conf->umask, UINT_MAX);
- conf_free(conf);
-}
-
-TEST(conf_read_invalid_size)
-{
- struct conf* conf = conf_create();
- char* errmsg;
- create_file("ccache.conf", "max_size = foo");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: invalid size: \"foo\"", errmsg);
- // Other cases tested in test_util.c.
- conf_free(conf);
-}
-
-TEST(conf_read_invalid_sloppiness)
-{
- struct conf* conf = conf_create();
- char* errmsg;
- create_file("ccache.conf", "sloppiness = file_macro, foo");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: unknown sloppiness: \"foo\"", errmsg);
- conf_free(conf);
-}
-
-TEST(conf_read_invalid_unsigned)
-{
- struct conf* conf = conf_create();
- char* errmsg;
-
- create_file("ccache.conf", "max_files =");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: invalid unsigned integer: \"\"", errmsg);
-
- create_file("ccache.conf", "max_files = -42");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_STR_EQ_FREE2("ccache.conf:1: invalid unsigned integer: \"-42\"",
- errmsg);
-
- create_file("ccache.conf", "max_files = foo");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_STR_EQ_FREE2("ccache.conf:1: invalid unsigned integer: \"foo\"",
- errmsg);
-
- conf_free(conf);
-}
-
-TEST(conf_read_missing_config_file)
-{
- struct conf* conf = conf_create();
- char* errmsg;
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, ENOENT);
- conf_free(conf);
-}
-
-TEST(verify_absolute_base_dir)
-{
- struct conf* conf = conf_create();
- char* errmsg;
-
- create_file("ccache.conf", "base_dir = relative/path");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_STR_EQ_FREE2("ccache.conf:1: not an absolute path: \"relative/path\"",
- errmsg);
-
- create_file("ccache.conf", "base_dir =");
- CHECK(conf_read(conf, "ccache.conf", &errmsg));
-
- conf_free(conf);
-}
-
-TEST(verify_dir_levels)
-{
- struct conf* conf = conf_create();
- char* errmsg;
-
- create_file("ccache.conf", "cache_dir_levels = 0");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_STR_EQ_FREE2(
- "ccache.conf:1: cache directory levels must be between 1 and 8", errmsg);
- create_file("ccache.conf", "cache_dir_levels = 9");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_STR_EQ_FREE2(
- "ccache.conf:1: cache directory levels must be between 1 and 8", errmsg);
-
- conf_free(conf);
-}
-
-TEST(conf_update_from_environment)
-{
- struct conf* conf = conf_create();
- char* errmsg;
-
- putenv(COMPRESS_1_ENV);
- CHECK(conf_update_from_environment(conf, &errmsg));
- CHECK(conf->compression);
-
- x_unsetenv("CCACHE_COMPRESS");
- putenv(NOCOMPRESS_1_ENV);
- CHECK(conf_update_from_environment(conf, &errmsg));
- CHECK(!conf->compression);
-
- conf_free(conf);
-}
-
-TEST(conf_set_new_value)
-{
- char* errmsg;
- char* data;
-
- create_file("ccache.conf", "path = vanilla\n");
- CHECKM(
- conf_set_value_in_file("ccache.conf", "compiler", "chocolate", &errmsg),
- errmsg);
- data = read_text_file("ccache.conf", 0);
- CHECK(data);
- CHECK_STR_EQ_FREE2("path = vanilla\ncompiler = chocolate\n", data);
-}
-
-TEST(conf_set_existing_value)
-{
- char* errmsg;
- char* data;
-
- create_file("ccache.conf", "path = chocolate\nstats = chocolate\n");
- CHECKM(conf_set_value_in_file("ccache.conf", "path", "vanilla", &errmsg),
- errmsg);
- data = read_text_file("ccache.conf", 0);
- CHECK(data);
- CHECK_STR_EQ_FREE2("path = vanilla\nstats = chocolate\n", data);
-}
-
-TEST(conf_set_unknown_option)
-{
- char* errmsg;
- char* data;
-
- create_file("ccache.conf", "path = chocolate\nstats = chocolate\n");
- CHECKM(!conf_set_value_in_file("ccache.conf", "foo", "bar", &errmsg), errmsg);
- CHECK_STR_EQ_FREE2("unknown configuration option \"foo\"", errmsg);
-
- data = read_text_file("ccache.conf", 0);
- CHECK(data);
- CHECK_STR_EQ_FREE2("path = chocolate\nstats = chocolate\n", data);
-}
-
-TEST(conf_print_existing_value)
-{
- struct conf* conf = conf_create();
- conf->max_files = 42;
- char* errmsg;
- {
- FILE* log = fopen("log", "w");
- CHECK(log);
- CHECK(conf_print_value(conf, "max_files", log, &errmsg));
- fclose(log);
- }
- {
- FILE* log = fopen("log", "r");
- CHECK(log);
- char buf[100];
- CHECK(fgets(buf, sizeof(buf), log));
- CHECK_STR_EQ("42\n", buf);
- fclose(log);
- }
- conf_free(conf);
-}
-
-TEST(conf_print_unknown_value)
-{
- struct conf* conf = conf_create();
- char* errmsg;
- {
- FILE* log = fopen("log", "w");
- CHECK(log);
- CHECK(!conf_print_value(conf, "foo", log, &errmsg));
- CHECK_STR_EQ_FREE2("unknown configuration option \"foo\"", errmsg);
- fclose(log);
- }
- {
- FILE* log = fopen("log", "r");
- CHECK(log);
- char buf[100];
- CHECK(!fgets(buf, sizeof(buf), log));
- fclose(log);
- }
- conf_free(conf);
-}
-
-TEST(conf_print_items)
-{
- struct conf* conf = conf_create();
- conf->base_dir = x_strdup("bd");
- conf->cache_dir = x_strdup("cd");
- conf->cache_dir_levels = 7;
- conf->compiler = x_strdup("c");
- conf->compiler_check = x_strdup("cc");
- conf->compression = true;
- conf->compression_level = 8;
- conf->cpp_extension = x_strdup("ce");
- conf->debug = false;
- conf->depend_mode = true;
- conf->direct_mode = false;
- conf->disable = true;
- conf->extra_files_to_hash = x_strdup("efth");
- conf->file_clone = true;
- conf->hard_link = true;
- conf->hash_dir = false;
- conf->ignore_headers_in_manifest = x_strdup("ihim");
- conf->keep_comments_cpp = true;
- conf->limit_multiple = 0.0;
- conf->log_file = x_strdup("lf");
- conf->max_files = 4711;
- conf->max_size = 98.7 * 1000 * 1000;
- conf->path = x_strdup("p");
- conf->pch_external_checksum = true;
- conf->prefix_command = x_strdup("pc");
- conf->prefix_command_cpp = x_strdup("pcc");
- conf->read_only = true;
- conf->read_only_direct = true;
- conf->recache = true;
- conf->run_second_cpp = false;
- conf->sloppiness = SLOPPY_FILE_MACRO | SLOPPY_INCLUDE_FILE_MTIME
- | SLOPPY_INCLUDE_FILE_CTIME | SLOPPY_TIME_MACROS
- | SLOPPY_FILE_STAT_MATCHES | SLOPPY_FILE_STAT_MATCHES_CTIME
- | SLOPPY_PCH_DEFINES | SLOPPY_SYSTEM_HEADERS
- | SLOPPY_CLANG_INDEX_STORE;
- conf->stats = false;
- conf->temporary_dir = x_strdup("td");
- conf->umask = 022;
- conf->unify = true;
-
- conf->item_origins =
- static_cast<const char**>(x_malloc(N_CONFIG_ITEMS * sizeof(char*)));
- for (size_t i = 0; i < N_CONFIG_ITEMS; ++i) {
-#ifndef __MINGW32__
- conf->item_origins[i] = format("origin%zu", i);
-#else
- conf->item_origins[i] = format("origin%u", (unsigned)i);
-#endif
- }
-
- size_t n = 0;
- conf_print_items(conf, conf_item_receiver, NULL);
- CHECK_INT_EQ(N_CONFIG_ITEMS, n_received_conf_items);
- CHECK_STR_EQ("base_dir = bd", received_conf_items[n++].descr);
- CHECK_STR_EQ("cache_dir = cd", received_conf_items[n++].descr);
- CHECK_STR_EQ("cache_dir_levels = 7", received_conf_items[n++].descr);
- CHECK_STR_EQ("compiler = c", received_conf_items[n++].descr);
- CHECK_STR_EQ("compiler_check = cc", received_conf_items[n++].descr);
- CHECK_STR_EQ("compression = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("compression_level = 8", received_conf_items[n++].descr);
- CHECK_STR_EQ("cpp_extension = ce", received_conf_items[n++].descr);
- CHECK_STR_EQ("debug = false", received_conf_items[n++].descr);
- CHECK_STR_EQ("depend_mode = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("direct_mode = false", received_conf_items[n++].descr);
- CHECK_STR_EQ("disable = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("extra_files_to_hash = efth", received_conf_items[n++].descr);
- CHECK_STR_EQ("file_clone = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("hard_link = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("hash_dir = false", received_conf_items[n++].descr);
- CHECK_STR_EQ("ignore_headers_in_manifest = ihim",
- received_conf_items[n++].descr);
- CHECK_STR_EQ("keep_comments_cpp = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("limit_multiple = 0.0", received_conf_items[n++].descr);
- CHECK_STR_EQ("log_file = lf", received_conf_items[n++].descr);
- CHECK_STR_EQ("max_files = 4711", received_conf_items[n++].descr);
- CHECK_STR_EQ("max_size = 98.7M", received_conf_items[n++].descr);
- CHECK_STR_EQ("path = p", received_conf_items[n++].descr);
- CHECK_STR_EQ("pch_external_checksum = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("prefix_command = pc", received_conf_items[n++].descr);
- CHECK_STR_EQ("prefix_command_cpp = pcc", received_conf_items[n++].descr);
- CHECK_STR_EQ("read_only = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("read_only_direct = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("recache = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("run_second_cpp = false", received_conf_items[n++].descr);
- CHECK_STR_EQ(
- "sloppiness = file_macro, include_file_mtime,"
- " include_file_ctime, time_macros, pch_defines,"
- " file_stat_matches, file_stat_matches_ctime, system_headers,"
- " clang_index_store",
- received_conf_items[n++].descr);
- CHECK_STR_EQ("stats = false", received_conf_items[n++].descr);
- CHECK_STR_EQ("temporary_dir = td", received_conf_items[n++].descr);
- CHECK_STR_EQ("umask = 022", received_conf_items[n++].descr);
- CHECK_STR_EQ("unify = true", received_conf_items[n++].descr);
-
- for (size_t i = 0; i < N_CONFIG_ITEMS; ++i) {
-#ifndef __MINGW32__
- char* expected = format("origin%zu", i);
-#else
- char* expected = format("origin%u", (unsigned)i);
-#endif
- CHECK_STR_EQ_FREE1(expected, received_conf_items[i].origin);
- }
-
- free_received_conf_items();
- conf_free(conf);
-}
-
-TEST_SUITE_END
#include "../src/ccache.hpp"
#include "framework.hpp"
-static char FOO_ENV[] = "FOO=bar";
-
TEST_SUITE(legacy_util)
TEST(x_basename)
{
char* errmsg;
- putenv(FOO_ENV);
+ x_setenv("FOO", "bar");
CHECK_STR_EQ_FREE2("bar", subst_env_in_string("$FOO", &errmsg));
CHECK(!errmsg);
#include "../src/util.hpp"
-#include "third_party/catch.hpp"
+#include <catch.hpp>
TEST_CASE("util::read_file and util::write_file")
{