当前位置: 首页 > 工具软件 > ccache > 使用案例 >

Ccache源码阅读(2)-- handle main options 1

易修洁
2023-12-01

options 处理

  • 如果第一个参数是以’-’ 开关,那么是进行设置
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,
  };
  // ......
 }
  • 定义options数组
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
  }
}
  • 小记:不可复制的模型,NonCopyable
class NonCopyable
{
protected:
 NonCopyable() = default;
private:
 NonCopyable(const NonCopyable&) = delete;
 NonCopyable& operator=(const NonCopyable&) = delete;
}
  • 从源码中易知 Context包含Config ,setup_config可以设置
  • 从文件中更新参数
/* 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);
 }
 类似资料: