blob: bdee5c9d49093d5d38359237799f67c7a7504147 [file] [log] [blame]
Ryan Mitchell833a1a62018-07-10 13:51:36 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
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 "Command.h"
18
19#include <iomanip>
20#include <iostream>
21#include <string>
22#include <vector>
23
24#include "androidfw/StringPiece.h"
25
26#include "util/Util.h"
27
28using android::StringPiece;
29
30namespace aapt {
31
32void Command::AddRequiredFlag(const StringPiece& name,
33 const StringPiece& description, std::string* value) {
34 auto func = [value](const StringPiece& arg) -> bool {
35 *value = arg.to_string();
36 return true;
37 };
38
39 flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
40}
41
42void Command::AddRequiredFlagList(const StringPiece& name,
43 const StringPiece& description,
44 std::vector<std::string>* value) {
45 auto func = [value](const StringPiece& arg) -> bool {
46 value->push_back(arg.to_string());
47 return true;
48 };
49
50 flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
51}
52
53void Command::AddOptionalFlag(const StringPiece& name,
54 const StringPiece& description,
55 Maybe<std::string>* value) {
56 auto func = [value](const StringPiece& arg) -> bool {
57 *value = arg.to_string();
58 return true;
59 };
60
61 flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
62}
63
64void Command::AddOptionalFlagList(const StringPiece& name,
65 const StringPiece& description,
66 std::vector<std::string>* value) {
67 auto func = [value](const StringPiece& arg) -> bool {
68 value->push_back(arg.to_string());
69 return true;
70 };
71
72 flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
73}
74
75void Command::AddOptionalFlagList(const StringPiece& name,
76 const StringPiece& description,
77 std::unordered_set<std::string>* value) {
78 auto func = [value](const StringPiece& arg) -> bool {
79 value->insert(arg.to_string());
80 return true;
81 };
82
83 flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
84}
85
86void Command::AddOptionalSwitch(const StringPiece& name,
87 const StringPiece& description, bool* value) {
88 auto func = [value](const StringPiece& arg) -> bool {
89 *value = true;
90 return true;
91 };
92
93 flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false});
94}
95
Ryan Mitchell214846d2018-09-19 16:57:01 -070096void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand, bool experimental) {
Ryan Mitchell833a1a62018-07-10 13:51:36 -070097 subcommand->fullname_ = name_ + " " + subcommand->name_;
Ryan Mitchell214846d2018-09-19 16:57:01 -070098 if (experimental) {
99 experimental_subcommands_.push_back(std::move(subcommand));
100 } else {
101 subcommands_.push_back(std::move(subcommand));
102 }
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700103}
104
105void Command::SetDescription(const android::StringPiece& description) {
106 description_ = description.to_string();
107}
108
109void Command::Usage(std::ostream* out) {
110 constexpr size_t kWidth = 50;
111
112 *out << fullname_;
113
114 if (!subcommands_.empty()) {
115 *out << " [subcommand]";
116 }
117
118 *out << " [options]";
119 for (const Flag& flag : flags_) {
120 if (flag.required) {
121 *out << " " << flag.name << " arg";
122 }
123 }
124
125 *out << " files...\n";
126
127 if (!subcommands_.empty()) {
128 *out << "\nSubcommands:\n";
129 for (auto& subcommand : subcommands_) {
130 std::string argline = subcommand->name_;
131
132 // Split the description by newlines and write out the argument (which is
133 // empty after the first line) followed by the description line. This will make sure
134 // that multiline descriptions are still right justified and aligned.
135 for (StringPiece line : util::Tokenize(subcommand->description_, '\n')) {
136 *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
137 argline = " ";
138 }
139 }
140 }
141
142 *out << "\nOptions:\n";
143
144 for (const Flag& flag : flags_) {
145 std::string argline = flag.name;
146 if (flag.num_args > 0) {
147 argline += " arg";
148 }
149
150 // Split the description by newlines and write out the argument (which is
151 // empty after the first line) followed by the description line. This will make sure
152 // that multiline descriptions are still right justified and aligned.
153 for (StringPiece line : util::Tokenize(flag.description, '\n')) {
154 *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
155 argline = " ";
156 }
157 }
158 *out << " " << std::setw(kWidth) << std::left << "-h"
159 << "Displays this help menu\n";
160 out->flush();
161}
162
163int Command::Execute(const std::vector<android::StringPiece>& args, std::ostream* out_error) {
164 std::vector<std::string> file_args;
165
166 for (size_t i = 0; i < args.size(); i++) {
167 StringPiece arg = args[i];
168 if (*(arg.data()) != '-') {
Ryan Mitchell214846d2018-09-19 16:57:01 -0700169 // Continue parsing as the subcommand if the first argument matches one of the subcommands
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700170 if (i == 0) {
171 for (auto& subcommand : subcommands_) {
172 if (arg == subcommand->name_ || arg==subcommand->short_name_) {
173 return subcommand->Execute(
174 std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error);
175 }
176 }
Ryan Mitchell214846d2018-09-19 16:57:01 -0700177 for (auto& subcommand : experimental_subcommands_) {
178 if (arg == subcommand->name_ || arg==subcommand->short_name_) {
179 return subcommand->Execute(
180 std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error);
181 }
182 }
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700183 }
184
185 file_args.push_back(arg.to_string());
186 continue;
187 }
188
189 if (arg == "-h" || arg == "--help") {
190 Usage(out_error);
191 return 1;
192 }
193
194 bool match = false;
195 for (Flag& flag : flags_) {
196 if (arg == flag.name) {
197 if (flag.num_args > 0) {
198 i++;
199 if (i >= args.size()) {
200 *out_error << flag.name << " missing argument.\n\n";
201 Usage(out_error);
202 return false;
203 }
204 flag.action(args[i]);
205 } else {
206 flag.action({});
207 }
208 flag.parsed = true;
209 match = true;
210 break;
211 }
212 }
213
214 if (!match) {
215 *out_error << "unknown option '" << arg << "'.\n\n";
216 Usage(out_error);
217 return 1;
218 }
219 }
220
221 for (const Flag& flag : flags_) {
222 if (flag.required && !flag.parsed) {
223 *out_error << "missing required flag " << flag.name << "\n\n";
224 Usage(out_error);
225 return 1;
226 }
227 }
228
229 return Action(file_args);
230}
231
232} // namespace aapt