int
ccache_main(int argc, const char* const* argv)
{
try {
// Check if we are being invoked as "ccache".
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);
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 cache_compilation(argc, argv);
} catch (const ErrorBase& e) {
PRINT(stderr, "ccache: error: {}\n", e.what());
return EXIT_FAILURE;
}
}
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,
};
// ......
}
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-stats", no_argument, nullptr, 's'},
{"version", no_argument, nullptr, 'V'},
{"zero-stats", no_argument, nullptr, 'z'},
{nullptr, 0, nullptr, 0}};
注: getopt.h
注: struct options的定义
Context ctx;
initialize(ctx, argc, argv);
static void
initialize(Context& ctx, int argc, const char* const* argv)
{
set_up_config(ctx.config);
set_up_context(ctx, argc, argv);
Logging::init(ctx.config);
// Set default umask for all files created by ccache from now on (if
// configured to). This is intentionally done after calling init_log so that
// the log file won't be affected by the umask but before creating the initial
// configuration file. The intention is that all files and directories in the
// cache directory should be affected by the configured umask and that no
// other files and directories should.
if (ctx.config.umask() != std::numeric_limits<uint32_t>::max()) {
ctx.original_umask = umask(ctx.config.umask());
}
LOG("=== CCACHE {} STARTED =========================================",
CCACHE_VERSION);
if (getenv("CCACHE_INTERNAL_TRACE")) {
#ifdef MTR_ENABLED
ctx.mini_trace = std::make_unique<MiniTrace>(ctx.args_info);
#else
LOG_RAW("Error: tracing is not enabled!");
#endif
}
}
class NonCopyable
{
protected:
NonCopyable() = default;
private:
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
}
/* file:ccache.cpp ; function: static void setup_config(Config& config) ; line:1880 ; */
config.set_secondary_config_path(env_ccache_configpath2
? env_ccache_configpath2
: FMT("{}/ccache.conf", SYSCONFDIR));
MTR_BEGIN("config", "conf_read_secondary");
// A missing config file in SYSCONFDIR is OK so don't check return value.
config.update_from_file(config.secondary_config_path());
MTR_END("config", "conf_read_secondary");
/* 内置设置 */
const std::unordered_map<std::string, ConfigItem> k_config_key_table = {
{"absolute_paths_in_stderr", ConfigItem::absolute_paths_in_stderr},
{"base_dir", ConfigItem::base_dir},
{"cache_dir", ConfigItem::cache_dir},
{"compiler", ConfigItem::compiler},
{"compiler_check", ConfigItem::compiler_check},
{"compiler_type", ConfigItem::compiler_type},
{"compression", ConfigItem::compression},
{"compression_level", ConfigItem::compression_level},
{"cpp_extension", ConfigItem::cpp_extension},
{"debug", ConfigItem::debug}, // 配置文件中 debug = true 即可启用日志
{"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},
{"ignore_options", ConfigItem::ignore_options},
{"inode_cache", ConfigItem::inode_cache},
{"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},
};
int c;
while ((c = getopt_long(argc,
const_cast<char* const*>(argv),
"cCd:k:hF:M:po:sVxX:z",
options,
nullptr))
!= -1) {
std::string arg = optarg ? optarg : std::string();
switch (c) {
case CHECKSUM_FILE: { // 校验和文件
Checksum checksum; // 校验和
Fd // Fd是一个维护句柄的一个类,自动Close句柄
fd( /* 如: ccache --checksum-file -
就是从标准输入流程得到内容 */
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...");
clean_old(
ctx, [&](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:
PRINT_RAW(stdout, Statistics::format_machine_readable(ctx.config));
break;
case 'c': // --cleanup
{
ProgressBar progress_bar("Cleaning...");
clean_up_all(ctx.config,
[&](double progress) { progress_bar.update(progress); });
if (isatty(STDOUT_FILENO)) {
PRINT_RAW(stdout, "\n");
}
break;
}
case 'C': // --clear
{
ProgressBar progress_bar("Clearing...");
wipe_all(ctx, [&](double progress) { progress_bar.update(progress); });
if (isatty(STDOUT_FILENO)) {
PRINT_RAW(stdout, "\n");
}
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::parse_unsigned(arg);
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);
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 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(ctx.config.primary_config_path(), key, value);
break;
}
case 'p': // --show-config
ctx.config.visit_items(configuration_printer);
break;
case 's': // --show-stats
PRINT_RAW(stdout, Statistics::format_human_readable(ctx.config));
break;
case 'V': // --version
PRINT(VERSION_TEXT, CCACHE_NAME, CCACHE_VERSION);
exit(EXIT_SUCCESS);
case 'x': // --show-compression
{
ProgressBar progress_bar("Scanning...");
compress_stats(ctx.config,
[&](double progress) { progress_bar.update(progress); });
break;
}
case 'X': // --recompress
{
optional<int8_t> wanted_level;
if (arg == "uncompressed") {
wanted_level = nullopt;
} else {
wanted_level =
Util::parse_signed(arg, INT8_MIN, INT8_MAX, "compression level");
}
ProgressBar progress_bar("Recompressing...");
compress_recompress(ctx, wanted_level, [&](double progress) {
progress_bar.update(progress);
});
break;
}
case 'z': // --zero-stats
Statistics::zero_all_counters(ctx.config);
PRINT_RAW(stdout, "Statistics zeroed\n");
break;
default:
PRINT(stderr, USAGE_TEXT, CCACHE_NAME, CCACHE_NAME);
exit(EXIT_FAILURE);
}
// Some of the above switches might have changed config settings, so run the
// setup again.
ctx.config = Config();
set_up_config(ctx.config);
}