From: Joel Rosdahl Date: Wed, 14 Aug 2019 19:58:19 +0000 (+0200) Subject: C++-ify configuration handling X-Git-Tag: v4.0~837 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=728acfac984fa4f0501dafe203d9e77608d2a7ac;p=thirdparty%2Fccache.git C++-ify configuration handling - Instead of using code generated by gperf, use ordinary unordered maps for the mapping from config keys (and environment variables) to config items. - Renamed “struct conf* conf” to “class Config g_config”. - C++-ified conf.cpp and confitems.cpp into Config.cpp. --- diff --git a/.gitignore b/.gitignore index 8d7ffe1dc..0c5518e42 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,6 @@ dev.mk dev_mode_disabled doc/ccache.1 perfdir.* -src/*_lookup.cpp src/version.cpp testdir.* unittest/run diff --git a/.travis.yml b/.travis.yml index cbbf0c194..756c737b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,6 @@ matrix: apt: packages: - elfutils - - gperf - libzstd1-dev - libb2-dev @@ -30,7 +29,6 @@ matrix: apt: packages: - elfutils - - gperf - libzstd1-dev - libb2-dev @@ -47,7 +45,6 @@ matrix: apt: packages: - gcc-multilib - - gperf - lib32stdc++-5-dev # Job 5: Linux cross-compiled 32-bit MinGW @@ -58,7 +55,6 @@ matrix: apt: packages: - elfutils - - gperf # Job 6: Linux cross-compiled 64-bit MinGW - os: linux @@ -68,7 +64,6 @@ matrix: apt: packages: - elfutils - - gperf # Job 7: Clang's undefined behavior sanitizer (UBSan) - os: linux @@ -78,7 +73,6 @@ matrix: apt: packages: - elfutils - - gperf - libzstd1-dev - libb2-dev @@ -90,7 +84,6 @@ matrix: apt: packages: - elfutils - - gperf - libzstd1-dev - libb2-dev @@ -101,7 +94,6 @@ matrix: addons: apt: packages: - - gperf - libzstd1-dev - libb2-dev @@ -114,7 +106,6 @@ matrix: apt: packages: - elfutils - - gperf - libzstd1-dev - libb2-dev before_install: diff --git a/.travis/Dockerfile b/.travis/Dockerfile index 6b17647ad..118aed5f9 100644 --- a/.travis/Dockerfile +++ b/.travis/Dockerfile @@ -30,7 +30,6 @@ RUN apt-get -qq update && apt-get install -y --no-install-recommends \ # ccache specific RUN apt-get -qq update && apt-get install -y --no-install-recommends \ - gperf \ elfutils \ libzstd1-dev \ libb2-dev \ diff --git a/Makefile.in b/Makefile.in index 8b1a2338c..9c0a86c96 100644 --- a/Makefile.in +++ b/Makefile.in @@ -32,6 +32,7 @@ Q=$(if $(quiet),@) non_third_party_sources = \ src/AtomicFile.cpp \ + src/Config.cpp \ src/args.cpp \ src/ccache.cpp \ src/cleanup.cpp \ @@ -41,8 +42,6 @@ non_third_party_sources = \ 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 \ @@ -58,8 +57,6 @@ non_third_party_sources = \ 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 \ @@ -76,12 +73,12 @@ non_third_party_objs = $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(non_third_p 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 diff --git a/buildenv/alpine/Dockerfile b/buildenv/alpine/Dockerfile index 8c9f88b58..2aa21fb02 100644 --- a/buildenv/alpine/Dockerfile +++ b/buildenv/alpine/Dockerfile @@ -6,6 +6,5 @@ RUN apk add --no-cache \ bash \ asciidoc \ autoconf \ - gperf \ zstd-dev \ ## diff --git a/buildenv/centos/Dockerfile b/buildenv/centos/Dockerfile index d6863e156..1bb01ff74 100644 --- a/buildenv/centos/Dockerfile +++ b/buildenv/centos/Dockerfile @@ -8,7 +8,6 @@ RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.n bash \ asciidoc \ autoconf \ - gperf \ libzstd-devel \ && rpm -e --nodeps graphviz \ && yum autoremove -y \ diff --git a/buildenv/debian/Dockerfile b/buildenv/debian/Dockerfile index f897414fc..28725f8c6 100644 --- a/buildenv/debian/Dockerfile +++ b/buildenv/debian/Dockerfile @@ -6,6 +6,5 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ bash \ asciidoc xsltproc docbook-xml docbook-xsl \ autoconf \ - gperf \ libzstd-dev \ && rm -rf /var/lib/apt/lists/* diff --git a/buildenv/fedora/Dockerfile b/buildenv/fedora/Dockerfile index c30aa32b3..e969b13c8 100644 --- a/buildenv/fedora/Dockerfile +++ b/buildenv/fedora/Dockerfile @@ -7,7 +7,6 @@ RUN dnf install -y \ bash \ asciidoc \ autoconf \ - gperf \ libzstd-devel \ && rpm -e --nodeps graphviz \ && dnf autoremove -y \ diff --git a/buildenv/ubuntu/Dockerfile b/buildenv/ubuntu/Dockerfile index 2b92f2b0d..27c78636f 100644 --- a/buildenv/ubuntu/Dockerfile +++ b/buildenv/ubuntu/Dockerfile @@ -6,6 +6,5 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ bash \ asciidoc xsltproc docbook-xml docbook-xsl \ autoconf \ - gperf \ libzstd-dev \ && rm -rf /var/lib/apt/lists/* diff --git a/configure.ac b/configure.ac index b900a494c..46805ef8e 100644 --- a/configure.ac +++ b/configure.ac @@ -61,6 +61,7 @@ if test "$ac_compiler_clang" = yes; then 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" @@ -255,10 +256,6 @@ if test ! -f $srcdir/dev_mode_disabled; then 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 diff --git a/dev.mk.in b/dev.mk.in index d4faad473..b0faaaa3c 100644 --- a/dev.mk.in +++ b/dev.mk.in @@ -12,7 +12,6 @@ COMPILEDB = compiledb CPPCHECK = cppcheck CPPCHECK_SUPPRESSIONS = misc/cppcheck-suppressions.txt DOCKER = docker -GPERF = @GPERF@ SCAN_BUILD = scan-build SHELLCHECK = shellcheck SHELLCHECK_EXCLUDES = misc/shellcheck-excludes.txt @@ -37,15 +36,13 @@ built_dist_files = $(generated_sources) $(generated_docs) 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 \ @@ -95,10 +92,6 @@ source_dist_files = \ 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 \ @@ -113,24 +106,6 @@ ifneq ($(shell sed 's/.*"\(.*\)".*/\1/' src/version.cpp 2>/dev/null),$(version)) 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) @@ -212,7 +187,7 @@ cppcheck: $(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 diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 76c874dc6..70afb9f9d 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -14,7 +14,6 @@ To build ccache from a source repository, you need: - [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 diff --git a/src/Config.cpp b/src/Config.cpp new file mode 100644 index 000000000..d2beaffd0 --- /dev/null +++ b/src/Config.cpp @@ -0,0 +1,830 @@ +// 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 +#include +#include +#include +#include +#include +#include + +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 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 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 + 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::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::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::min() + || result > std::numeric_limits::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 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); +} diff --git a/src/Config.hpp b/src/Config.hpp new file mode 100644 index 000000000..a05ba4f0b --- /dev/null +++ b/src/Config.hpp @@ -0,0 +1,423 @@ +// 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 +#include +#include +#include +#include + +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 + 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::max(); // Don't set umask + bool m_unify = false; + + std::unordered_map 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; +} diff --git a/src/ccache.cpp b/src/ccache.cpp index 742076011..206349229 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -19,7 +19,13 @@ #include "ccache.hpp" +#include "Error.hpp" #include "compopt.hpp" +#include "util.hpp" + +#include +#include + #ifdef HAVE_GETOPT_LONG # include #else @@ -35,11 +41,7 @@ #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; @@ -111,9 +113,6 @@ static const char USAGE_TEXT[] = "\n" "See also .\n"; -// Global configuration data. -struct conf* conf = NULL; - // Where to write configuration changes. char* primary_config_path = NULL; @@ -288,7 +287,7 @@ static pid_t compiler_pid = 0; 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; @@ -327,7 +326,7 @@ failed(void) 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); @@ -339,13 +338,13 @@ failed(void) 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; } @@ -469,12 +468,13 @@ clean_up_internal_tempdir(void) { 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) { @@ -506,7 +506,7 @@ fclose_exitfn(void* context) static void dump_debug_log_buffer_exitfn(void* context) { - if (!conf->debug) { + if (!g_config.debug()) { return; } @@ -522,7 +522,7 @@ init_hash_debug(struct hash* hash, const char* section_name, FILE* debug_text_file) { - if (!conf->debug) { + if (!g_config.debug()) { return; } @@ -596,7 +596,7 @@ remember_include_file(char* path, 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; } @@ -658,14 +658,14 @@ remember_include_file(char* path, // 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; @@ -680,7 +680,7 @@ remember_include_file(char* path, 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); @@ -703,7 +703,7 @@ remember_include_file(char* 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; @@ -716,7 +716,7 @@ remember_include_file(char* path, 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) { @@ -740,9 +740,9 @@ remember_include_file(char* path, 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: @@ -765,7 +765,8 @@ print_included_files(FILE* fp) 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; } @@ -850,9 +851,9 @@ process_preprocessed_file(struct hash* hash, const char* path, bool pump) 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( @@ -966,7 +967,7 @@ process_preprocessed_file(struct hash* hash, const char* path, bool pump) 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: @@ -1038,7 +1039,7 @@ process_preprocessed_file(struct hash* hash, const char* path, bool pump) 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 } @@ -1066,7 +1067,8 @@ use_relative_paths_in_depfile(const char* depfile) 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 { @@ -1187,8 +1189,8 @@ send_cached_stderr(const char* path_stderr) 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; } @@ -1218,7 +1220,8 @@ update_cached_result_globals(struct digest* result_name) 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. @@ -1228,7 +1231,7 @@ to_cache(struct args* args, struct hash* depend_mode_hash) 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 // . @@ -1247,7 +1250,7 @@ to_cache(struct args* args, struct hash* depend_mode_hash) 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); @@ -1271,7 +1274,7 @@ to_cache(struct args* args, struct hash* depend_mode_hash) 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()); @@ -1290,7 +1293,7 @@ to_cache(struct args* args, struct hash* depend_mode_hash) 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( @@ -1377,7 +1380,7 @@ to_cache(struct args* args, struct hash* depend_mode_hash) 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) { @@ -1467,7 +1470,7 @@ to_cache(struct args* args, struct hash* depend_mode_hash) // 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); } @@ -1523,12 +1526,12 @@ get_result_name_from_cpp(struct args* args, struct hash* hash) 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); @@ -1542,7 +1545,7 @@ get_result_name_from_cpp(struct args* args, struct hash* hash) 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"); @@ -1575,12 +1578,12 @@ get_result_name_from_cpp(struct args* args, struct hash* hash) } 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 @@ -1603,23 +1606,24 @@ hash_compiler(struct hash* hash, 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()); } } } @@ -1686,7 +1690,7 @@ hash_common_info(struct args* args, struct hash* hash) // 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], '.'); @@ -1714,7 +1718,7 @@ hash_common_info(struct args* args, struct hash* hash) 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}; @@ -1728,7 +1732,7 @@ hash_common_info(struct args* args, struct hash* hash) } // 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]; @@ -1798,8 +1802,8 @@ hash_common_info(struct args* args, struct hash* hash) } } - 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; @@ -2049,7 +2053,7 @@ calculate_result_name(struct args* args, struct hash* hash, int direct_mode) } } - 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"); @@ -2057,25 +2061,25 @@ calculate_result_name(struct args* args, struct hash* hash, int direct_mode) } 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"); @@ -2117,7 +2121,7 @@ static void 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; } @@ -2225,8 +2229,8 @@ find_compiler(char** argv) } // 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); @@ -2465,9 +2469,9 @@ cc_process_args(struct args* args, } // 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. @@ -2492,7 +2496,7 @@ cc_process_args(struct args* args, 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; } @@ -2798,11 +2802,11 @@ cc_process_args(struct args* args, // 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. @@ -2919,7 +2923,7 @@ cc_process_args(struct args* args, 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 @@ -3067,14 +3071,14 @@ cc_process_args(struct args* args, } } // 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 . @@ -3137,7 +3141,7 @@ cc_process_args(struct args* args, 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"); @@ -3168,7 +3172,7 @@ cc_process_args(struct args* args, 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"); @@ -3201,23 +3205,22 @@ cc_process_args(struct args* args, 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. @@ -3351,7 +3354,7 @@ cc_process_args(struct args* args, *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. @@ -3414,7 +3417,7 @@ create_initial_config_file(const char* path) 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); @@ -3423,7 +3426,7 @@ create_initial_config_file(const char* path) max_size *= 16; } else { max_files = 0; - max_size = conf->max_size; + max_size = g_config.max_size(); } free(stats_dir); @@ -3433,13 +3436,13 @@ create_initial_config_file(const char* path) } 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); } @@ -3510,59 +3513,39 @@ initialize(void) #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) { @@ -3576,8 +3559,8 @@ initialize(void) cc_log("=== CCACHE %s STARTED =========================================", CCACHE_VERSION); - if (conf->umask != UINT_MAX) { - umask(conf->umask); + if (g_config.umask() != std::numeric_limits::max()) { + umask(g_config.umask()); } if (enable_internal_trace) { @@ -3594,8 +3577,9 @@ initialize(void) 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); @@ -3671,7 +3655,6 @@ cc_reset(void) stats_file = NULL; output_is_precompiled_header = false; - conf = conf_create(); seen_split_dwarf = false; } @@ -3695,10 +3678,20 @@ set_up_uncached_err(void) } 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; @@ -3722,16 +3715,16 @@ ccache(int argc, char* argv[]) 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(); } @@ -3744,7 +3737,7 @@ ccache(int argc, char* argv[]) 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]); @@ -3760,11 +3753,11 @@ ccache(int argc, char* argv[]) } 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); @@ -3791,7 +3784,7 @@ ccache(int argc, char* argv[]) 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) { @@ -3816,7 +3809,7 @@ ccache(int argc, char* argv[]) 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); @@ -3838,12 +3831,12 @@ ccache(int argc, char* argv[]) } } - 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); @@ -3884,15 +3877,15 @@ ccache(int argc, char* argv[]) 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"); @@ -3902,14 +3895,6 @@ ccache(int argc, char* argv[]) x_exit(0); } -static void -configuration_printer(const char* descr, const char* origin, void* context) -{ - assert(context); - auto f = static_cast(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[]) @@ -3977,13 +3962,13 @@ 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; @@ -3992,55 +3977,40 @@ ccache_main_options(int argc, char* argv[]) 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) { @@ -4048,16 +4018,14 @@ ccache_main_options(int argc, char* argv[]) } 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 @@ -4071,7 +4039,7 @@ ccache_main_options(int argc, char* argv[]) case 'x': // --show-compression initialize(); - compress_stats(conf); + compress_stats(g_config); break; case 'z': // --zero-stats @@ -4094,20 +4062,25 @@ int ccache_main(int argc, char* argv[]); 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; + } } diff --git a/src/ccache.hpp b/src/ccache.hpp index e8be9244d..4052e6f93 100644 --- a/src/ccache.hpp +++ b/src/ccache.hpp @@ -21,7 +21,6 @@ #include "system.hpp" -#include "conf.hpp" #include "counters.hpp" #include "third_party/minitrace.h" @@ -39,6 +38,9 @@ # define MYNAME "ccache" #endif +#define STRINGIFY(x) #x +#define TO_STRING(x) STRINGIFY(x) + extern const char CCACHE_VERSION[]; // Statistics fields in storage order. @@ -119,6 +121,8 @@ extern enum guessed_compiler guessed_compiler; // Buffer size for I/O operations. Should be a multiple of 4 KiB. #define READ_BUFFER_SIZE 65536 +class Config; + // ---------------------------------------------------------------------------- // args.c @@ -250,14 +254,14 @@ void exitfn_call(void); // ---------------------------------------------------------------------------- // 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 diff --git a/src/cleanup.cpp b/src/cleanup.cpp index 4ef6665ab..955a937be 100644 --- a/src/cleanup.cpp +++ b/src/cleanup.cpp @@ -17,6 +17,7 @@ // 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 @@ -161,16 +162,16 @@ sort_and_clean(void) // 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; @@ -215,11 +216,11 @@ clean_up_dir(struct conf* conf, const char* dir, double limit_multiple) // 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); } } @@ -264,14 +265,14 @@ wipe_dir(const char* dir) // 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); } diff --git a/src/compress.cpp b/src/compress.cpp index d7656fc9d..bd0666086 100644 --- a/src/compress.cpp +++ b/src/compress.cpp @@ -105,7 +105,7 @@ measure_fn(const char* fname, struct stat* st) // Process up all cache subdirectories. void -compress_stats(struct conf* conf) +compress_stats(const Config& config) { on_disk_size = 0; compr_size = 0; @@ -113,7 +113,7 @@ compress_stats(struct conf* conf) 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); } diff --git a/src/compression.cpp b/src/compression.cpp index 766c45cf3..1fb682a21 100644 --- a/src/compression.cpp +++ b/src/compression.cpp @@ -18,20 +18,18 @@ #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* diff --git a/src/conf.cpp b/src/conf.cpp deleted file mode 100644 index bf0a454d4..000000000 --- a/src/conf.cpp +++ /dev/null @@ -1,459 +0,0 @@ -// 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(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(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; -} diff --git a/src/conf.hpp b/src/conf.hpp deleted file mode 100644 index 54c27f03c..000000000 --- a/src/conf.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// 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); diff --git a/src/confitems.cpp b/src/confitems.cpp deleted file mode 100644 index 386a430f9..000000000 --- a/src/confitems.cpp +++ /dev/null @@ -1,334 +0,0 @@ -// 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; - } -} diff --git a/src/confitems.gperf b/src/confitems.gperf deleted file mode 100644 index fe9a1201b..000000000 --- a/src/confitems.gperf +++ /dev/null @@ -1,57 +0,0 @@ -%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) diff --git a/src/confitems.hpp b/src/confitems.hpp deleted file mode 100644 index aba12d983..000000000 --- a/src/confitems.hpp +++ /dev/null @@ -1,69 +0,0 @@ -// 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); diff --git a/src/envtoconfitems.gperf b/src/envtoconfitems.gperf deleted file mode 100644 index f7703f143..000000000 --- a/src/envtoconfitems.gperf +++ /dev/null @@ -1,49 +0,0 @@ -%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" diff --git a/src/envtoconfitems.hpp b/src/envtoconfitems.hpp deleted file mode 100644 index 7885b483a..000000000 --- a/src/envtoconfitems.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// 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); diff --git a/src/execute.cpp b/src/execute.cpp index fb5c638ff..f69e68176 100644 --- a/src/execute.cpp +++ b/src/execute.cpp @@ -17,12 +17,12 @@ // 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. @@ -298,7 +298,7 @@ find_executable(const char* name, const char* exclude_name) return x_strdup(name); } - char* path = conf->path; + const char* path = g_config.path().c_str(); if (str_eq(path, "")) { path = getenv("PATH"); } @@ -311,14 +311,16 @@ find_executable(const char* name, const char* exclude_name) } 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]; @@ -330,7 +332,7 @@ find_executable_in_path(const char* name, const char* exclude_name, char* path) } (void)exclude_name; if (ret) { - free(path); + free(path_buf); return x_strdup(namebuf); } #else @@ -355,14 +357,14 @@ find_executable_in_path(const char* name, const char* exclude_name, char* path) } // Found it! - free(path); + free(path_buf); return fname; } free(fname); #endif } - free(path); + free(path_buf); return NULL; } diff --git a/src/hashutil.cpp b/src/hashutil.cpp index 99888296d..e9c56c70f 100644 --- a/src/hashutil.cpp +++ b/src/hashutil.cpp @@ -82,7 +82,7 @@ check_for_temporal_macros(const char* str, size_t len) // 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, @@ -92,7 +92,7 @@ hash_source_code_string(struct conf* conf, // 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); } @@ -127,7 +127,7 @@ hash_source_code_string(struct conf* conf, // 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)) { @@ -141,7 +141,7 @@ hash_source_code_file(struct conf* conf, struct hash* hash, const char* 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; } diff --git a/src/hashutil.hpp b/src/hashutil.hpp index 32ebeec2e..9bd34aa67 100644 --- a/src/hashutil.hpp +++ b/src/hashutil.hpp @@ -20,7 +20,7 @@ #include "system.hpp" -#include "conf.hpp" +#include "Config.hpp" #include "hash.hpp" #include @@ -35,13 +35,14 @@ int strings_equal(void* str1, void* str2); #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); diff --git a/src/manifest.cpp b/src/manifest.cpp index babc6cf8a..2b9091f9c 100644 --- a/src/manifest.cpp +++ b/src/manifest.cpp @@ -451,7 +451,7 @@ out: } static bool -verify_result(struct conf* conf, +verify_result(const Config& config, struct manifest* mf, struct result* result, struct hashtable* stated_files, @@ -486,8 +486,8 @@ verify_result(struct conf* conf, 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; @@ -507,7 +507,7 @@ verify_result(struct conf* conf, auto actual = static_cast(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); @@ -674,7 +674,7 @@ add_result_entry(struct manifest* mf, // 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); @@ -694,7 +694,7 @@ manifest_get(struct conf* conf, const char* manifest_path) 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(x_malloc(sizeof(digest))); *name = mf->results[i - 1].name; goto out; diff --git a/src/manifest.hpp b/src/manifest.hpp index f46270a3a..21a558704 100644 --- a/src/manifest.hpp +++ b/src/manifest.hpp @@ -20,7 +20,7 @@ #include "system.hpp" -#include "conf.hpp" +#include "Config.hpp" #include "hashutil.hpp" #include "third_party/hashtable.h" @@ -28,7 +28,7 @@ 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); diff --git a/src/result.cpp b/src/result.cpp index 147ecaf53..25762310a 100644 --- a/src/result.cpp +++ b/src/result.cpp @@ -18,6 +18,7 @@ #include "result.hpp" +#include "Config.hpp" #include "ccache.hpp" #include "common_header.hpp" #include "compression.hpp" @@ -82,7 +83,6 @@ // // 1: Introduced in ccache 4.0. -extern const struct conf* conf; extern char* stats_file; const char RESULT_MAGIC[4] = {'c', 'C', 'r', 'S'}; @@ -282,14 +282,14 @@ get_raw_file_path(const char* result_path_in_cache, uint32_t entry_number) 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); @@ -584,7 +584,7 @@ error: 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; } diff --git a/src/result.hpp b/src/result.hpp index 98b4b4983..e49d407eb 100644 --- a/src/result.hpp +++ b/src/result.hpp @@ -20,7 +20,7 @@ #include "system.hpp" -#include "conf.hpp" +#include extern const char RESULT_MAGIC[4]; #define RESULT_VERSION 1 diff --git a/src/stats.cpp b/src/stats.cpp index 0a6b9c604..863f09c22 100644 --- a/src/stats.cpp +++ b/src/stats.cpp @@ -32,7 +32,6 @@ #include extern char* stats_file; -extern struct conf* conf; extern unsigned lock_staleness_limit; extern char* primary_config_path; extern char* secondary_config_path; @@ -266,9 +265,9 @@ stats_collect(struct counters* counters, time_t* last_updated) 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 @@ -318,9 +317,7 @@ stats_read(const char* sfile, struct counters* counters) static void stats_flush_to_file(const char* sfile, struct counters* updates) { - assert(conf); - - if (!conf->stats) { + if (!g_config.stats()) { return; } @@ -344,7 +341,8 @@ stats_flush_to_file(const char* sfile, struct counters* updates) // 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); } @@ -361,7 +359,7 @@ stats_flush_to_file(const char* sfile, struct counters* updates) 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)) { @@ -373,25 +371,25 @@ stats_flush_to_file(const char* sfile, struct counters* updates) 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); @@ -428,13 +426,12 @@ stats_get_pending(enum stats stat) 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", @@ -475,11 +472,11 @@ stats_summary(void) } } - 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); } @@ -491,8 +488,6 @@ stats_summary(void) void stats_print(void) { - assert(conf); - struct counters* counters = counters_init(STATS_END); time_t last_updated; stats_collect(counters, &last_updated); @@ -512,9 +507,7 @@ stats_print(void) 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); @@ -523,7 +516,7 @@ stats_zero(void) 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); diff --git a/src/util.cpp b/src/util.cpp index b50bf23c4..7d5153d7d 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -19,6 +19,7 @@ #include "util.hpp" +#include "Config.hpp" #include "Error.hpp" #include "ccache.hpp" @@ -64,15 +65,13 @@ # include #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. @@ -89,23 +88,22 @@ init_log(void) 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(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)); @@ -192,7 +190,7 @@ warn_log_fail(void) // 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); } @@ -320,14 +318,15 @@ fatal(const char* format, ...) 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; } diff --git a/unittest/framework.cpp b/unittest/framework.cpp index 4cab44be6..5a219c707 100644 --- a/unittest/framework.cpp +++ b/unittest/framework.cpp @@ -40,7 +40,6 @@ static int verbose; 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 : "") @@ -135,7 +134,7 @@ cct_test_begin(const char* name) cct_chdir(name); current_test = name; - putenv(CONFIG_PATH_ENV); + x_setenv("CCACHE_CONFIG_PATH", "/dev/null"); cc_reset(); } diff --git a/unittest/main.cpp b/unittest/main.cpp index 2c28d9d89..cd16e88ab 100644 --- a/unittest/main.cpp +++ b/unittest/main.cpp @@ -19,7 +19,7 @@ #include "framework.hpp" #define CATCH_CONFIG_RUNNER -#include "third_party/catch.hpp" +#include unsigned suite_args(unsigned); unsigned suite_argument_processing(unsigned); @@ -42,7 +42,6 @@ main(int argc, char** argv) &suite_compopt, &suite_compr_type_none, &suite_compr_type_zstd, - &suite_conf, &suite_counters, &suite_hash, &suite_hashutil, @@ -52,7 +51,7 @@ main(int argc, char** argv) NULL}; #ifdef _WIN32 - putenv("CCACHE_DETECT_SHEBANG=1"); + x_setenv("CCACHE_DETECT_SHEBANG", "1"); #endif char* testdir = format("testdir.%d", (int)getpid()); diff --git a/unittest/test_Config.cpp b/unittest/test_Config.cpp new file mode 100644 index 000000000..9b6602454 --- /dev/null +++ b/unittest/test_Config.cpp @@ -0,0 +1,454 @@ +// 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 +#include +#include +#include +#include + +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(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::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::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 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 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.; diff --git a/unittest/test_argument_processing.cpp b/unittest/test_argument_processing.cpp index 6f46cc7c6..c42a4895b 100644 --- a/unittest/test_argument_processing.cpp +++ b/unittest/test_argument_processing.cpp @@ -18,22 +18,20 @@ // 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 } @@ -124,7 +122,7 @@ TEST(cpp_only_flags_to_preprocessor_if_run_second_cpp_is_false) 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); @@ -150,7 +148,7 @@ TEST(cpp_only_flags_to_preprocessor_and_compiler_if_run_second_cpp_is_true) 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); @@ -183,8 +181,7 @@ TEST(sysroot_should_be_rewritten_if_basedir_is_used) 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); @@ -206,8 +203,7 @@ TEST(sysroot_with_separate_argument_should_be_rewritten_if_basedir_is_used) 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); @@ -392,8 +388,7 @@ TEST(isystem_flag_with_separate_arg_should_be_rewritten_if_basedir_is_used) 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); @@ -416,8 +411,7 @@ TEST(isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used) 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); @@ -443,8 +437,7 @@ TEST(I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used) 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); diff --git a/unittest/test_conf.cpp b/unittest/test_conf.cpp deleted file mode 100644 index d4c10bcf1..000000000 --- a/unittest/test_conf.cpp +++ /dev/null @@ -1,562 +0,0 @@ -// 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(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 diff --git a/unittest/test_legacy_util.cpp b/unittest/test_legacy_util.cpp index 06fac0d66..d0311fbc1 100644 --- a/unittest/test_legacy_util.cpp +++ b/unittest/test_legacy_util.cpp @@ -21,8 +21,6 @@ #include "../src/ccache.hpp" #include "framework.hpp" -static char FOO_ENV[] = "FOO=bar"; - TEST_SUITE(legacy_util) TEST(x_basename) @@ -93,7 +91,7 @@ TEST(subst_env_in_string) { char* errmsg; - putenv(FOO_ENV); + x_setenv("FOO", "bar"); CHECK_STR_EQ_FREE2("bar", subst_env_in_string("$FOO", &errmsg)); CHECK(!errmsg); diff --git a/unittest/test_util.cpp b/unittest/test_util.cpp index fcd4d1527..588a1ba47 100644 --- a/unittest/test_util.cpp +++ b/unittest/test_util.cpp @@ -18,7 +18,7 @@ #include "../src/util.hpp" -#include "third_party/catch.hpp" +#include TEST_CASE("util::read_file and util::write_file") {