blob: 89618d3a46762ec542f72e44b7ecbb7672a198bc [file] [log] [blame]
Shane Farmer74cdea32017-05-12 16:22:36 -07001/*
2 * Copyright (C) 2017 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 "configuration/ConfigurationParser.h"
18
19#include <algorithm>
20#include <functional>
21#include <memory>
22#include <utility>
23
24#include <android-base/logging.h>
25
26#include "ConfigDescription.h"
27#include "Diagnostics.h"
28#include "util/Util.h"
29#include "xml/XmlActionExecutor.h"
30#include "xml/XmlDom.h"
31#include "xml/XmlUtil.h"
32
33namespace aapt {
34
35namespace {
36
37using ::aapt::configuration::Abi;
38using ::aapt::configuration::AndroidManifest;
39using ::aapt::configuration::AndroidSdk;
40using ::aapt::configuration::Artifact;
41using ::aapt::configuration::Configuration;
42using ::aapt::configuration::GlTexture;
43using ::aapt::configuration::Group;
44using ::aapt::configuration::Locale;
45using ::aapt::util::TrimWhitespace;
46using ::aapt::xml::Element;
47using ::aapt::xml::FindRootElement;
48using ::aapt::xml::NodeCast;
49using ::aapt::xml::XmlActionExecutor;
50using ::aapt::xml::XmlActionExecutorPolicy;
51using ::aapt::xml::XmlNodeAction;
52
53const std::unordered_map<std::string, Abi> kAbiMap = {
54 {"armeabi", Abi::kArmeV6},
55 {"armeabi-v7a", Abi::kArmV7a},
56 {"arm64-v8a", Abi::kArm64V8a},
57 {"x86", Abi::kX86},
58 {"x86_64", Abi::kX86_64},
59 {"mips", Abi::kMips},
60 {"mips64", Abi::kMips64},
61 {"universal", Abi::kUniversal},
62};
63
64constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
65
66/** A default noop diagnostics context. */
67class NoopDiagnostics : public IDiagnostics {
68 public:
69 void Log(Level level, DiagMessageActual& actualMsg) override {}
70};
71NoopDiagnostics noop_;
72
73std::string GetLabel(const Element* element, IDiagnostics* diag) {
74 std::string label;
75 for (const auto& attr : element->attributes) {
76 if (attr.name == "label") {
77 label = attr.value;
78 break;
79 }
80 }
81
82 if (label.empty()) {
83 diag->Error(DiagMessage() << "No label found for element " << element->name);
84 }
85 return label;
86}
87
88/** XML node visitor that removes all of the namespace URIs from the node and all children. */
89class NamespaceVisitor : public xml::Visitor {
90 public:
91 void Visit(xml::Element* node) override {
92 node->namespace_uri.clear();
93 VisitChildren(node);
94 }
95};
96
97} // namespace
98
99ConfigurationParser::ConfigurationParser(std::string contents)
100 : contents_(std::move(contents)),
101 diag_(&noop_) {
102}
103
104Maybe<Configuration> ConfigurationParser::Parse() {
105 std::istringstream in(contents_);
106
107 auto doc = xml::Inflate(&in, diag_, Source("config.xml"));
108 if (!doc) {
109 return {};
110 }
111
112 // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
113 auto* root = FindRootElement(doc.get());
114 if (root == nullptr) {
115 diag_->Error(DiagMessage() << "Could not find the root element in the XML document");
116 return {};
117 }
118
119 std::string& xml_ns = root->namespace_uri;
120 if (!xml_ns.empty()) {
121 if (xml_ns != kAaptXmlNs) {
122 diag_->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
123 return {};
124 }
125
126 xml_ns.clear();
127 NamespaceVisitor visitor;
128 root->Accept(&visitor);
129 }
130
131 XmlActionExecutor executor;
132 XmlNodeAction& root_action = executor["post-process"];
133 XmlNodeAction& artifacts_action = root_action["artifacts"];
134 XmlNodeAction& groups_action = root_action["groups"];
135
136 Configuration config;
137
138 // Helper to bind a static method to an action handler in the DOM executor.
139 auto bind_handler = [&config](std::function<bool(Configuration*, Element*, IDiagnostics*)> h)
140 -> XmlNodeAction::ActionFuncWithDiag {
141 return std::bind(h, &config, std::placeholders::_1, std::placeholders::_2);
142 };
143
144 // Parse the artifact elements.
145 artifacts_action["artifact"].Action(bind_handler(artifact_handler_));
146 artifacts_action["artifact-format"].Action(bind_handler(artifact_format_handler_));
147
148 // Parse the different configuration groups.
149 groups_action["abi-group"].Action(bind_handler(abi_group_handler_));
150 groups_action["screen-density-group"].Action(bind_handler(screen_density_group_handler_));
151 groups_action["locale-group"].Action(bind_handler(locale_group_handler_));
152 groups_action["android-sdk-group"].Action(bind_handler(android_sdk_group_handler_));
153 groups_action["gl-texture-group"].Action(bind_handler(gl_texture_group_handler_));
154 groups_action["device-feature-group"].Action(bind_handler(device_feature_group_handler_));
155
156 if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag_, doc.get())) {
157 diag_->Error(DiagMessage() << "Could not process XML document");
158 return {};
159 }
160
161 return {config};
162}
163
164ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
165 [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
166 Artifact artifact{};
167 for (const auto& attr : root_element->attributes) {
168 if (attr.name == "name") {
169 artifact.name = attr.value;
170 } else if (attr.name == "abi-group") {
171 artifact.abi_group = {attr.value};
172 } else if (attr.name == "screen-density-group") {
173 artifact.screen_density_group = {attr.value};
174 } else if (attr.name == "locale-group") {
175 artifact.locale_group = {attr.value};
176 } else if (attr.name == "android-sdk-group") {
177 artifact.android_sdk_group = {attr.value};
178 } else if (attr.name == "gl-texture-group") {
179 artifact.gl_texture_group = {attr.value};
180 } else if (attr.name == "device-feature-group") {
181 artifact.device_feature_group = {attr.value};
182 } else {
183 diag->Note(
184 DiagMessage() << "Unknown artifact attribute: " << attr.name << " = " << attr.value);
185 }
186 }
187 config->artifacts[artifact.name] = artifact;
188 return true;
189 };
190
191ConfigurationParser::ActionHandler ConfigurationParser::artifact_format_handler_ =
192 [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
193 for (auto& node : root_element->children) {
194 xml::Text* t;
195 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
196 config->artifact_format = TrimWhitespace(t->text).to_string();
197 break;
198 }
199 }
200 return true;
201 };
202
203ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ =
204 [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
205 std::string label = GetLabel(root_element, diag);
206 if (label.empty()) {
207 return false;
208 }
209
210 auto& group = config->abi_groups[label];
211 bool valid = true;
212
213 for (auto* child : root_element->GetChildElements()) {
214 if (child->name != "abi") {
215 diag->Error(
216 DiagMessage() << "Unexpected element in ABI group: " << child->name);
217 valid = false;
218 } else {
219 for (auto& node : child->children) {
220 xml::Text* t;
221 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
222 group.push_back(kAbiMap.at(TrimWhitespace(t->text).to_string()));
223 break;
224 }
225 }
226 }
227 }
228
229 return valid;
230 };
231
232ConfigurationParser::ActionHandler ConfigurationParser::screen_density_group_handler_ =
233 [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
234 std::string label = GetLabel(root_element, diag);
235 if (label.empty()) {
236 return false;
237 }
238
239 auto& group = config->screen_density_groups[label];
240 bool valid = true;
241
242 for (auto* child : root_element->GetChildElements()) {
243 if (child->name != "screen-density") {
244 diag->Error(
245 DiagMessage() << "Unexpected root_element in screen density group: "
246 << child->name);
247 valid = false;
248 } else {
249 for (auto& node : child->children) {
250 xml::Text* t;
251 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
252 ConfigDescription config_descriptor;
253 const android::StringPiece& text = TrimWhitespace(t->text);
254 if (ConfigDescription::Parse(text, &config_descriptor)) {
255 // Copy the density with the minimum SDK version stripped out.
256 group.push_back(config_descriptor.CopyWithoutSdkVersion());
257 } else {
258 diag->Error(
259 DiagMessage() << "Could not parse config descriptor for screen-density: "
260 << text);
261 valid = false;
262 }
263 break;
264 }
265 }
266 }
267 }
268
269 return valid;
270 };
271
272ConfigurationParser::ActionHandler ConfigurationParser::locale_group_handler_ =
273 [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
274 std::string label = GetLabel(root_element, diag);
275 if (label.empty()) {
276 return false;
277 }
278
279 auto& group = config->locale_groups[label];
280 bool valid = true;
281
282 for (auto* child : root_element->GetChildElements()) {
283 if (child->name != "locale") {
284 diag->Error(
285 DiagMessage() << "Unexpected root_element in screen density group: "
286 << child->name);
287 valid = false;
288 } else {
289 Locale entry;
290 for (const auto& attr : child->attributes) {
291 if (attr.name == "lang") {
292 entry.lang = {attr.value};
293 } else if (attr.name == "region") {
294 entry.region = {attr.value};
295 } else {
296 diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name
297 << " = " << attr.value);
298 }
299 }
300 group.push_back(entry);
301 }
302 }
303
304 return valid;
305 };
306
307ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handler_ =
308 [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
309 std::string label = GetLabel(root_element, diag);
310 if (label.empty()) {
311 return false;
312 }
313
314 auto& group = config->android_sdk_groups[label];
315 bool valid = true;
316
317 for (auto* child : root_element->GetChildElements()) {
318 if (child->name != "android-sdk") {
319 diag->Error(
320 DiagMessage() << "Unexpected root_element in ABI group: " << child->name);
321 valid = false;
322 } else {
323 AndroidSdk entry;
324 for (const auto& attr : child->attributes) {
325 if (attr.name == "minSdkVersion") {
326 entry.min_sdk_version = {attr.value};
327 } else if (attr.name == "targetSdkVersion") {
328 entry.target_sdk_version = {attr.value};
329 } else if (attr.name == "maxSdkVersion") {
330 entry.max_sdk_version = {attr.value};
331 } else {
332 diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name
333 << " = " << attr.value);
334 }
335 }
336
337 // TODO: Fill in the manifest details when they are finalised.
338 for (auto node : child->GetChildElements()) {
339 if (node->name == "manifest") {
340 if (entry.manifest) {
341 diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
342 continue;
343 }
344 entry.manifest = {AndroidManifest()};
345 }
346 }
347
348 group.push_back(entry);
349 }
350 }
351
352 return valid;
353 };
354
355ConfigurationParser::ActionHandler ConfigurationParser::gl_texture_group_handler_ =
356 [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
357 std::string label = GetLabel(root_element, diag);
358 if (label.empty()) {
359 return false;
360 }
361
362 auto& group = config->gl_texture_groups[label];
363 bool valid = true;
364
365 GlTexture result;
366 for (auto* child : root_element->GetChildElements()) {
367 if (child->name != "gl-texture") {
368 diag->Error(
369 DiagMessage() << "Unexpected element in GL texture group: "
370 << child->name);
371 valid = false;
372 } else {
373 for (const auto& attr : child->attributes) {
374 if (attr.name == "name") {
375 result.name = attr.value;
376 break;
377 }
378 }
379
380 for (auto* element : child->GetChildElements()) {
381 if (element->name != "texture-path") {
382 diag->Error(
383 DiagMessage() << "Unexpected element in gl-texture element: "
384 << child->name);
385 valid = false;
386 continue;
387 }
388 for (auto& node : element->children) {
389 xml::Text* t;
390 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
391 result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
392 }
393 }
394 }
395 }
396 group.push_back(result);
397 }
398
399 return valid;
400 };
401
402ConfigurationParser::ActionHandler ConfigurationParser::device_feature_group_handler_ =
403 [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
404 std::string label = GetLabel(root_element, diag);
405 if (label.empty()) {
406 return false;
407 }
408
409 auto& group = config->device_feature_groups[label];
410 bool valid = true;
411
412 for (auto* child : root_element->GetChildElements()) {
413 if (child->name != "supports-feature") {
414 diag->Error(
415 DiagMessage() << "Unexpected root_element in device feature group: "
416 << child->name);
417 valid = false;
418 } else {
419 for (auto& node : child->children) {
420 xml::Text* t;
421 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
422 group.push_back(TrimWhitespace(t->text).to_string());
423 break;
424 }
425 }
426 }
427 }
428
429 return valid;
430 };
431
432} // namespace aapt