1//
2// Copyright 2019 The Abseil Authors.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// https://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#include "absl/flags/usage_config.h"
17
18#include <iostream>
19#include <memory>
20
21#include "absl/base/attributes.h"
22#include "absl/flags/internal/path_util.h"
23#include "absl/flags/internal/program_name.h"
24#include "absl/strings/str_cat.h"
25#include "absl/strings/strip.h"
26#include "absl/synchronization/mutex.h"
27
28extern "C" {
29
30// Additional report of fatal usage error message before we std::exit. Error is
31// fatal if is_fatal argument to ReportUsageError is true.
32ABSL_ATTRIBUTE_WEAK void AbslInternalReportFatalUsageError(absl::string_view) {}
33
34} // extern "C"
35
36namespace absl {
37namespace flags_internal {
38
39namespace {
40
41// --------------------------------------------------------------------
42// Returns true if flags defined in the filename should be reported with
43// -helpshort flag.
44
45bool ContainsHelpshortFlags(absl::string_view filename) {
46 // By default we only want flags in binary's main. We expect the main
47 // routine to reside in <program>.cc or <program>-main.cc or
48 // <program>_main.cc, where the <program> is the name of the binary.
49 auto suffix = flags_internal::Basename(filename);
50 if (!absl::ConsumePrefix(&suffix,
51 flags_internal::ShortProgramInvocationName()))
52 return false;
53 return absl::StartsWith(suffix, ".") || absl::StartsWith(suffix, "-main.") ||
54 absl::StartsWith(suffix, "_main.");
55}
56
57// --------------------------------------------------------------------
58// Returns true if flags defined in the filename should be reported with
59// -helppackage flag.
60
61bool ContainsHelppackageFlags(absl::string_view filename) {
62 // TODO(rogeeff): implement properly when registry is available.
63 return ContainsHelpshortFlags(filename);
64}
65
66// --------------------------------------------------------------------
67// Generates program version information into supplied output.
68
69std::string VersionString() {
70 std::string version_str(flags_internal::ShortProgramInvocationName());
71
72 version_str += "\n";
73
74#if !defined(NDEBUG)
75 version_str += "Debug build (NDEBUG not #defined)\n";
76#endif
77
78 return version_str;
79}
80
81// --------------------------------------------------------------------
82// Normalizes the filename specific to the build system/filesystem used.
83
84std::string NormalizeFilename(absl::string_view filename) {
85 // Skip any leading slashes
86 auto pos = filename.find_first_not_of("\\/");
87 if (pos == absl::string_view::npos) return "";
88
89 filename.remove_prefix(pos);
90 return std::string(filename);
91}
92
93// --------------------------------------------------------------------
94
95ABSL_CONST_INIT absl::Mutex custom_usage_config_guard(absl::kConstInit);
96ABSL_CONST_INIT FlagsUsageConfig* custom_usage_config
97 GUARDED_BY(custom_usage_config_guard) = nullptr;
98
99} // namespace
100
101FlagsUsageConfig GetUsageConfig() {
102 absl::MutexLock l(&custom_usage_config_guard);
103
104 if (custom_usage_config) return *custom_usage_config;
105
106 FlagsUsageConfig default_config;
107 default_config.contains_helpshort_flags = &ContainsHelpshortFlags;
108 default_config.contains_help_flags = &ContainsHelppackageFlags;
109 default_config.contains_helppackage_flags = &ContainsHelppackageFlags;
110 default_config.version_string = &VersionString;
111 default_config.normalize_filename = &NormalizeFilename;
112
113 return default_config;
114}
115
116void ReportUsageError(absl::string_view msg, bool is_fatal) {
117 std::cerr << "ERROR: " << msg << std::endl;
118
119 if (is_fatal) {
120 AbslInternalReportFatalUsageError(msg);
121 }
122}
123
124} // namespace flags_internal
125
126void SetFlagsUsageConfig(FlagsUsageConfig usage_config) {
127 absl::MutexLock l(&flags_internal::custom_usage_config_guard);
128
129 if (!usage_config.contains_helpshort_flags)
130 usage_config.contains_helpshort_flags =
131 flags_internal::ContainsHelpshortFlags;
132
133 if (!usage_config.contains_help_flags)
134 usage_config.contains_help_flags = flags_internal::ContainsHelppackageFlags;
135
136 if (!usage_config.contains_helppackage_flags)
137 usage_config.contains_helppackage_flags =
138 flags_internal::ContainsHelppackageFlags;
139
140 if (!usage_config.version_string)
141 usage_config.version_string = flags_internal::VersionString;
142
143 if (!usage_config.normalize_filename)
144 usage_config.normalize_filename = flags_internal::NormalizeFilename;
145
146 if (flags_internal::custom_usage_config)
147 *flags_internal::custom_usage_config = usage_config;
148 else
149 flags_internal::custom_usage_config = new FlagsUsageConfig(usage_config);
150}
151
152} // namespace absl
153