1 /**
2 * Copyright 2020-2021 Huawei Technologies Co., Ltd
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 * http://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
17 #include "tools/common/flag_parser.h"
18 #include "src/common/log_adapter.h"
19 #include "tools/converter/converter_context.h"
20 #include "src/common/file_utils.h"
21
22 namespace mindspore {
23 namespace lite {
24 constexpr auto kConverterLite = "converter_lite";
25 // parse flags read from command line
ParseFlags(int argc,const char * const * argv,bool supportUnknown,bool supportDuplicate)26 Option<std::string> FlagParser::ParseFlags(int argc, const char *const *argv, bool supportUnknown,
27 bool supportDuplicate) {
28 MS_ASSERT(argv != nullptr);
29 const int FLAG_PREFIX_LEN = 2;
30 if (argc <= 0) {
31 MS_LOG(ERROR) << "The arguments number is out of range";
32 return Option<std::string>("Failed: flags is not valid");
33 }
34 binName = GetFileName(argv[0]);
35 if (binName == kConverterLite) {
36 std::map<std::string, std::string> flag_argv_maps;
37 auto real_path = RealPath(argv[0]);
38 if (real_path.empty()) {
39 MS_LOG(WARNING) << kConverterLite << " path is not exist.";
40 }
41 flag_argv_maps["converter_lite_path"] = real_path;
42 ConverterInnerContext::GetInstance()->SetExternalUsedConfigInfos(mindspore::converter::KConverterParam,
43 flag_argv_maps);
44 }
45
46 std::multimap<std::string, Option<std::string>> keyValues{};
47 for (int i = 1; i < argc; i++) {
48 std::string tmp = argv[i];
49 Trim(&tmp);
50 const std::string flagItem(tmp);
51
52 if (flagItem == "--") {
53 break;
54 }
55
56 if (flagItem.find("--") == std::string::npos) {
57 return Option<std::string>("Failed: flag " + flagItem + " is not valid.");
58 }
59
60 std::string key;
61 Option<std::string> value = Option<std::string>(None());
62
63 size_t pos = flagItem.find_first_of('=');
64 if (pos == std::string::npos) {
65 key = flagItem.substr(FLAG_PREFIX_LEN);
66 } else {
67 key = flagItem.substr(FLAG_PREFIX_LEN, pos - FLAG_PREFIX_LEN);
68 value = Option<std::string>(flagItem.substr(pos + 1));
69 }
70
71 keyValues.insert(std::pair<std::string, Option<std::string>>(key, value));
72 }
73
74 Option<std::string> ret = Option<std::string>(InnerParseFlags(&keyValues));
75 if (ret.IsSome()) {
76 return Option<std::string>(ret.Get());
77 }
78
79 return Option<std::string>(None());
80 }
81
GetRealFlagName(std::string * flagName,const std::string & oriFlagName)82 bool FlagParser::GetRealFlagName(std::string *flagName, const std::string &oriFlagName) {
83 MS_ASSERT(flagName != nullptr);
84 const int BOOL_TYPE_FLAG_PREFIX_LEN = 3;
85 bool opaque = false;
86 if (StartsWithPrefix(oriFlagName, "no-")) {
87 *flagName = oriFlagName.substr(BOOL_TYPE_FLAG_PREFIX_LEN);
88 opaque = true;
89 } else {
90 *flagName = oriFlagName;
91 }
92 return opaque;
93 }
94
95 // Inner parse function
InnerParseFlags(std::multimap<std::string,Option<std::string>> * keyValues)96 Option<std::string> FlagParser::InnerParseFlags(std::multimap<std::string, Option<std::string>> *keyValues) {
97 MS_ASSERT(keyValues != nullptr);
98 for (auto &keyValue : *keyValues) {
99 std::string flagName;
100 bool opaque = GetRealFlagName(&flagName, keyValue.first);
101 Option<std::string> flagValue = keyValue.second;
102
103 auto item = flags.find(flagName);
104 if (item == flags.end()) {
105 return Option<std::string>(std::string(flagName + " is not a valid flag"));
106 }
107 FlagInfo *flag = &(item->second);
108 if (flag == nullptr) {
109 return Option<std::string>("Failed: flag is nullptr");
110 }
111 if (flag->isParsed) {
112 return Option<std::string>("Failed: already parsed flag: " + flagName);
113 }
114 std::string tmpValue;
115 if (!flag->isBoolean) {
116 if (opaque) {
117 return Option<std::string>(flagName + " is not a boolean type");
118 }
119 if (flagValue.IsNone()) {
120 return Option<std::string>("No value provided for non-boolean type: " + flagName);
121 }
122 tmpValue = flagValue.Get();
123 } else {
124 if (flagValue.IsNone() || flagValue.Get().empty()) {
125 tmpValue = !opaque ? "true" : "false";
126 } else if (!opaque) {
127 tmpValue = flagValue.Get();
128 } else {
129 return Option<std::string>(std::string("Boolean flag can not have non-empty value"));
130 }
131 }
132 // begin to parse value
133 Option<Nothing> ret = flag->parse(this, tmpValue);
134 if (ret.IsNone()) {
135 return Option<std::string>("Failed to parse value for: " + flag->flagName);
136 }
137 flag->isParsed = true;
138 }
139
140 // to check flags not given in command line but added as in constructor
141 for (auto &flag : flags) {
142 if (flag.second.isRequired && !flag.second.isParsed) {
143 return Option<std::string>("Error, value of '" + flag.first + "' not provided");
144 }
145 }
146
147 return Option<std::string>(None());
148 }
149
ReplaceAll(std::string * str,const std::string & oldValue,const std::string & newValue)150 void ReplaceAll(std::string *str, const std::string &oldValue, const std::string &newValue) {
151 if (str == nullptr) {
152 MS_LOG(ERROR) << "Input str is nullptr";
153 return;
154 }
155 while (true) {
156 std::string::size_type pos(0);
157 if ((pos = str->find(oldValue)) != std::string::npos) {
158 str->replace(pos, oldValue.length(), newValue);
159 } else {
160 break;
161 }
162 }
163 }
164
Usage(const Option<std::string> & usgMsg) const165 std::string FlagParser::Usage(const Option<std::string> &usgMsg) const {
166 // first line, brief of the usage
167 std::string usageString = usgMsg.IsSome() ? usgMsg.Get() + "\n" : "";
168 // usage of bin name
169 usageString += usageMsg.IsNone() ? "\nusage: " + binName + " [options]\n" : usageMsg.Get() + "\n";
170 // help line of help message, usageLine:message of parameters
171 std::string helpLine;
172 std::string usageLine;
173 uint32_t i = 0;
174 for (auto flag = flags.begin(); flag != flags.end(); flag++) {
175 std::string flagName = flag->second.flagName;
176 std::string helpInfo = flag->second.helpInfo;
177 // parameter line
178 std::string thisLine = flagName == "help" ? " --" + flagName : " --" + flagName + "=VALUE";
179 if (++i <= flags.size()) {
180 // add parameter help message of each line
181 thisLine += " " + helpInfo;
182 ReplaceAll(&helpInfo, "\n\r", "\n");
183 usageLine += thisLine + "\n";
184 } else {
185 // brief help message
186 helpLine = thisLine + " " + helpInfo + "\n";
187 }
188 }
189 // total usage is brief of usage+ brief of bin + help message + brief of
190 // parameters
191 return usageString + helpLine + usageLine;
192 }
193 } // namespace lite
194 } // namespace mindspore
195