From: Joel Rosdahl Date: Fri, 23 Jul 2021 11:26:08 +0000 (+0200) Subject: Refactor main option processing to a separate file X-Git-Tag: v4.4~95 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=32c037e7d6e284c7b929cce579ca094d739b7dd8;p=thirdparty%2Fccache.git Refactor main option processing to a separate file --- diff --git a/src/ccache.cpp b/src/ccache.cpp index 4d013e9d5..81298bd99 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -21,7 +21,6 @@ #include "Args.hpp" #include "ArgsInfo.hpp" -#include "Checksum.hpp" #include "Context.hpp" #include "Depfile.hpp" #include "Fd.hpp" @@ -33,10 +32,7 @@ #include "Logging.hpp" #include "Manifest.hpp" #include "MiniTrace.hpp" -#include "ProgressBar.hpp" #include "Result.hpp" -#include "ResultDumper.hpp" -#include "ResultExtractor.hpp" #include "ResultRetriever.hpp" #include "SignalHandler.hpp" #include "TemporaryFile.hpp" @@ -54,10 +50,10 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -65,16 +61,6 @@ #include "third_party/nonstd/optional.hpp" #include "third_party/nonstd/string_view.hpp" -#ifdef HAVE_GETOPT_LONG -# include -#elif defined(_WIN32) -# include "third_party/win32/getopt.h" -#else -extern "C" { -# include "third_party/getopt_long.h" -} -#endif - #include #ifdef HAVE_UNISTD_H @@ -96,75 +82,6 @@ using nonstd::nullopt; using nonstd::optional; using nonstd::string_view; -constexpr const char VERSION_TEXT[] = - R"({} version {} -Features: {} - -Copyright (C) 2002-2007 Andrew Tridgell -Copyright (C) 2009-2021 Joel Rosdahl and other contributors - -See 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. -)"; - -constexpr const char USAGE_TEXT[] = - R"(Usage: - {} [options] - {} compiler [compiler options] - compiler [compiler options] (via symbolic link) - -Common options: - -c, --cleanup delete old files and recalculate size counters - (normally not needed as this is done - automatically) - -C, --clear clear the cache completely (except configuration) - --config-path PATH operate on configuration file PATH instead of the - default - -d, --directory PATH operate on cache directory PATH instead of the - default - --evict-older-than AGE remove files older than AGE (unsigned integer - with a d (days) or s (seconds) suffix) - -F, --max-files NUM set maximum number of files in cache to NUM (use - 0 for no limit) - -M, --max-size SIZE set maximum size of cache to SIZE (use 0 for no - limit); available suffixes: k, M, G, T (decimal) - and Ki, Mi, Gi, Ti (binary); default suffix: G - -X, --recompress LEVEL recompress the cache to level LEVEL (integer or - "uncompressed") using the Zstandard algorithm; - see "Cache compression" in the manual for details - -o, --set-config KEY=VAL set configuration item KEY to value VAL - -x, --show-compression show compression statistics - -p, --show-config show current configuration options in - human-readable format - --show-log-stats print statistics counters from the stats log - in human-readable format - -s, --show-stats show summary of configuration and statistics - counters in human-readable format - -z, --zero-stats zero statistics counters - - -h, --help print this help text - -V, --version print version and copyright information - -Options for scripting or debugging: - --checksum-file PATH print the checksum (64 bit XXH3) of the file at - PATH - --dump-manifest PATH dump manifest file at PATH in text format - --dump-result PATH dump result file at PATH in text format - --extract-result PATH extract data stored in result file at PATH to the - current working directory - -k, --get-config KEY print the value of configuration key KEY - --hash-file PATH print the hash (160 bit BLAKE3) of the file at - PATH - --print-stats print statistics counter IDs and corresponding - values in machine-parsable format - -See also the manual on . -)"; - // This is a string that identifies the current "version" of the hash sum // computed by ccache. If, for any reason, we want to force the hash sum to be // different for the same input in a new ccache version, we can just change @@ -1901,14 +1818,6 @@ set_up_uncached_err() Util::setenv("UNCACHED_ERR_FD", FMT("{}", uncached_fd)); } -static void -configuration_printer(const std::string& key, - const std::string& value, - const std::string& origin) -{ - PRINT(stdout, "({}) {} = {}\n", origin, key, value); -} - static int cache_compilation(int argc, const char* const* argv); static Statistic do_cache_compilation(Context& ctx, const char* const* argv); @@ -1943,41 +1852,6 @@ log_result_to_stats_log(Context& ctx) .log_result(ctx.args_info.input_file, *result_id); } -static void -print_compression_statistics(const storage::primary::CompressionStatistics& cs) -{ - const double ratio = cs.compr_size > 0 - ? static_cast(cs.content_size) / cs.compr_size - : 0.0; - const double savings = ratio > 0.0 ? 100.0 - (100.0 / ratio) : 0.0; - - const std::string on_disk_size_str = - Util::format_human_readable_size(cs.on_disk_size); - const std::string cache_size_str = - Util::format_human_readable_size(cs.compr_size + cs.incompr_size); - const std::string compr_size_str = - Util::format_human_readable_size(cs.compr_size); - const std::string content_size_str = - Util::format_human_readable_size(cs.content_size); - const std::string incompr_size_str = - Util::format_human_readable_size(cs.incompr_size); - - PRINT(stdout, - "Total data: {:>8s} ({} disk blocks)\n", - cache_size_str, - on_disk_size_str); - PRINT(stdout, - "Compressed data: {:>8s} ({:.1f}% of original size)\n", - compr_size_str, - 100.0 - savings); - PRINT(stdout, " - Original data: {:>8s}\n", content_size_str); - PRINT(stdout, - " - Compression ratio: {:>5.3f} x ({:.1f}% space savings)\n", - ratio, - savings); - PRINT(stdout, "Incompressible data: {:>8s}\n", incompr_size_str); -} - static void finalize_at_exit(Context& ctx) { @@ -2291,283 +2165,6 @@ do_cache_compilation(Context& ctx, const char* const* argv) return Statistic::cache_miss; } -// The main program when not doing a compile. -static int -handle_main_options(int argc, const char* const* argv) -{ - enum longopts { - CHECKSUM_FILE, - CONFIG_PATH, - DUMP_MANIFEST, - DUMP_RESULT, - EVICT_OLDER_THAN, - EXTRACT_RESULT, - HASH_FILE, - PRINT_STATS, - SHOW_LOG_STATS, - }; - static const struct option options[] = { - {"checksum-file", required_argument, nullptr, CHECKSUM_FILE}, - {"cleanup", no_argument, nullptr, 'c'}, - {"clear", no_argument, nullptr, 'C'}, - {"config-path", required_argument, nullptr, CONFIG_PATH}, - {"directory", required_argument, nullptr, 'd'}, - {"dump-manifest", required_argument, nullptr, DUMP_MANIFEST}, - {"dump-result", required_argument, nullptr, DUMP_RESULT}, - {"evict-older-than", required_argument, nullptr, EVICT_OLDER_THAN}, - {"extract-result", required_argument, nullptr, EXTRACT_RESULT}, - {"get-config", required_argument, nullptr, 'k'}, - {"hash-file", required_argument, nullptr, HASH_FILE}, - {"help", no_argument, nullptr, 'h'}, - {"max-files", required_argument, nullptr, 'F'}, - {"max-size", required_argument, nullptr, 'M'}, - {"print-stats", no_argument, nullptr, PRINT_STATS}, - {"recompress", required_argument, nullptr, 'X'}, - {"set-config", required_argument, nullptr, 'o'}, - {"show-compression", no_argument, nullptr, 'x'}, - {"show-config", no_argument, nullptr, 'p'}, - {"show-log-stats", no_argument, nullptr, SHOW_LOG_STATS}, - {"show-stats", no_argument, nullptr, 's'}, - {"version", no_argument, nullptr, 'V'}, - {"zero-stats", no_argument, nullptr, 'z'}, - {nullptr, 0, nullptr, 0}}; - - int c; - while ((c = getopt_long(argc, - const_cast(argv), - "cCd:k:hF:M:po:sVxX:z", - options, - nullptr)) - != -1) { - Context ctx; - - std::string arg = optarg ? optarg : std::string(); - - switch (c) { - case CHECKSUM_FILE: { - Checksum checksum; - Fd fd(arg == "-" ? STDIN_FILENO : open(arg.c_str(), O_RDONLY)); - Util::read_fd(*fd, [&checksum](const void* data, size_t size) { - checksum.update(data, size); - }); - PRINT(stdout, "{:016x}\n", checksum.digest()); - break; - } - - case CONFIG_PATH: - Util::setenv("CCACHE_CONFIGPATH", arg); - break; - - case DUMP_MANIFEST: - return Manifest::dump(arg, stdout) ? 0 : 1; - - case DUMP_RESULT: { - ResultDumper result_dumper(stdout); - Result::Reader result_reader(arg); - auto error = result_reader.read(result_dumper); - if (error) { - PRINT(stderr, "Error: {}\n", *error); - } - return error ? EXIT_FAILURE : EXIT_SUCCESS; - } - - case EVICT_OLDER_THAN: { - auto seconds = Util::parse_duration(arg); - ProgressBar progress_bar("Evicting..."); - ctx.storage.primary.clean_old( - [&](double progress) { progress_bar.update(progress); }, seconds); - if (isatty(STDOUT_FILENO)) { - PRINT_RAW(stdout, "\n"); - } - break; - } - - case EXTRACT_RESULT: { - ResultExtractor result_extractor("."); - Result::Reader result_reader(arg); - auto error = result_reader.read(result_extractor); - if (error) { - PRINT(stderr, "Error: {}\n", *error); - } - return error ? EXIT_FAILURE : EXIT_SUCCESS; - } - - case HASH_FILE: { - Hash hash; - if (arg == "-") { - hash.hash_fd(STDIN_FILENO); - } else { - hash.hash_file(arg); - } - PRINT(stdout, "{}\n", hash.digest().to_string()); - break; - } - - case PRINT_STATS: { - core::StatisticsCounters counters; - time_t last_updated; - std::tie(counters, last_updated) = - ctx.storage.primary.get_all_statistics(); - core::Statistics statistics(counters); - PRINT_RAW(stdout, statistics.format_machine_readable(last_updated)); - break; - } - - case 'c': // --cleanup - { - ProgressBar progress_bar("Cleaning..."); - ctx.storage.primary.clean_all( - [&](double progress) { progress_bar.update(progress); }); - if (isatty(STDOUT_FILENO)) { - PRINT_RAW(stdout, "\n"); - } - break; - } - - case 'C': // --clear - { - ProgressBar progress_bar("Clearing..."); - ctx.storage.primary.wipe_all( - [&](double progress) { progress_bar.update(progress); }); - if (isatty(STDOUT_FILENO)) { - PRINT_RAW(stdout, "\n"); - } -#ifdef INODE_CACHE_SUPPORTED - ctx.inode_cache.drop(); -#endif - break; - } - - case 'd': // --directory - Util::setenv("CCACHE_DIR", arg); - break; - - case 'h': // --help - PRINT(stdout, USAGE_TEXT, CCACHE_NAME, CCACHE_NAME); - exit(EXIT_SUCCESS); - - case 'k': // --get-config - PRINT(stdout, "{}\n", ctx.config.get_string_value(arg)); - break; - - case 'F': { // --max-files - auto files = util::value_or_throw(util::parse_unsigned(arg)); - ctx.config.set_value_in_file( - ctx.config.primary_config_path(), "max_files", arg); - if (files == 0) { - PRINT_RAW(stdout, "Unset cache file limit\n"); - } else { - PRINT(stdout, "Set cache file limit to {}\n", files); - } - break; - } - - case 'M': { // --max-size - uint64_t size = Util::parse_size(arg); - ctx.config.set_value_in_file( - ctx.config.primary_config_path(), "max_size", arg); - if (size == 0) { - PRINT_RAW(stdout, "Unset cache size limit\n"); - } else { - PRINT(stdout, - "Set cache size limit to {}\n", - Util::format_human_readable_size(size)); - } - break; - } - - case 'o': { // --set-config - // Start searching for equal sign at position 1 to improve error message - // for the -o=K=V case (key "=K" and value "V"). - size_t eq_pos = arg.find('=', 1); - if (eq_pos == std::string::npos) { - throw core::Error("missing equal sign in \"{}\"", arg); - } - std::string key = arg.substr(0, eq_pos); - std::string value = arg.substr(eq_pos + 1); - ctx.config.set_value_in_file( - ctx.config.primary_config_path(), key, value); - break; - } - - case 'p': // --show-config - ctx.config.visit_items(configuration_printer); - break; - - case SHOW_LOG_STATS: { - if (ctx.config.stats_log().empty()) { - throw core::Fatal("No stats log has been configured"); - } - PRINT(stdout, "{:36}{}\n", "stats log", ctx.config.stats_log()); - core::Statistics statistics( - core::StatsLog(ctx.config.stats_log()).read()); - const auto timestamp = - Stat::stat(ctx.config.stats_log(), Stat::OnError::log).mtime(); - PRINT_RAW(stdout, statistics.format_human_readable(timestamp, true)); - break; - } - - case 's': { // --show-stats - core::StatisticsCounters counters; - time_t last_updated; - std::tie(counters, last_updated) = - ctx.storage.primary.get_all_statistics(); - core::Statistics statistics(counters); - PRINT_RAW(stdout, statistics.format_config_header(ctx.config)); - PRINT_RAW(stdout, statistics.format_human_readable(last_updated, false)); - PRINT_RAW(stdout, statistics.format_config_footer(ctx.config)); - break; - } - - case 'V': // --version - PRINT(VERSION_TEXT, CCACHE_NAME, CCACHE_VERSION, storage::get_features()); - exit(EXIT_SUCCESS); - - case 'x': // --show-compression - { - ProgressBar progress_bar("Scanning..."); - const auto compression_statistics = - ctx.storage.primary.get_compression_statistics( - [&](double progress) { progress_bar.update(progress); }); - if (isatty(STDOUT_FILENO)) { - PRINT_RAW(stdout, "\n\n"); - } - print_compression_statistics(compression_statistics); - break; - } - - case 'X': // --recompress - { - optional wanted_level; - if (arg == "uncompressed") { - wanted_level = nullopt; - } else { - wanted_level = util::value_or_throw( - util::parse_signed(arg, INT8_MIN, INT8_MAX, "compression level")); - } - - ProgressBar progress_bar("Recompressing..."); - ctx.storage.primary.recompress( - wanted_level, [&](double progress) { progress_bar.update(progress); }); - break; - } - - case 'z': // --zero-stats - ctx.storage.primary.zero_all_statistics(); - PRINT_RAW(stdout, "Statistics zeroed\n"); - break; - - default: - PRINT(stderr, USAGE_TEXT, CCACHE_NAME, CCACHE_NAME); - exit(EXIT_FAILURE); - } - } - - return 0; -} - -int ccache_main(int argc, const char* const* argv); - int ccache_main(int argc, const char* const* argv) { @@ -2576,13 +2173,13 @@ ccache_main(int argc, const char* const* argv) std::string program_name(Util::base_name(argv[0])); if (Util::same_program_name(program_name, CCACHE_NAME)) { if (argc < 2) { - PRINT(stderr, USAGE_TEXT, CCACHE_NAME, CCACHE_NAME); + PRINT_RAW(stderr, core::get_usage_text()); exit(EXIT_FAILURE); } // If the first argument isn't an option, then assume we are being // passed a compiler name and options. if (argv[1][0] == '-') { - return handle_main_options(argc, argv); + return core::process_main_options(argc, argv); } } diff --git a/src/ccache.hpp b/src/ccache.hpp index 7b8f06ffa..2b799fe8e 100644 --- a/src/ccache.hpp +++ b/src/ccache.hpp @@ -36,6 +36,8 @@ using FindExecutableFunction = const std::string& name, const std::string& exclude_name)>; +int ccache_main(int argc, const char* const* argv); + // Tested by unit tests. void find_compiler(Context& ctx, const FindExecutableFunction& find_executable_function); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c282613fb..a00797514 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -3,6 +3,7 @@ set( ${CMAKE_CURRENT_SOURCE_DIR}/Statistics.cpp ${CMAKE_CURRENT_SOURCE_DIR}/StatisticsCounters.cpp ${CMAKE_CURRENT_SOURCE_DIR}/StatsLog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mainoptions.cpp ) target_sources(ccache_lib PRIVATE ${sources}) diff --git a/src/core/mainoptions.cpp b/src/core/mainoptions.cpp new file mode 100644 index 000000000..678912582 --- /dev/null +++ b/src/core/mainoptions.cpp @@ -0,0 +1,458 @@ +// Copyright (C) 2021 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 "mainoptions.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_GETOPT_LONG +# include +#elif defined(_WIN32) +# include +#else +extern "C" { +# include +} +#endif + +namespace core { + +constexpr const char VERSION_TEXT[] = + R"({0} version {1} +Features: {2} + +Copyright (C) 2002-2007 Andrew Tridgell +Copyright (C) 2009-2021 Joel Rosdahl and other contributors + +See 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. +)"; + +constexpr const char USAGE_TEXT[] = + R"(Usage: + {0} [options] + {0} compiler [compiler options] + compiler [compiler options] (via symbolic link) + +Common options: + -c, --cleanup delete old files and recalculate size counters + (normally not needed as this is done + automatically) + -C, --clear clear the cache completely (except configuration) + --config-path PATH operate on configuration file PATH instead of the + default + -d, --directory PATH operate on cache directory PATH instead of the + default + --evict-older-than AGE remove files older than AGE (unsigned integer + with a d (days) or s (seconds) suffix) + -F, --max-files NUM set maximum number of files in cache to NUM (use + 0 for no limit) + -M, --max-size SIZE set maximum size of cache to SIZE (use 0 for no + limit); available suffixes: k, M, G, T (decimal) + and Ki, Mi, Gi, Ti (binary); default suffix: G + -X, --recompress LEVEL recompress the cache to level LEVEL (integer or + "uncompressed") using the Zstandard algorithm; + see "Cache compression" in the manual for details + -o, --set-config KEY=VAL set configuration item KEY to value VAL + -x, --show-compression show compression statistics + -p, --show-config show current configuration options in + human-readable format + --show-log-stats print statistics counters from the stats log + in human-readable format + -s, --show-stats show summary of configuration and statistics + counters in human-readable format + -z, --zero-stats zero statistics counters + + -h, --help print this help text + -V, --version print version and copyright information + +Options for scripting or debugging: + --checksum-file PATH print the checksum (64 bit XXH3) of the file at + PATH + --dump-manifest PATH dump manifest file at PATH in text format + --dump-result PATH dump result file at PATH in text format + --extract-result PATH extract data stored in result file at PATH to the + current working directory + -k, --get-config KEY print the value of configuration key KEY + --hash-file PATH print the hash (160 bit BLAKE3) of the file at + PATH + --print-stats print statistics counter IDs and corresponding + values in machine-parsable format + +See also the manual on . +)"; + +static void +configuration_printer(const std::string& key, + const std::string& value, + const std::string& origin) +{ + PRINT(stdout, "({}) {} = {}\n", origin, key, value); +} + +static void +print_compression_statistics(const storage::primary::CompressionStatistics& cs) +{ + const double ratio = cs.compr_size > 0 + ? static_cast(cs.content_size) / cs.compr_size + : 0.0; + const double savings = ratio > 0.0 ? 100.0 - (100.0 / ratio) : 0.0; + + const std::string on_disk_size_str = + Util::format_human_readable_size(cs.on_disk_size); + const std::string cache_size_str = + Util::format_human_readable_size(cs.compr_size + cs.incompr_size); + const std::string compr_size_str = + Util::format_human_readable_size(cs.compr_size); + const std::string content_size_str = + Util::format_human_readable_size(cs.content_size); + const std::string incompr_size_str = + Util::format_human_readable_size(cs.incompr_size); + + PRINT(stdout, + "Total data: {:>8s} ({} disk blocks)\n", + cache_size_str, + on_disk_size_str); + PRINT(stdout, + "Compressed data: {:>8s} ({:.1f}% of original size)\n", + compr_size_str, + 100.0 - savings); + PRINT(stdout, " - Original data: {:>8s}\n", content_size_str); + PRINT(stdout, + " - Compression ratio: {:>5.3f} x ({:.1f}% space savings)\n", + ratio, + savings); + PRINT(stdout, "Incompressible data: {:>8s}\n", incompr_size_str); +} + +static std::string +get_version_text() +{ + return FMT( + VERSION_TEXT, CCACHE_NAME, CCACHE_VERSION, storage::get_features()); +} + +std::string +get_usage_text() +{ + return FMT(USAGE_TEXT, CCACHE_NAME); +} + +int +process_main_options(int argc, const char* const* argv) +{ + enum longopts { + CHECKSUM_FILE, + CONFIG_PATH, + DUMP_MANIFEST, + DUMP_RESULT, + EVICT_OLDER_THAN, + EXTRACT_RESULT, + HASH_FILE, + PRINT_STATS, + SHOW_LOG_STATS, + }; + static const struct option options[] = { + {"checksum-file", required_argument, nullptr, CHECKSUM_FILE}, + {"cleanup", no_argument, nullptr, 'c'}, + {"clear", no_argument, nullptr, 'C'}, + {"config-path", required_argument, nullptr, CONFIG_PATH}, + {"directory", required_argument, nullptr, 'd'}, + {"dump-manifest", required_argument, nullptr, DUMP_MANIFEST}, + {"dump-result", required_argument, nullptr, DUMP_RESULT}, + {"evict-older-than", required_argument, nullptr, EVICT_OLDER_THAN}, + {"extract-result", required_argument, nullptr, EXTRACT_RESULT}, + {"get-config", required_argument, nullptr, 'k'}, + {"hash-file", required_argument, nullptr, HASH_FILE}, + {"help", no_argument, nullptr, 'h'}, + {"max-files", required_argument, nullptr, 'F'}, + {"max-size", required_argument, nullptr, 'M'}, + {"print-stats", no_argument, nullptr, PRINT_STATS}, + {"recompress", required_argument, nullptr, 'X'}, + {"set-config", required_argument, nullptr, 'o'}, + {"show-compression", no_argument, nullptr, 'x'}, + {"show-config", no_argument, nullptr, 'p'}, + {"show-log-stats", no_argument, nullptr, SHOW_LOG_STATS}, + {"show-stats", no_argument, nullptr, 's'}, + {"version", no_argument, nullptr, 'V'}, + {"zero-stats", no_argument, nullptr, 'z'}, + {nullptr, 0, nullptr, 0}}; + + int c; + while ((c = getopt_long(argc, + const_cast(argv), + "cCd:k:hF:M:po:sVxX:z", + options, + nullptr)) + != -1) { + Config config; + config.read(); + + std::string arg = optarg ? optarg : std::string(); + + switch (c) { + case CHECKSUM_FILE: { + Checksum checksum; + Fd fd(arg == "-" ? STDIN_FILENO : open(arg.c_str(), O_RDONLY)); + Util::read_fd(*fd, [&checksum](const void* data, size_t size) { + checksum.update(data, size); + }); + PRINT(stdout, "{:016x}\n", checksum.digest()); + break; + } + + case CONFIG_PATH: + Util::setenv("CCACHE_CONFIGPATH", arg); + break; + + case DUMP_MANIFEST: + return Manifest::dump(arg, stdout) ? 0 : 1; + + case DUMP_RESULT: { + ResultDumper result_dumper(stdout); + Result::Reader result_reader(arg); + auto error = result_reader.read(result_dumper); + if (error) { + PRINT(stderr, "Error: {}\n", *error); + } + return error ? EXIT_FAILURE : EXIT_SUCCESS; + } + + case EVICT_OLDER_THAN: { + auto seconds = Util::parse_duration(arg); + ProgressBar progress_bar("Evicting..."); + storage::primary::PrimaryStorage(config).clean_old( + [&](double progress) { progress_bar.update(progress); }, seconds); + if (isatty(STDOUT_FILENO)) { + PRINT_RAW(stdout, "\n"); + } + break; + } + + case EXTRACT_RESULT: { + ResultExtractor result_extractor("."); + Result::Reader result_reader(arg); + auto error = result_reader.read(result_extractor); + if (error) { + PRINT(stderr, "Error: {}\n", *error); + } + return error ? EXIT_FAILURE : EXIT_SUCCESS; + } + + case HASH_FILE: { + Hash hash; + if (arg == "-") { + hash.hash_fd(STDIN_FILENO); + } else { + hash.hash_file(arg); + } + PRINT(stdout, "{}\n", hash.digest().to_string()); + break; + } + + case PRINT_STATS: { + StatisticsCounters counters; + time_t last_updated; + std::tie(counters, last_updated) = + storage::primary::PrimaryStorage(config).get_all_statistics(); + Statistics statistics(counters); + PRINT_RAW(stdout, statistics.format_machine_readable(last_updated)); + break; + } + + case 'c': // --cleanup + { + ProgressBar progress_bar("Cleaning..."); + storage::primary::PrimaryStorage(config).clean_all( + [&](double progress) { progress_bar.update(progress); }); + if (isatty(STDOUT_FILENO)) { + PRINT_RAW(stdout, "\n"); + } + break; + } + + case 'C': // --clear + { + ProgressBar progress_bar("Clearing..."); + storage::primary::PrimaryStorage(config).wipe_all( + [&](double progress) { progress_bar.update(progress); }); + if (isatty(STDOUT_FILENO)) { + PRINT_RAW(stdout, "\n"); + } +#ifdef INODE_CACHE_SUPPORTED + InodeCache(config).drop(); +#endif + break; + } + + case 'd': // --directory + Util::setenv("CCACHE_DIR", arg); + break; + + case 'h': // --help + PRINT(stdout, USAGE_TEXT, CCACHE_NAME, CCACHE_NAME); + exit(EXIT_SUCCESS); + + case 'k': // --get-config + PRINT(stdout, "{}\n", config.get_string_value(arg)); + break; + + case 'F': { // --max-files + auto files = util::value_or_throw(util::parse_unsigned(arg)); + config.set_value_in_file(config.primary_config_path(), "max_files", arg); + if (files == 0) { + PRINT_RAW(stdout, "Unset cache file limit\n"); + } else { + PRINT(stdout, "Set cache file limit to {}\n", files); + } + break; + } + + case 'M': { // --max-size + uint64_t size = Util::parse_size(arg); + config.set_value_in_file(config.primary_config_path(), "max_size", arg); + if (size == 0) { + PRINT_RAW(stdout, "Unset cache size limit\n"); + } else { + PRINT(stdout, + "Set cache size limit to {}\n", + Util::format_human_readable_size(size)); + } + break; + } + + case 'o': { // --set-config + // Start searching for equal sign at position 1 to improve error message + // for the -o=K=V case (key "=K" and value "V"). + size_t eq_pos = arg.find('=', 1); + if (eq_pos == std::string::npos) { + throw Error("missing equal sign in \"{}\"", arg); + } + std::string key = arg.substr(0, eq_pos); + std::string value = arg.substr(eq_pos + 1); + config.set_value_in_file(config.primary_config_path(), key, value); + break; + } + + case 'p': // --show-config + config.visit_items(configuration_printer); + break; + + case SHOW_LOG_STATS: { + if (config.stats_log().empty()) { + throw Fatal("No stats log has been configured"); + } + PRINT(stdout, "{:36}{}\n", "stats log", config.stats_log()); + Statistics statistics(StatsLog(config.stats_log()).read()); + const auto timestamp = + Stat::stat(config.stats_log(), Stat::OnError::log).mtime(); + PRINT_RAW(stdout, statistics.format_human_readable(timestamp, true)); + break; + } + + case 's': { // --show-stats + StatisticsCounters counters; + time_t last_updated; + std::tie(counters, last_updated) = + storage::primary::PrimaryStorage(config).get_all_statistics(); + Statistics statistics(counters); + PRINT_RAW(stdout, statistics.format_config_header(config)); + PRINT_RAW(stdout, statistics.format_human_readable(last_updated, false)); + PRINT_RAW(stdout, statistics.format_config_footer(config)); + break; + } + + case 'V': // --version + PRINT_RAW(stdout, get_version_text()); + exit(EXIT_SUCCESS); + + case 'x': // --show-compression + { + ProgressBar progress_bar("Scanning..."); + const auto compression_statistics = + storage::primary::PrimaryStorage(config).get_compression_statistics( + [&](double progress) { progress_bar.update(progress); }); + if (isatty(STDOUT_FILENO)) { + PRINT_RAW(stdout, "\n\n"); + } + print_compression_statistics(compression_statistics); + break; + } + + case 'X': // --recompress + { + nonstd::optional wanted_level; + if (arg == "uncompressed") { + wanted_level = nonstd::nullopt; + } else { + wanted_level = util::value_or_throw( + util::parse_signed(arg, INT8_MIN, INT8_MAX, "compression level")); + } + + ProgressBar progress_bar("Recompressing..."); + storage::primary::PrimaryStorage(config).recompress( + wanted_level, [&](double progress) { progress_bar.update(progress); }); + break; + } + + case 'z': // --zero-stats + storage::primary::PrimaryStorage(config).zero_all_statistics(); + PRINT_RAW(stdout, "Statistics zeroed\n"); + break; + + default: + PRINT(stderr, USAGE_TEXT, CCACHE_NAME, CCACHE_NAME); + exit(EXIT_FAILURE); + } + } + + return 0; +} + +} // namespace core diff --git a/src/core/mainoptions.hpp b/src/core/mainoptions.hpp new file mode 100644 index 000000000..e0f33e60b --- /dev/null +++ b/src/core/mainoptions.hpp @@ -0,0 +1,30 @@ +// Copyright (C) 2021 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 + +namespace core { + +// The main program when not doing a compile. +int process_main_options(int argc, const char* const* argv); + +std::string get_usage_text(); + +} // namespace core