std::string parseArgs()

in thrift/compiler/compiler.cc [140:300]


std::string parseArgs(
    const std::vector<std::string>& arguments,
    parsing_params& pparams,
    gen_params& gparams,
    diagnostic_params& dparams) {
  // Check for necessary arguments, you gotta have at least a filename and
  // an output language flag.
  if (arguments.size() < 2) {
    usage();
    return {};
  }

  // A helper that grabs the next argument, if possible.
  // Outputs an error and returns nullptr if not.
  size_t arg_i = 1; // Skip the binary name.
  auto consume_arg = [&](const char* arg_name) -> const std::string* {
    // Note: The input filename must be the last argument.
    if (arg_i + 2 >= arguments.size()) {
      fprintf(
          stderr,
          "!!! Missing %s between %s and '%s'\n",
          arg_name,
          arguments[arg_i].c_str(),
          arguments[arg_i + 1].c_str());
      usage();
      return nullptr;
    }
    return &arguments[++arg_i];
  };

  // Hacky parameter handling... I didn't feel like using a library sorry!
  bool nowarn = false; // Guard so --nowarn and --strict are order agnostic.
  for (; arg_i < arguments.size() - 1;
       ++arg_i) { // Last argument is the src file.
    // Parse flag.
    std::string flag;
    if (arguments[arg_i].size() < 2 || arguments[arg_i][0] != '-') {
      fprintf(stderr, "!!! Expected flag, got: %s\n", arguments[arg_i].c_str());
      usage();
      return {};
    } else if (arguments[arg_i][1] == '-') {
      flag = arguments[arg_i].substr(2);
    } else {
      flag = arguments[arg_i].substr(1);
    }

    // Interpret flag.
    if (flag == "allow-experimental-features") {
      auto* arg = consume_arg("feature");
      if (arg == nullptr) {
        return {};
      }
      boost::algorithm::split(
          pparams.allow_experimental_features, *arg, isComma);
    } else if (flag == "debug") {
      dparams.debug = true;
      g_debug = 1;
    } else if (flag == "nowarn") {
      dparams.warn_level = g_warn = 0;
      nowarn = true;
    } else if (flag == "strict") {
      pparams.strict = 255;
      if (!nowarn) { // Don't override nowarn.
        dparams.warn_level = g_warn = 2;
      }
    } else if (flag == "v" || flag == "verbose") {
      dparams.info = true;
      g_verbose = 1;
    } else if (flag == "r" || flag == "recurse") {
      gparams.gen_recurse = true;
    } else if (flag == "allow-neg-keys") {
      pparams.allow_neg_field_keys = true;
    } else if (flag == "allow-neg-enum-vals") {
      dparams.allow_neg_enum_vals = true;
    } else if (flag == "allow-64bit-consts") {
      pparams.allow_64bit_consts = true;
    } else if (flag == "record-genfiles") {
      auto* arg = consume_arg("genfile file specification");
      if (arg == nullptr) {
        return {};
      }
      gparams.genfile = *arg;
    } else if (flag == "gen") {
      auto* arg = consume_arg("generator specification");
      if (arg == nullptr) {
        return {};
      }
      gparams.targets.push_back(*arg);
    } else if (flag == "cpp_use_include_prefix") {
      g_cpp_use_include_prefix = true;
    } else if (flag == "I") {
      auto* arg = consume_arg("include directory");
      if (arg == nullptr) {
        return {};
      }
      // An argument of "-I\ asdf" is invalid and has unknown results
      pparams.incl_searchpath.push_back(*arg);
    } else if (flag == "o" || flag == "out") {
      auto* arg = consume_arg("output directory");
      if (arg == nullptr) {
        return {};
      }
      std::string out_path = *arg;
      bool out_path_is_absolute = (flag == "out");

      // Strip out trailing \ on a Windows path
      if (platform_is_windows()) {
        int last = out_path.length() - 1;
        if (out_path[last] == '\\') {
          out_path.erase(last);
        }
      }

      if (out_path_is_absolute) {
        // Invoker specified `-out blah`. We are supposed to output directly
        // into blah, e.g. `blah/Foo.java`. Make the directory if necessary,
        // just like how for `-o blah` we make `o/gen-java`
        boost::system::error_code errc;
        boost::filesystem::create_directory(out_path, errc);
        if (errc) {
          fprintf(
              stderr,
              "Output path %s is unusable or not a directory\n",
              out_path.c_str());
          return {};
        }
      }
      if (!boost::filesystem::is_directory(out_path)) {
        fprintf(
            stderr,
            "Output path %s is unusable or not a directory\n",
            out_path.c_str());
        return {};
      }
      gparams.context = {std::move(out_path), out_path_is_absolute};
    } else {
      fprintf(
          stderr, "!!! Unrecognized option: %s\n", arguments[arg_i].c_str());
      usage();
      return {};
    }
  }

  if (const char* env_p = std::getenv("THRIFT_INCLUDE_PATH")) {
    std::vector<std::string> components;
    boost::algorithm::split(components, env_p, isPathSeparator);
    pparams.incl_searchpath.insert(
        pparams.incl_searchpath.end(), components.begin(), components.end());
  }

  // You gotta generate something!
  if (gparams.targets.empty()) {
    fprintf(stderr, "!!! No output language(s) specified\n\n");
    usage();
    return {};
  }

  // Return the input file name.
  assert(arg_i == arguments.size() - 1);
  return arguments[arg_i];
}