16 #include "absl/flags/parse.h"
33 #include "absl/base/attributes.h"
34 #include "absl/base/config.h"
35 #include "absl/base/const_init.h"
36 #include "absl/base/thread_annotations.h"
37 #include "absl/flags/commandlineflag.h"
38 #include "absl/flags/config.h"
39 #include "absl/flags/flag.h"
40 #include "absl/flags/internal/commandlineflag.h"
41 #include "absl/flags/internal/flag.h"
42 #include "absl/flags/internal/parse.h"
43 #include "absl/flags/internal/private_handle_accessor.h"
44 #include "absl/flags/internal/program_name.h"
45 #include "absl/flags/internal/usage.h"
46 #include "absl/flags/reflection.h"
47 #include "absl/flags/usage.h"
48 #include "absl/flags/usage_config.h"
49 #include "absl/strings/ascii.h"
50 #include "absl/strings/str_cat.h"
51 #include "absl/strings/string_view.h"
52 #include "absl/strings/strip.h"
53 #include "absl/synchronization/mutex.h"
59 namespace flags_internal {
75 struct SpecifiedFlagsCompare {
76 bool operator()(
const CommandLineFlag* a,
const CommandLineFlag*
b)
const {
77 return a->Name() <
b->Name();
93 "comma-separated list of files to load flags from")
101 if (absl::flags_internal::flagfile_needs_processing) {
105 absl::flags_internal::flagfile_needs_processing =
true;
108 "comma-separated list of flags to set from the environment"
109 " [use 'export FLAGS_flag1=value']")
117 if (absl::flags_internal::fromenv_needs_processing) {
121 absl::flags_internal::fromenv_needs_processing =
true;
124 "comma-separated list of flags to try to set from the environment if "
133 if (absl::flags_internal::tryfromenv_needs_processing) {
135 "tryfromenv set twice before it is handled.");
138 absl::flags_internal::tryfromenv_needs_processing =
true;
141 ABSL_FLAG(std::vector<std::string>, undefok, {},
142 "comma-separated list of flag names that it is okay to specify "
143 "on the command line even if the program does not define a flag "
148 namespace flags_internal {
155 ArgsList(
int argc,
char* argv[]) :
args_(argv, argv + argc),
next_arg_(0) {}
156 explicit ArgsList(
const std::vector<std::string>&
args)
160 bool ReadFromFlagfile(
const std::string& flag_file_name);
163 int FrontIndex()
const {
return next_arg_; }
172 bool ArgsList::ReadFromFlagfile(
const std::string& flag_file_name) {
173 std::ifstream flag_file(flag_file_name);
177 absl::StrCat(
"Can't open flagfile ", flag_file_name),
true);
189 while (std::getline(flag_file,
line)) {
192 if (stripped.
empty() || stripped[0] ==
'#') {
197 if (stripped[0] ==
'-') {
198 if (stripped ==
"--") {
200 "Flagfile can't contain position arguments or --",
true);
211 absl::StrCat(
"Unexpected line in the flagfile ", flag_file_name,
": ",
226 bool GetEnvVar(
const char* var_name,
std::string& var_value) {
229 auto get_res = GetEnvironmentVariableA(var_name,
buf,
sizeof(
buf));
230 if (get_res >=
sizeof(
buf)) {
240 const char* val =
::getenv(var_name);
241 if (val ==
nullptr) {
263 std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue(
272 auto equal_sign_pos =
arg.find(
"=");
277 bool is_empty_value =
false;
280 value =
arg.substr(equal_sign_pos + 1);
281 is_empty_value =
value.empty();
294 bool is_negative =
false;
308 void CheckDefaultValuesParsingRoundtrip() {
311 if (
flag.IsRetired())
return;
314 if (
flag.IsOfType<
T>())
return;
332 bool ReadFlagfiles(
const std::vector<std::string>& flagfiles,
333 std::vector<ArgsList>& input_args) {
335 for (
auto it = flagfiles.rbegin();
it != flagfiles.rend(); ++
it) {
338 if (al.ReadFromFlagfile(*
it)) {
339 input_args.push_back(al);
353 bool ReadFlagsFromEnv(
const std::vector<std::string>& flag_names,
354 std::vector<ArgsList>& input_args,
355 bool fail_on_absent_in_env) {
357 std::vector<std::string>
args;
363 for (
const auto& flag_name : flag_names) {
365 if (flag_name ==
"fromenv" || flag_name ==
"tryfromenv") {
367 absl::StrCat(
"Infinite recursion on flag ", flag_name),
true);
375 if (!GetEnvVar(envname.c_str(), envval)) {
376 if (fail_on_absent_in_env) {
378 absl::StrCat(envname,
" not found in environment"),
true);
390 input_args.emplace_back(
args);
400 bool HandleGeneratorFlags(std::vector<ArgsList>& input_args,
401 std::vector<std::string>& flagfile_value) {
423 if (flags_internal::flagfile_needs_processing) {
426 if (input_args.size() == 1) {
427 flagfile_value.insert(flagfile_value.end(), flagfiles.begin(),
431 success &= ReadFlagfiles(flagfiles, input_args);
433 flags_internal::flagfile_needs_processing =
false;
439 if (flags_internal::fromenv_needs_processing) {
442 success &= ReadFlagsFromEnv(flags_list, input_args,
true);
444 flags_internal::fromenv_needs_processing =
false;
447 if (flags_internal::tryfromenv_needs_processing) {
450 success &= ReadFlagsFromEnv(flags_list, input_args,
false);
452 flags_internal::tryfromenv_needs_processing =
false;
460 void ResetGeneratorFlags(
const std::vector<std::string>& flagfile_value) {
465 if (!flagfile_value.empty()) {
468 flags_internal::flagfile_needs_processing =
false;
480 flags_internal::fromenv_needs_processing =
false;
481 flags_internal::tryfromenv_needs_processing =
false;
491 std::tuple<bool, absl::string_view> DeduceFlagValue(
const CommandLineFlag&
flag,
495 ArgsList* curr_list) {
512 if (
flag.IsOfType<
bool>()) {
514 if (is_empty_value) {
518 "Missing the value after assignment for the boolean flag '",
525 value = is_negative ?
"0" :
"1";
526 }
else if (is_negative) {
529 absl::StrCat(
"Negative form with assignment is not valid for the "
535 }
else if (is_negative) {
542 }
else if (
value.empty() && (!is_empty_value)) {
543 if (curr_list->Size() == 1) {
552 curr_list->PopFront();
553 value = curr_list->Front();
561 auto maybe_flag_name = std::get<0>(SplitNameAndValue(
value.substr(1)));
563 if (maybe_flag_name.empty() ||
564 std::get<0>(LocateFlag(maybe_flag_name)) !=
nullptr) {
569 "' to the value '",
value,
"'?"));
581 if (
std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) {
586 std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) {
600 "ParseCommandLine is not invoked yet");
602 return std::binary_search(specified_flags->begin(), specified_flags->end(),
603 flag_name, SpecifiedFlagsCompare{});
620 CheckDefaultValuesParsingRoundtrip();
622 std::vector<std::string> flagfile_value;
624 std::vector<ArgsList> input_args;
625 input_args.push_back(ArgsList(argc, argv));
627 std::vector<char*> output_args;
628 std::vector<char*> positional_args;
629 output_args.reserve(argc);
635 std::vector<std::pair<bool, std::string>> undefined_flag_names;
641 output_args.push_back(argv[0]);
644 if (specified_flags ==
nullptr) {
645 specified_flags =
new std::vector<const CommandLineFlag*>;
647 specified_flags->clear();
654 while (!input_args.empty()) {
656 success &= HandleGeneratorFlags(input_args, flagfile_value);
660 ArgsList& curr_list = input_args.back();
662 curr_list.PopFront();
664 if (curr_list.Size() == 0) {
665 input_args.pop_back();
673 bool arg_from_argv = input_args.size() == 1;
679 "Flagfile cannot contain positional argument");
681 positional_args.push_back(argv[curr_list.FrontIndex()]);
686 output_args.push_back(argv[curr_list.FrontIndex()]);
696 bool is_empty_value =
false;
698 std::tie(flag_name,
value, is_empty_value) = SplitNameAndValue(
arg);
702 if (flag_name.
empty()) {
704 "Flagfile cannot contain positional argument");
706 curr_list.PopFront();
712 bool is_negative =
false;
713 std::tie(
flag, is_negative) = LocateFlag(flag_name);
715 if (
flag ==
nullptr) {
722 undefined_flag_names.emplace_back(arg_from_argv,
729 auto curr_index = curr_list.FrontIndex();
730 bool value_success =
true;
731 std::tie(value_success,
value) =
732 DeduceFlagValue(*
flag,
value, is_negative, is_empty_value, &curr_list);
733 success &= value_success;
737 (curr_index != curr_list.FrontIndex())) {
738 output_args.push_back(argv[curr_list.FrontIndex()]);
747 if (
flag->IsRetired())
continue;
752 specified_flags->push_back(
flag);
756 for (
const auto& flag_name : undefined_flag_names) {
757 if (CanIgnoreUndefinedFlag(flag_name.second))
continue;
760 absl::StrCat(
"Unknown command line flag '", flag_name.second,
"'"),
766 #if ABSL_FLAGS_STRIP_NAMES
769 "NOTE: command line flags are disabled in this build",
true);
788 ResetGeneratorFlags(flagfile_value);
792 for (
auto arg : positional_args) {
793 output_args.push_back(
arg);
797 if (!input_args.empty()) {
798 for (
int arg_index = input_args.back().FrontIndex(); arg_index < argc;
800 output_args.push_back(argv[arg_index]);
805 specified_flags->shrink_to_fit();
806 std::sort(specified_flags->begin(), specified_flags->end(),
807 SpecifiedFlagsCompare{});