blob: dd06b38f6c015546f05402e957d685bb76b3f435 [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>
Shane Farmer57669432017-06-19 12:52:04 -070021#include <map>
Shane Farmer74cdea32017-05-12 16:22:36 -070022#include <memory>
Shane Farmer11cdc1c2018-01-31 16:43:24 -080023#include <string>
Shane Farmer74cdea32017-05-12 16:22:36 -070024#include <utility>
25
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070026#include "android-base/file.h"
27#include "android-base/logging.h"
Mårten Kongstad5c541f62018-06-20 08:46:41 +020028#include "androidfw/ConfigDescription.h"
Shane Farmer74cdea32017-05-12 16:22:36 -070029
Shane Farmer74cdea32017-05-12 16:22:36 -070030#include "Diagnostics.h"
Shane Farmer3edd4722017-09-01 14:34:22 -070031#include "ResourceUtils.h"
Shane Farmercb6c3f92017-11-27 13:19:36 -080032#include "configuration/ConfigurationParser.internal.h"
Shane Farmerb1027272017-06-14 09:10:28 -070033#include "io/File.h"
34#include "io/FileSystem.h"
Adam Lesinski00451162017-10-03 07:44:08 -070035#include "io/StringStream.h"
Shane Farmer9ecc0752017-08-24 15:55:36 -070036#include "util/Files.h"
Shane Farmer9f0e7f12017-06-22 12:26:44 -070037#include "util/Maybe.h"
Shane Farmer74cdea32017-05-12 16:22:36 -070038#include "util/Util.h"
39#include "xml/XmlActionExecutor.h"
40#include "xml/XmlDom.h"
41#include "xml/XmlUtil.h"
42
Mårten Kongstad5c541f62018-06-20 08:46:41 +020043using ::android::ConfigDescription;
44
Shane Farmer74cdea32017-05-12 16:22:36 -070045namespace aapt {
46
47namespace {
48
49using ::aapt::configuration::Abi;
50using ::aapt::configuration::AndroidManifest;
51using ::aapt::configuration::AndroidSdk;
Shane Farmercb6c3f92017-11-27 13:19:36 -080052using ::aapt::configuration::ConfiguredArtifact;
53using ::aapt::configuration::DeviceFeature;
54using ::aapt::configuration::Entry;
Shane Farmer78c43d72017-12-04 09:08:38 -080055using ::aapt::configuration::ExtractConfiguration;
Shane Farmer74cdea32017-05-12 16:22:36 -070056using ::aapt::configuration::GlTexture;
57using ::aapt::configuration::Group;
58using ::aapt::configuration::Locale;
Shane Farmer78c43d72017-12-04 09:08:38 -080059using ::aapt::configuration::OrderedEntry;
Shane Farmercb6c3f92017-11-27 13:19:36 -080060using ::aapt::configuration::OutputArtifact;
61using ::aapt::configuration::PostProcessingConfiguration;
62using ::aapt::configuration::handler::AbiGroupTagHandler;
Shane Farmer78c43d72017-12-04 09:08:38 -080063using ::aapt::configuration::handler::AndroidSdkTagHandler;
Shane Farmercb6c3f92017-11-27 13:19:36 -080064using ::aapt::configuration::handler::ArtifactFormatTagHandler;
65using ::aapt::configuration::handler::ArtifactTagHandler;
66using ::aapt::configuration::handler::DeviceFeatureGroupTagHandler;
67using ::aapt::configuration::handler::GlTextureGroupTagHandler;
68using ::aapt::configuration::handler::LocaleGroupTagHandler;
69using ::aapt::configuration::handler::ScreenDensityGroupTagHandler;
Shane Farmerb1027272017-06-14 09:10:28 -070070using ::aapt::io::IFile;
71using ::aapt::io::RegularFile;
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070072using ::aapt::io::StringInputStream;
Shane Farmer74cdea32017-05-12 16:22:36 -070073using ::aapt::util::TrimWhitespace;
74using ::aapt::xml::Element;
Shane Farmer74cdea32017-05-12 16:22:36 -070075using ::aapt::xml::NodeCast;
76using ::aapt::xml::XmlActionExecutor;
77using ::aapt::xml::XmlActionExecutorPolicy;
78using ::aapt::xml::XmlNodeAction;
Shane Farmer0a5b2012017-06-22 12:24:12 -070079using ::android::StringPiece;
Shane Farmercb6c3f92017-11-27 13:19:36 -080080using ::android::base::ReadFileToString;
Shane Farmer74cdea32017-05-12 16:22:36 -070081
Shane Farmercb6c3f92017-11-27 13:19:36 -080082const std::unordered_map<StringPiece, Abi> kStringToAbiMap = {
Shane Farmer57669432017-06-19 12:52:04 -070083 {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
84 {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips},
85 {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal},
86};
Shane Farmercb6c3f92017-11-27 13:19:36 -080087const std::array<StringPiece, 8> kAbiToStringMap = {
88 {"armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64", "universal"}};
Shane Farmer74cdea32017-05-12 16:22:36 -070089
90constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
91
92/** A default noop diagnostics context. */
93class NoopDiagnostics : public IDiagnostics {
94 public:
95 void Log(Level level, DiagMessageActual& actualMsg) override {}
96};
97NoopDiagnostics noop_;
98
Shane Farmer11cdc1c2018-01-31 16:43:24 -080099/** Returns the value of the label attribute for a given element. */
Shane Farmer74cdea32017-05-12 16:22:36 -0700100std::string GetLabel(const Element* element, IDiagnostics* diag) {
101 std::string label;
102 for (const auto& attr : element->attributes) {
103 if (attr.name == "label") {
104 label = attr.value;
105 break;
106 }
107 }
108
109 if (label.empty()) {
110 diag->Error(DiagMessage() << "No label found for element " << element->name);
111 }
112 return label;
113}
114
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800115/** Returns the value of the version-code-order attribute for a given element. */
116Maybe<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
117 const xml::Attribute* version = element->FindAttribute("", "version-code-order");
118 if (version == nullptr) {
119 std::string label = GetLabel(element, diag);
120 diag->Error(DiagMessage() << "No version-code-order found for element '" << element->name
121 << "' with label '" << label << "'");
122 return {};
123 }
124 return std::stoi(version->value);
125}
126
Shane Farmer74cdea32017-05-12 16:22:36 -0700127/** XML node visitor that removes all of the namespace URIs from the node and all children. */
128class NamespaceVisitor : public xml::Visitor {
129 public:
130 void Visit(xml::Element* node) override {
131 node->namespace_uri.clear();
132 VisitChildren(node);
133 }
134};
135
Shane Farmercb6c3f92017-11-27 13:19:36 -0800136/** Copies the values referenced in a configuration group to the target list. */
137template <typename T>
138bool CopyXmlReferences(const Maybe<std::string>& name, const Group<T>& groups,
139 std::vector<T>* target) {
140 // If there was no item configured, there is nothing to do and no error.
141 if (!name) {
142 return true;
143 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700144
Shane Farmercb6c3f92017-11-27 13:19:36 -0800145 // If the group could not be found, then something is wrong.
146 auto group = groups.find(name.value());
147 if (group == groups.end()) {
148 return false;
149 }
Shane Farmerb1027272017-06-14 09:10:28 -0700150
Shane Farmer78c43d72017-12-04 09:08:38 -0800151 for (const T& item : group->second.entry) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800152 target->push_back(item);
153 }
154 return true;
Shane Farmer57669432017-06-19 12:52:04 -0700155}
156
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700157/**
158 * Attempts to replace the placeholder in the name string with the provided value. Returns true on
159 * success, or false if the either the placeholder is not found in the name, or the value is not
160 * present and the placeholder was.
161 */
Shane Farmercb6c3f92017-11-27 13:19:36 -0800162bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
163 std::string* name, IDiagnostics* diag) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700164 size_t offset = name->find(placeholder.data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700165 bool found = (offset != std::string::npos);
166
167 // Make sure the placeholder was present if the desired value is present.
168 if (!found) {
169 if (value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700170 diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
171 return false;
172 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700173 return true;
174 }
175
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700176 DCHECK(found) << "Missing return path for placeholder not found";
177
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700178 // Make sure the placeholder was not present if the desired value was not present.
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700179 if (!value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700180 diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700181 return false;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700182 }
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700183
Shane Farmer0a5b2012017-06-22 12:24:12 -0700184 name->replace(offset, placeholder.length(), value.value().data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700185
186 // Make sure there was only one instance of the placeholder.
Shane Farmer0a5b2012017-06-22 12:24:12 -0700187 if (name->find(placeholder.data()) != std::string::npos) {
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700188 diag->Error(DiagMessage() << "Placeholder present multiple times: " << placeholder);
189 return false;
190 }
191 return true;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700192}
193
Shane Farmer9ecc0752017-08-24 15:55:36 -0700194/**
Shane Farmercb6c3f92017-11-27 13:19:36 -0800195 * An ActionHandler for processing XML elements in the XmlActionExecutor. Returns true if the
196 * element was successfully processed, otherwise returns false.
197 */
198using ActionHandler = std::function<bool(configuration::PostProcessingConfiguration* config,
199 xml::Element* element, IDiagnostics* diag)>;
200
201/** Binds an ActionHandler to the current configuration being populated. */
202xml::XmlNodeAction::ActionFuncWithDiag Bind(configuration::PostProcessingConfiguration* config,
203 const ActionHandler& handler) {
204 return [config, handler](xml::Element* root_element, SourcePathDiagnostics* diag) {
205 return handler(config, root_element, diag);
206 };
207}
208
Shane Farmercb6c3f92017-11-27 13:19:36 -0800209/** Converts a ConfiguredArtifact into an OutputArtifact. */
210Maybe<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
211 const std::string& apk_name,
212 const PostProcessingConfiguration& config,
213 IDiagnostics* diag) {
214 if (!artifact.name && !config.artifact_format) {
215 diag->Error(
216 DiagMessage() << "Artifact does not have a name and no global name template defined");
217 return {};
218 }
219
220 Maybe<std::string> artifact_name =
221 (artifact.name) ? artifact.Name(apk_name, diag)
222 : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
223
224 if (!artifact_name) {
225 diag->Error(DiagMessage() << "Could not determine split APK artifact name");
226 return {};
227 }
228
229 OutputArtifact output_artifact;
230 output_artifact.name = artifact_name.value();
231
232 SourcePathDiagnostics src_diag{{output_artifact.name}, diag};
233 bool has_errors = false;
234
235 if (!CopyXmlReferences(artifact.abi_group, config.abi_groups, &output_artifact.abis)) {
236 src_diag.Error(DiagMessage() << "Could not lookup required ABIs: "
237 << artifact.abi_group.value());
238 has_errors = true;
239 }
240
241 if (!CopyXmlReferences(artifact.locale_group, config.locale_groups, &output_artifact.locales)) {
242 src_diag.Error(DiagMessage() << "Could not lookup required locales: "
243 << artifact.locale_group.value());
244 has_errors = true;
245 }
246
247 if (!CopyXmlReferences(artifact.screen_density_group, config.screen_density_groups,
248 &output_artifact.screen_densities)) {
249 src_diag.Error(DiagMessage() << "Could not lookup required screen densities: "
250 << artifact.screen_density_group.value());
251 has_errors = true;
252 }
253
254 if (!CopyXmlReferences(artifact.device_feature_group, config.device_feature_groups,
255 &output_artifact.features)) {
256 src_diag.Error(DiagMessage() << "Could not lookup required device features: "
257 << artifact.device_feature_group.value());
258 has_errors = true;
259 }
260
261 if (!CopyXmlReferences(artifact.gl_texture_group, config.gl_texture_groups,
262 &output_artifact.textures)) {
263 src_diag.Error(DiagMessage() << "Could not lookup required OpenGL texture formats: "
264 << artifact.gl_texture_group.value());
265 has_errors = true;
266 }
267
Shane Farmer78c43d72017-12-04 09:08:38 -0800268 if (artifact.android_sdk) {
269 auto entry = config.android_sdks.find(artifact.android_sdk.value());
270 if (entry == config.android_sdks.end()) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800271 src_diag.Error(DiagMessage() << "Could not lookup required Android SDK version: "
Shane Farmer78c43d72017-12-04 09:08:38 -0800272 << artifact.android_sdk.value());
Shane Farmercb6c3f92017-11-27 13:19:36 -0800273 has_errors = true;
274 } else {
275 output_artifact.android_sdk = {entry->second};
276 }
277 }
278
279 if (has_errors) {
280 return {};
281 }
282 return {output_artifact};
283}
284
285} // namespace
286
287namespace configuration {
288
Shane Farmer78c43d72017-12-04 09:08:38 -0800289/** Returns the binary reprasentation of the XML configuration. */
290Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
291 const std::string& config_path,
292 IDiagnostics* diag) {
293 StringInputStream in(contents);
294 std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag, Source(config_path));
295 if (!doc) {
296 return {};
297 }
298
299 // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
300 Element* root = doc->root.get();
301 if (root == nullptr) {
302 diag->Error(DiagMessage() << "Could not find the root element in the XML document");
303 return {};
304 }
305
306 std::string& xml_ns = root->namespace_uri;
307 if (!xml_ns.empty()) {
308 if (xml_ns != kAaptXmlNs) {
309 diag->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
310 return {};
311 }
312
313 xml_ns.clear();
314 NamespaceVisitor visitor;
315 root->Accept(&visitor);
316 }
317
318 XmlActionExecutor executor;
319 XmlNodeAction& root_action = executor["post-process"];
320 XmlNodeAction& artifacts_action = root_action["artifacts"];
321
322 PostProcessingConfiguration config;
323
324 // Parse the artifact elements.
325 artifacts_action["artifact"].Action(Bind(&config, ArtifactTagHandler));
326 artifacts_action["artifact-format"].Action(Bind(&config, ArtifactFormatTagHandler));
327
328 // Parse the different configuration groups.
329 root_action["abi-groups"]["abi-group"].Action(Bind(&config, AbiGroupTagHandler));
330 root_action["screen-density-groups"]["screen-density-group"].Action(
331 Bind(&config, ScreenDensityGroupTagHandler));
332 root_action["locale-groups"]["locale-group"].Action(Bind(&config, LocaleGroupTagHandler));
333 root_action["android-sdks"]["android-sdk"].Action(Bind(&config, AndroidSdkTagHandler));
334 root_action["gl-texture-groups"]["gl-texture-group"].Action(
335 Bind(&config, GlTextureGroupTagHandler));
336 root_action["device-feature-groups"]["device-feature-group"].Action(
337 Bind(&config, DeviceFeatureGroupTagHandler));
338
339 if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag, doc.get())) {
340 diag->Error(DiagMessage() << "Could not process XML document");
341 return {};
342 }
343
344 return {config};
345}
346
Shane Farmercb6c3f92017-11-27 13:19:36 -0800347const StringPiece& AbiToString(Abi abi) {
348 return kAbiToStringMap.at(static_cast<size_t>(abi));
349}
350
351/**
Shane Farmer9ecc0752017-08-24 15:55:36 -0700352 * Returns the common artifact base name from a template string.
353 */
354Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
355 const StringPiece ext = file::GetExtension(apk_name);
356 size_t end_index = apk_name.to_string().rfind(ext.to_string());
357 const std::string base_name =
358 (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : "";
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700359
Shane Farmer9ecc0752017-08-24 15:55:36 -0700360 // Base name is optional.
361 if (result.find("${basename}") != std::string::npos) {
362 Maybe<StringPiece> maybe_base_name =
363 base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
364 if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
365 return {};
366 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700367 }
368
Shane Farmer0a5b2012017-06-22 12:24:12 -0700369 // Extension is optional.
370 if (result.find("${ext}") != std::string::npos) {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700371 // Make sure we disregard the '.' in the extension when replacing the placeholder.
372 if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700373 return {};
374 }
Shane Farmer9ecc0752017-08-24 15:55:36 -0700375 } else {
376 // If no extension is specified, and the name template does not end in the current extension,
377 // add the existing extension.
378 if (!util::EndsWith(result, ext)) {
379 result.append(ext.to_string());
380 }
Shane Farmer0a5b2012017-06-22 12:24:12 -0700381 }
382
Shane Farmer9ecc0752017-08-24 15:55:36 -0700383 return result;
384}
385
Shane Farmercb6c3f92017-11-27 13:19:36 -0800386Maybe<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
387 const StringPiece& apk_name,
388 IDiagnostics* diag) const {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700389 Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
390 if (!base) {
391 return {};
392 }
393 std::string result = std::move(base.value());
394
Shane Farmer0a5b2012017-06-22 12:24:12 -0700395 if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700396 return {};
397 }
398
Shane Farmer0a5b2012017-06-22 12:24:12 -0700399 if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700400 return {};
401 }
402
Shane Farmer0a5b2012017-06-22 12:24:12 -0700403 if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700404 return {};
405 }
406
Shane Farmer78c43d72017-12-04 09:08:38 -0800407 if (!ReplacePlaceholder("${sdk}", android_sdk, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700408 return {};
409 }
410
Shane Farmer0a5b2012017-06-22 12:24:12 -0700411 if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700412 return {};
413 }
414
Shane Farmer0a5b2012017-06-22 12:24:12 -0700415 if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) {
416 return {};
417 }
418
419 return result;
420}
421
Shane Farmercb6c3f92017-11-27 13:19:36 -0800422Maybe<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700423 if (!name) {
424 return {};
425 }
426
Shane Farmer9ecc0752017-08-24 15:55:36 -0700427 return ToBaseName(name.value(), apk_name, diag);
428}
Shane Farmer0a5b2012017-06-22 12:24:12 -0700429
Shane Farmer57669432017-06-19 12:52:04 -0700430} // namespace configuration
Shane Farmerb1027272017-06-14 09:10:28 -0700431
432/** Returns a ConfigurationParser for the file located at the provided path. */
433Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
434 std::string contents;
435 if (!ReadFileToString(path, &contents, true)) {
436 return {};
437 }
Shane Farmer78c43d72017-12-04 09:08:38 -0800438 return ConfigurationParser(contents, path);
Shane Farmerb1027272017-06-14 09:10:28 -0700439}
440
Shane Farmer78c43d72017-12-04 09:08:38 -0800441ConfigurationParser::ConfigurationParser(std::string contents, const std::string& config_path)
442 : contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700443}
444
Shane Farmercb6c3f92017-11-27 13:19:36 -0800445Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
446 const android::StringPiece& apk_path) {
Shane Farmer78c43d72017-12-04 09:08:38 -0800447 Maybe<PostProcessingConfiguration> maybe_config =
448 ExtractConfiguration(contents_, config_path_, diag_);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800449 if (!maybe_config) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700450 return {};
451 }
Shane Farmercb6c3f92017-11-27 13:19:36 -0800452
453 // Convert from a parsed configuration to a list of artifacts for processing.
Shane Farmer78c43d72017-12-04 09:08:38 -0800454 const std::string& apk_name = file::GetFilename(apk_path).to_string();
Shane Farmercb6c3f92017-11-27 13:19:36 -0800455 std::vector<OutputArtifact> output_artifacts;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800456
Shane Farmer78c43d72017-12-04 09:08:38 -0800457 PostProcessingConfiguration& config = maybe_config.value();
Shane Farmer78c43d72017-12-04 09:08:38 -0800458
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800459 bool valid = true;
Shane Farmer78c43d72017-12-04 09:08:38 -0800460 int version = 1;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800461
Shane Farmer78c43d72017-12-04 09:08:38 -0800462 for (const ConfiguredArtifact& artifact : config.artifacts) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800463 Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_);
464 if (!output_artifact) {
465 // Defer return an error condition so that all errors are reported.
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800466 valid = false;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800467 } else {
Shane Farmer78c43d72017-12-04 09:08:38 -0800468 output_artifact.value().version = version++;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800469 output_artifacts.push_back(std::move(output_artifact.value()));
470 }
471 }
472
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800473 if (!config.ValidateVersionCodeOrdering(diag_)) {
474 diag_->Error(DiagMessage() << "could not validate post processing configuration");
475 valid = false;
476 }
477
478 if (valid) {
479 // Sorting artifacts requires that all references are valid as it uses them to determine order.
480 config.SortArtifacts();
481 }
482
483 if (!valid) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800484 return {};
485 }
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800486
Shane Farmercb6c3f92017-11-27 13:19:36 -0800487 return {output_artifacts};
Shane Farmer74cdea32017-05-12 16:22:36 -0700488}
489
Shane Farmercb6c3f92017-11-27 13:19:36 -0800490namespace configuration {
491namespace handler {
492
493bool ArtifactTagHandler(PostProcessingConfiguration* config, Element* root_element,
494 IDiagnostics* diag) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800495 ConfiguredArtifact artifact{};
Shane Farmer280be342017-06-21 15:20:15 -0700496 for (const auto& attr : root_element->attributes) {
497 if (attr.name == "name") {
498 artifact.name = attr.value;
499 } else if (attr.name == "abi-group") {
500 artifact.abi_group = {attr.value};
501 } else if (attr.name == "screen-density-group") {
502 artifact.screen_density_group = {attr.value};
503 } else if (attr.name == "locale-group") {
504 artifact.locale_group = {attr.value};
Shane Farmer78c43d72017-12-04 09:08:38 -0800505 } else if (attr.name == "android-sdk") {
506 artifact.android_sdk = {attr.value};
Shane Farmer280be342017-06-21 15:20:15 -0700507 } else if (attr.name == "gl-texture-group") {
508 artifact.gl_texture_group = {attr.value};
509 } else if (attr.name == "device-feature-group") {
510 artifact.device_feature_group = {attr.value};
511 } else {
512 diag->Note(DiagMessage() << "Unknown artifact attribute: " << attr.name << " = "
513 << attr.value);
514 }
515 }
516 config->artifacts.push_back(artifact);
517 return true;
518};
Shane Farmer74cdea32017-05-12 16:22:36 -0700519
Shane Farmercb6c3f92017-11-27 13:19:36 -0800520bool ArtifactFormatTagHandler(PostProcessingConfiguration* config, Element* root_element,
521 IDiagnostics* /* diag */) {
Shane Farmer280be342017-06-21 15:20:15 -0700522 for (auto& node : root_element->children) {
523 xml::Text* t;
524 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
525 config->artifact_format = TrimWhitespace(t->text).to_string();
526 break;
527 }
528 }
529 return true;
530};
531
Shane Farmercb6c3f92017-11-27 13:19:36 -0800532bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
533 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700534 std::string label = GetLabel(root_element, diag);
535 if (label.empty()) {
536 return false;
537 }
538
Shane Farmer280be342017-06-21 15:20:15 -0700539 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800540 OrderedEntry<Abi>& entry = config->abi_groups[label];
541 Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
542 if (!order) {
543 valid = false;
544 } else {
545 entry.order = order.value();
546 }
547 auto& group = entry.entry;
Shane Farmer280be342017-06-21 15:20:15 -0700548
Shane Farmer39e474f2017-12-18 14:44:11 -0800549 // Special case for empty abi-group tag. Label will be used as the ABI.
550 if (root_element->GetChildElements().empty()) {
551 auto abi = kStringToAbiMap.find(label);
552 if (abi == kStringToAbiMap.end()) {
553 return false;
554 }
555 group.push_back(abi->second);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800556 return valid;
Shane Farmer39e474f2017-12-18 14:44:11 -0800557 }
558
Shane Farmer280be342017-06-21 15:20:15 -0700559 for (auto* child : root_element->GetChildElements()) {
560 if (child->name != "abi") {
561 diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
562 valid = false;
563 } else {
564 for (auto& node : child->children) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700565 xml::Text* t;
566 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Shane Farmer39e474f2017-12-18 14:44:11 -0800567 auto abi = kStringToAbiMap.find(TrimWhitespace(t->text).to_string());
568 if (abi != kStringToAbiMap.end()) {
569 group.push_back(abi->second);
570 } else {
571 diag->Error(DiagMessage() << "Could not parse ABI value: " << t->text);
572 valid = false;
573 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700574 break;
575 }
576 }
Shane Farmer280be342017-06-21 15:20:15 -0700577 }
578 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700579
Shane Farmer280be342017-06-21 15:20:15 -0700580 return valid;
581};
Shane Farmer74cdea32017-05-12 16:22:36 -0700582
Shane Farmercb6c3f92017-11-27 13:19:36 -0800583bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
584 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700585 std::string label = GetLabel(root_element, diag);
586 if (label.empty()) {
587 return false;
588 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700589
Shane Farmer280be342017-06-21 15:20:15 -0700590 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800591 OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
592 Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
593 if (!order) {
594 valid = false;
595 } else {
596 entry.order = order.value();
597 }
598 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700599
Shane Farmer39e474f2017-12-18 14:44:11 -0800600 // Special case for empty screen-density-group tag. Label will be used as the screen density.
601 if (root_element->GetChildElements().empty()) {
602 ConfigDescription config_descriptor;
603 bool parsed = ConfigDescription::Parse(label, &config_descriptor);
604 if (parsed &&
605 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
606 android::ResTable_config::CONFIG_DENSITY)) {
607 // Copy the density with the minimum SDK version stripped out.
608 group.push_back(config_descriptor.CopyWithoutSdkVersion());
609 } else {
610 diag->Error(DiagMessage()
611 << "Could not parse config descriptor for empty screen-density-group: "
612 << label);
613 valid = false;
614 }
615
616 return valid;
617 }
618
Shane Farmer280be342017-06-21 15:20:15 -0700619 for (auto* child : root_element->GetChildElements()) {
620 if (child->name != "screen-density") {
621 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
622 << child->name);
623 valid = false;
624 } else {
625 for (auto& node : child->children) {
626 xml::Text* t;
627 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
628 ConfigDescription config_descriptor;
629 const android::StringPiece& text = TrimWhitespace(t->text);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700630 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
631 if (parsed &&
632 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
633 android::ResTable_config::CONFIG_DENSITY)) {
Shane Farmer280be342017-06-21 15:20:15 -0700634 // Copy the density with the minimum SDK version stripped out.
635 group.push_back(config_descriptor.CopyWithoutSdkVersion());
636 } else {
637 diag->Error(DiagMessage()
638 << "Could not parse config descriptor for screen-density: " << text);
639 valid = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700640 }
Shane Farmer280be342017-06-21 15:20:15 -0700641 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700642 }
643 }
Shane Farmer280be342017-06-21 15:20:15 -0700644 }
645 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700646
Shane Farmer280be342017-06-21 15:20:15 -0700647 return valid;
648};
Shane Farmer74cdea32017-05-12 16:22:36 -0700649
Shane Farmercb6c3f92017-11-27 13:19:36 -0800650bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
651 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700652 std::string label = GetLabel(root_element, diag);
653 if (label.empty()) {
654 return false;
655 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700656
Shane Farmer280be342017-06-21 15:20:15 -0700657 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800658 OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
659 Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
660 if (!order) {
661 valid = false;
662 } else {
663 entry.order = order.value();
664 }
665 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700666
Shane Farmer39e474f2017-12-18 14:44:11 -0800667 // Special case to auto insert a locale for an empty group. Label will be used for locale.
668 if (root_element->GetChildElements().empty()) {
669 ConfigDescription config_descriptor;
670 bool parsed = ConfigDescription::Parse(label, &config_descriptor);
671 if (parsed &&
672 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
673 android::ResTable_config::CONFIG_LOCALE)) {
674 // Copy the locale with the minimum SDK version stripped out.
675 group.push_back(config_descriptor.CopyWithoutSdkVersion());
676 } else {
677 diag->Error(DiagMessage()
678 << "Could not parse config descriptor for empty screen-density-group: "
679 << label);
680 valid = false;
681 }
682
683 return valid;
684 }
685
Shane Farmer280be342017-06-21 15:20:15 -0700686 for (auto* child : root_element->GetChildElements()) {
687 if (child->name != "locale") {
688 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
689 << child->name);
690 valid = false;
691 } else {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700692 for (auto& node : child->children) {
693 xml::Text* t;
694 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
695 ConfigDescription config_descriptor;
696 const android::StringPiece& text = TrimWhitespace(t->text);
697 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
698 if (parsed &&
699 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
700 android::ResTable_config::CONFIG_LOCALE)) {
701 // Copy the locale with the minimum SDK version stripped out.
702 group.push_back(config_descriptor.CopyWithoutSdkVersion());
703 } else {
704 diag->Error(DiagMessage()
705 << "Could not parse config descriptor for screen-density: " << text);
706 valid = false;
707 }
708 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700709 }
710 }
Shane Farmer280be342017-06-21 15:20:15 -0700711 }
712 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700713
Shane Farmer280be342017-06-21 15:20:15 -0700714 return valid;
715};
Shane Farmer74cdea32017-05-12 16:22:36 -0700716
Shane Farmer78c43d72017-12-04 09:08:38 -0800717bool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_element,
718 IDiagnostics* diag) {
719 AndroidSdk entry = AndroidSdk::ForMinSdk(-1);
Shane Farmer280be342017-06-21 15:20:15 -0700720 bool valid = true;
Shane Farmer78c43d72017-12-04 09:08:38 -0800721 for (const auto& attr : root_element->attributes) {
722 bool valid_attr = false;
723 if (attr.name == "label") {
724 entry.label = attr.value;
725 valid_attr = true;
726 } else if (attr.name == "minSdkVersion") {
727 Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
728 if (version) {
729 valid_attr = true;
730 entry.min_sdk_version = version.value();
731 }
732 } else if (attr.name == "targetSdkVersion") {
733 Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
734 if (version) {
735 valid_attr = true;
736 entry.target_sdk_version = version;
737 }
738 } else if (attr.name == "maxSdkVersion") {
739 Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
740 if (version) {
741 valid_attr = true;
742 entry.max_sdk_version = version;
743 }
744 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700745
Shane Farmer78c43d72017-12-04 09:08:38 -0800746 if (!valid_attr) {
747 diag->Error(DiagMessage() << "Invalid attribute: " << attr.name << " = " << attr.value);
Shane Farmer280be342017-06-21 15:20:15 -0700748 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700749 }
750 }
751
Shane Farmer78c43d72017-12-04 09:08:38 -0800752 if (entry.min_sdk_version == -1) {
753 diag->Error(DiagMessage() << "android-sdk is missing minSdkVersion attribute");
754 valid = false;
755 }
756
757 // TODO: Fill in the manifest details when they are finalised.
758 for (auto node : root_element->GetChildElements()) {
759 if (node->name == "manifest") {
760 if (entry.manifest) {
761 diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
762 continue;
763 }
764 entry.manifest = {AndroidManifest()};
765 }
766 }
767
768 config->android_sdks[entry.label] = entry;
Shane Farmer280be342017-06-21 15:20:15 -0700769 return valid;
770};
Shane Farmer74cdea32017-05-12 16:22:36 -0700771
Shane Farmercb6c3f92017-11-27 13:19:36 -0800772bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
773 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700774 std::string label = GetLabel(root_element, diag);
775 if (label.empty()) {
776 return false;
777 }
778
Shane Farmer280be342017-06-21 15:20:15 -0700779 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800780 OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
781 Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
782 if (!order) {
783 valid = false;
784 } else {
785 entry.order = order.value();
786 }
787 auto& group = entry.entry;
Shane Farmer280be342017-06-21 15:20:15 -0700788
789 GlTexture result;
790 for (auto* child : root_element->GetChildElements()) {
791 if (child->name != "gl-texture") {
792 diag->Error(DiagMessage() << "Unexpected element in GL texture group: " << child->name);
793 valid = false;
794 } else {
795 for (const auto& attr : child->attributes) {
796 if (attr.name == "name") {
797 result.name = attr.value;
798 break;
799 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700800 }
801
Shane Farmer280be342017-06-21 15:20:15 -0700802 for (auto* element : child->GetChildElements()) {
803 if (element->name != "texture-path") {
804 diag->Error(DiagMessage() << "Unexpected element in gl-texture element: " << child->name);
Shane Farmer74cdea32017-05-12 16:22:36 -0700805 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700806 continue;
807 }
808 for (auto& node : element->children) {
809 xml::Text* t;
810 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
811 result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
Shane Farmer74cdea32017-05-12 16:22:36 -0700812 }
813 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700814 }
Shane Farmer280be342017-06-21 15:20:15 -0700815 }
816 group.push_back(result);
817 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700818
Shane Farmer280be342017-06-21 15:20:15 -0700819 return valid;
820};
Shane Farmer74cdea32017-05-12 16:22:36 -0700821
Shane Farmercb6c3f92017-11-27 13:19:36 -0800822bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
823 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700824 std::string label = GetLabel(root_element, diag);
825 if (label.empty()) {
826 return false;
827 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700828
Shane Farmer280be342017-06-21 15:20:15 -0700829 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800830 OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
831 Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
832 if (!order) {
833 valid = false;
834 } else {
835 entry.order = order.value();
836 }
837 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700838
Shane Farmer280be342017-06-21 15:20:15 -0700839 for (auto* child : root_element->GetChildElements()) {
840 if (child->name != "supports-feature") {
841 diag->Error(DiagMessage() << "Unexpected root_element in device feature group: "
842 << child->name);
843 valid = false;
844 } else {
845 for (auto& node : child->children) {
846 xml::Text* t;
847 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
848 group.push_back(TrimWhitespace(t->text).to_string());
849 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700850 }
851 }
Shane Farmer280be342017-06-21 15:20:15 -0700852 }
853 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700854
Shane Farmer280be342017-06-21 15:20:15 -0700855 return valid;
856};
Shane Farmer74cdea32017-05-12 16:22:36 -0700857
Shane Farmercb6c3f92017-11-27 13:19:36 -0800858} // namespace handler
859} // namespace configuration
860
Shane Farmer74cdea32017-05-12 16:22:36 -0700861} // namespace aapt