blob: e7a45851e2396d4897802436c227c63e45f274ce [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 Kongstad24c9aa62018-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 Farmer74cdea32017-05-12 16:22:36 -070037#include "util/Util.h"
38#include "xml/XmlActionExecutor.h"
39#include "xml/XmlDom.h"
40#include "xml/XmlUtil.h"
41
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020042using ::android::ConfigDescription;
43
Shane Farmer74cdea32017-05-12 16:22:36 -070044namespace aapt {
45
46namespace {
47
48using ::aapt::configuration::Abi;
49using ::aapt::configuration::AndroidManifest;
50using ::aapt::configuration::AndroidSdk;
Shane Farmercb6c3f92017-11-27 13:19:36 -080051using ::aapt::configuration::ConfiguredArtifact;
52using ::aapt::configuration::DeviceFeature;
53using ::aapt::configuration::Entry;
Shane Farmer78c43d72017-12-04 09:08:38 -080054using ::aapt::configuration::ExtractConfiguration;
Shane Farmer74cdea32017-05-12 16:22:36 -070055using ::aapt::configuration::GlTexture;
56using ::aapt::configuration::Group;
57using ::aapt::configuration::Locale;
Shane Farmer78c43d72017-12-04 09:08:38 -080058using ::aapt::configuration::OrderedEntry;
Shane Farmercb6c3f92017-11-27 13:19:36 -080059using ::aapt::configuration::OutputArtifact;
60using ::aapt::configuration::PostProcessingConfiguration;
61using ::aapt::configuration::handler::AbiGroupTagHandler;
Shane Farmer78c43d72017-12-04 09:08:38 -080062using ::aapt::configuration::handler::AndroidSdkTagHandler;
Shane Farmercb6c3f92017-11-27 13:19:36 -080063using ::aapt::configuration::handler::ArtifactFormatTagHandler;
64using ::aapt::configuration::handler::ArtifactTagHandler;
65using ::aapt::configuration::handler::DeviceFeatureGroupTagHandler;
66using ::aapt::configuration::handler::GlTextureGroupTagHandler;
67using ::aapt::configuration::handler::LocaleGroupTagHandler;
68using ::aapt::configuration::handler::ScreenDensityGroupTagHandler;
Shane Farmerb1027272017-06-14 09:10:28 -070069using ::aapt::io::IFile;
70using ::aapt::io::RegularFile;
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070071using ::aapt::io::StringInputStream;
Shane Farmer74cdea32017-05-12 16:22:36 -070072using ::aapt::util::TrimWhitespace;
73using ::aapt::xml::Element;
Shane Farmer74cdea32017-05-12 16:22:36 -070074using ::aapt::xml::NodeCast;
75using ::aapt::xml::XmlActionExecutor;
76using ::aapt::xml::XmlActionExecutorPolicy;
77using ::aapt::xml::XmlNodeAction;
Shane Farmer0a5b2012017-06-22 12:24:12 -070078using ::android::StringPiece;
Shane Farmercb6c3f92017-11-27 13:19:36 -080079using ::android::base::ReadFileToString;
Shane Farmer74cdea32017-05-12 16:22:36 -070080
Shane Farmercb6c3f92017-11-27 13:19:36 -080081const std::unordered_map<StringPiece, Abi> kStringToAbiMap = {
Shane Farmer57669432017-06-19 12:52:04 -070082 {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
83 {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips},
84 {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal},
85};
Shane Farmercb6c3f92017-11-27 13:19:36 -080086const std::array<StringPiece, 8> kAbiToStringMap = {
87 {"armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64", "universal"}};
Shane Farmer74cdea32017-05-12 16:22:36 -070088
89constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
90
91/** A default noop diagnostics context. */
92class NoopDiagnostics : public IDiagnostics {
93 public:
94 void Log(Level level, DiagMessageActual& actualMsg) override {}
95};
96NoopDiagnostics noop_;
97
Shane Farmer11cdc1c2018-01-31 16:43:24 -080098/** Returns the value of the label attribute for a given element. */
Shane Farmer74cdea32017-05-12 16:22:36 -070099std::string GetLabel(const Element* element, IDiagnostics* diag) {
100 std::string label;
101 for (const auto& attr : element->attributes) {
102 if (attr.name == "label") {
103 label = attr.value;
104 break;
105 }
106 }
107
108 if (label.empty()) {
109 diag->Error(DiagMessage() << "No label found for element " << element->name);
110 }
111 return label;
112}
113
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800114/** Returns the value of the version-code-order attribute for a given element. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700115std::optional<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800116 const xml::Attribute* version = element->FindAttribute("", "version-code-order");
117 if (version == nullptr) {
118 std::string label = GetLabel(element, diag);
119 diag->Error(DiagMessage() << "No version-code-order found for element '" << element->name
120 << "' with label '" << label << "'");
121 return {};
122 }
123 return std::stoi(version->value);
124}
125
Shane Farmer74cdea32017-05-12 16:22:36 -0700126/** XML node visitor that removes all of the namespace URIs from the node and all children. */
127class NamespaceVisitor : public xml::Visitor {
128 public:
129 void Visit(xml::Element* node) override {
130 node->namespace_uri.clear();
131 VisitChildren(node);
132 }
133};
134
Shane Farmercb6c3f92017-11-27 13:19:36 -0800135/** Copies the values referenced in a configuration group to the target list. */
136template <typename T>
Ryan Mitchell4382e442021-07-14 12:53:01 -0700137bool CopyXmlReferences(const std::optional<std::string>& name, const Group<T>& groups,
Shane Farmercb6c3f92017-11-27 13:19:36 -0800138 std::vector<T>* target) {
139 // If there was no item configured, there is nothing to do and no error.
140 if (!name) {
141 return true;
142 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700143
Shane Farmercb6c3f92017-11-27 13:19:36 -0800144 // If the group could not be found, then something is wrong.
145 auto group = groups.find(name.value());
146 if (group == groups.end()) {
147 return false;
148 }
Shane Farmerb1027272017-06-14 09:10:28 -0700149
Shane Farmer78c43d72017-12-04 09:08:38 -0800150 for (const T& item : group->second.entry) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800151 target->push_back(item);
152 }
153 return true;
Shane Farmer57669432017-06-19 12:52:04 -0700154}
155
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700156/**
157 * Attempts to replace the placeholder in the name string with the provided value. Returns true on
158 * success, or false if the either the placeholder is not found in the name, or the value is not
159 * present and the placeholder was.
160 */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700161bool ReplacePlaceholder(const StringPiece& placeholder, const std::optional<StringPiece>& value,
Shane Farmercb6c3f92017-11-27 13:19:36 -0800162 std::string* name, IDiagnostics* diag) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700163 size_t offset = name->find(placeholder.data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700164 bool found = (offset != std::string::npos);
165
166 // Make sure the placeholder was present if the desired value is present.
167 if (!found) {
168 if (value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700169 diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
170 return false;
171 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700172 return true;
173 }
174
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700175 DCHECK(found) << "Missing return path for placeholder not found";
176
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700177 // Make sure the placeholder was not present if the desired value was not present.
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700178 if (!value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700179 diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700180 return false;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700181 }
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700182
Shane Farmer0a5b2012017-06-22 12:24:12 -0700183 name->replace(offset, placeholder.length(), value.value().data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700184
185 // Make sure there was only one instance of the placeholder.
Shane Farmer0a5b2012017-06-22 12:24:12 -0700186 if (name->find(placeholder.data()) != std::string::npos) {
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700187 diag->Error(DiagMessage() << "Placeholder present multiple times: " << placeholder);
188 return false;
189 }
190 return true;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700191}
192
Shane Farmer9ecc0752017-08-24 15:55:36 -0700193/**
Shane Farmercb6c3f92017-11-27 13:19:36 -0800194 * An ActionHandler for processing XML elements in the XmlActionExecutor. Returns true if the
195 * element was successfully processed, otherwise returns false.
196 */
197using ActionHandler = std::function<bool(configuration::PostProcessingConfiguration* config,
198 xml::Element* element, IDiagnostics* diag)>;
199
200/** Binds an ActionHandler to the current configuration being populated. */
201xml::XmlNodeAction::ActionFuncWithDiag Bind(configuration::PostProcessingConfiguration* config,
202 const ActionHandler& handler) {
203 return [config, handler](xml::Element* root_element, SourcePathDiagnostics* diag) {
204 return handler(config, root_element, diag);
205 };
206}
207
Shane Farmercb6c3f92017-11-27 13:19:36 -0800208/** Converts a ConfiguredArtifact into an OutputArtifact. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700209std::optional<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
210 const std::string& apk_name,
211 const PostProcessingConfiguration& config,
212 IDiagnostics* diag) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800213 if (!artifact.name && !config.artifact_format) {
214 diag->Error(
215 DiagMessage() << "Artifact does not have a name and no global name template defined");
216 return {};
217 }
218
Ryan Mitchell4382e442021-07-14 12:53:01 -0700219 std::optional<std::string> artifact_name =
Shane Farmercb6c3f92017-11-27 13:19:36 -0800220 (artifact.name) ? artifact.Name(apk_name, diag)
221 : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
222
223 if (!artifact_name) {
224 diag->Error(DiagMessage() << "Could not determine split APK artifact name");
225 return {};
226 }
227
228 OutputArtifact output_artifact;
229 output_artifact.name = artifact_name.value();
230
231 SourcePathDiagnostics src_diag{{output_artifact.name}, diag};
232 bool has_errors = false;
233
234 if (!CopyXmlReferences(artifact.abi_group, config.abi_groups, &output_artifact.abis)) {
235 src_diag.Error(DiagMessage() << "Could not lookup required ABIs: "
236 << artifact.abi_group.value());
237 has_errors = true;
238 }
239
240 if (!CopyXmlReferences(artifact.locale_group, config.locale_groups, &output_artifact.locales)) {
241 src_diag.Error(DiagMessage() << "Could not lookup required locales: "
242 << artifact.locale_group.value());
243 has_errors = true;
244 }
245
246 if (!CopyXmlReferences(artifact.screen_density_group, config.screen_density_groups,
247 &output_artifact.screen_densities)) {
248 src_diag.Error(DiagMessage() << "Could not lookup required screen densities: "
249 << artifact.screen_density_group.value());
250 has_errors = true;
251 }
252
253 if (!CopyXmlReferences(artifact.device_feature_group, config.device_feature_groups,
254 &output_artifact.features)) {
255 src_diag.Error(DiagMessage() << "Could not lookup required device features: "
256 << artifact.device_feature_group.value());
257 has_errors = true;
258 }
259
260 if (!CopyXmlReferences(artifact.gl_texture_group, config.gl_texture_groups,
261 &output_artifact.textures)) {
262 src_diag.Error(DiagMessage() << "Could not lookup required OpenGL texture formats: "
263 << artifact.gl_texture_group.value());
264 has_errors = true;
265 }
266
Shane Farmer78c43d72017-12-04 09:08:38 -0800267 if (artifact.android_sdk) {
268 auto entry = config.android_sdks.find(artifact.android_sdk.value());
269 if (entry == config.android_sdks.end()) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800270 src_diag.Error(DiagMessage() << "Could not lookup required Android SDK version: "
Shane Farmer78c43d72017-12-04 09:08:38 -0800271 << artifact.android_sdk.value());
Shane Farmercb6c3f92017-11-27 13:19:36 -0800272 has_errors = true;
273 } else {
274 output_artifact.android_sdk = {entry->second};
275 }
276 }
277
278 if (has_errors) {
279 return {};
280 }
281 return {output_artifact};
282}
283
284} // namespace
285
286namespace configuration {
287
Shane Farmer78c43d72017-12-04 09:08:38 -0800288/** Returns the binary reprasentation of the XML configuration. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700289std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
290 const std::string& config_path,
291 IDiagnostics* diag) {
Shane Farmer78c43d72017-12-04 09:08:38 -0800292 StringInputStream in(contents);
293 std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag, Source(config_path));
294 if (!doc) {
295 return {};
296 }
297
298 // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
299 Element* root = doc->root.get();
300 if (root == nullptr) {
301 diag->Error(DiagMessage() << "Could not find the root element in the XML document");
302 return {};
303 }
304
305 std::string& xml_ns = root->namespace_uri;
306 if (!xml_ns.empty()) {
307 if (xml_ns != kAaptXmlNs) {
308 diag->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
309 return {};
310 }
311
312 xml_ns.clear();
313 NamespaceVisitor visitor;
314 root->Accept(&visitor);
315 }
316
317 XmlActionExecutor executor;
318 XmlNodeAction& root_action = executor["post-process"];
319 XmlNodeAction& artifacts_action = root_action["artifacts"];
320
321 PostProcessingConfiguration config;
322
323 // Parse the artifact elements.
324 artifacts_action["artifact"].Action(Bind(&config, ArtifactTagHandler));
325 artifacts_action["artifact-format"].Action(Bind(&config, ArtifactFormatTagHandler));
326
327 // Parse the different configuration groups.
328 root_action["abi-groups"]["abi-group"].Action(Bind(&config, AbiGroupTagHandler));
329 root_action["screen-density-groups"]["screen-density-group"].Action(
330 Bind(&config, ScreenDensityGroupTagHandler));
331 root_action["locale-groups"]["locale-group"].Action(Bind(&config, LocaleGroupTagHandler));
332 root_action["android-sdks"]["android-sdk"].Action(Bind(&config, AndroidSdkTagHandler));
333 root_action["gl-texture-groups"]["gl-texture-group"].Action(
334 Bind(&config, GlTextureGroupTagHandler));
335 root_action["device-feature-groups"]["device-feature-group"].Action(
336 Bind(&config, DeviceFeatureGroupTagHandler));
337
338 if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag, doc.get())) {
339 diag->Error(DiagMessage() << "Could not process XML document");
340 return {};
341 }
342
343 return {config};
344}
345
Shane Farmercb6c3f92017-11-27 13:19:36 -0800346const StringPiece& AbiToString(Abi abi) {
347 return kAbiToStringMap.at(static_cast<size_t>(abi));
348}
349
350/**
Shane Farmer9ecc0752017-08-24 15:55:36 -0700351 * Returns the common artifact base name from a template string.
352 */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700353std::optional<std::string> ToBaseName(std::string result, const StringPiece& apk_name,
354 IDiagnostics* diag) {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700355 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) {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700362 auto maybe_base_name = base_name.empty() ? std::nullopt
363 : std::optional<StringPiece>{base_name};
Shane Farmer9ecc0752017-08-24 15:55:36 -0700364 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
Ryan Mitchell4382e442021-07-14 12:53:01 -0700386std::optional<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
387 const StringPiece& apk_name,
388 IDiagnostics* diag) const {
389 std::optional<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
Shane Farmer9ecc0752017-08-24 15:55:36 -0700390 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
Ryan Mitchell4382e442021-07-14 12:53:01 -0700422std::optional<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name,
423 IDiagnostics* diag) const {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700424 if (!name) {
425 return {};
426 }
427
Shane Farmer9ecc0752017-08-24 15:55:36 -0700428 return ToBaseName(name.value(), apk_name, diag);
429}
Shane Farmer0a5b2012017-06-22 12:24:12 -0700430
Shane Farmer57669432017-06-19 12:52:04 -0700431} // namespace configuration
Shane Farmerb1027272017-06-14 09:10:28 -0700432
433/** Returns a ConfigurationParser for the file located at the provided path. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700434std::optional<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
Shane Farmerb1027272017-06-14 09:10:28 -0700435 std::string contents;
436 if (!ReadFileToString(path, &contents, true)) {
437 return {};
438 }
Shane Farmer78c43d72017-12-04 09:08:38 -0800439 return ConfigurationParser(contents, path);
Shane Farmerb1027272017-06-14 09:10:28 -0700440}
441
Shane Farmer78c43d72017-12-04 09:08:38 -0800442ConfigurationParser::ConfigurationParser(std::string contents, const std::string& config_path)
443 : contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700444}
445
Ryan Mitchell4382e442021-07-14 12:53:01 -0700446std::optional<std::vector<OutputArtifact>> ConfigurationParser::Parse(
Shane Farmercb6c3f92017-11-27 13:19:36 -0800447 const android::StringPiece& apk_path) {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700448 std::optional<PostProcessingConfiguration> maybe_config =
Shane Farmer78c43d72017-12-04 09:08:38 -0800449 ExtractConfiguration(contents_, config_path_, diag_);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800450 if (!maybe_config) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700451 return {};
452 }
Shane Farmercb6c3f92017-11-27 13:19:36 -0800453
454 // Convert from a parsed configuration to a list of artifacts for processing.
Shane Farmer78c43d72017-12-04 09:08:38 -0800455 const std::string& apk_name = file::GetFilename(apk_path).to_string();
Shane Farmercb6c3f92017-11-27 13:19:36 -0800456 std::vector<OutputArtifact> output_artifacts;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800457
Shane Farmer78c43d72017-12-04 09:08:38 -0800458 PostProcessingConfiguration& config = maybe_config.value();
Shane Farmer78c43d72017-12-04 09:08:38 -0800459
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800460 bool valid = true;
Shane Farmer78c43d72017-12-04 09:08:38 -0800461 int version = 1;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800462
Shane Farmer78c43d72017-12-04 09:08:38 -0800463 for (const ConfiguredArtifact& artifact : config.artifacts) {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700464 std::optional<OutputArtifact> output_artifact =
465 ToOutputArtifact(artifact, apk_name, config, diag_);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800466 if (!output_artifact) {
467 // Defer return an error condition so that all errors are reported.
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800468 valid = false;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800469 } else {
Shane Farmer78c43d72017-12-04 09:08:38 -0800470 output_artifact.value().version = version++;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800471 output_artifacts.push_back(std::move(output_artifact.value()));
472 }
473 }
474
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800475 if (!config.ValidateVersionCodeOrdering(diag_)) {
476 diag_->Error(DiagMessage() << "could not validate post processing configuration");
477 valid = false;
478 }
479
480 if (valid) {
481 // Sorting artifacts requires that all references are valid as it uses them to determine order.
482 config.SortArtifacts();
483 }
484
485 if (!valid) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800486 return {};
487 }
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800488
Shane Farmercb6c3f92017-11-27 13:19:36 -0800489 return {output_artifacts};
Shane Farmer74cdea32017-05-12 16:22:36 -0700490}
491
Shane Farmercb6c3f92017-11-27 13:19:36 -0800492namespace configuration {
493namespace handler {
494
495bool ArtifactTagHandler(PostProcessingConfiguration* config, Element* root_element,
496 IDiagnostics* diag) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800497 ConfiguredArtifact artifact{};
Shane Farmer280be342017-06-21 15:20:15 -0700498 for (const auto& attr : root_element->attributes) {
499 if (attr.name == "name") {
500 artifact.name = attr.value;
501 } else if (attr.name == "abi-group") {
502 artifact.abi_group = {attr.value};
503 } else if (attr.name == "screen-density-group") {
504 artifact.screen_density_group = {attr.value};
505 } else if (attr.name == "locale-group") {
506 artifact.locale_group = {attr.value};
Shane Farmer78c43d72017-12-04 09:08:38 -0800507 } else if (attr.name == "android-sdk") {
508 artifact.android_sdk = {attr.value};
Shane Farmer280be342017-06-21 15:20:15 -0700509 } else if (attr.name == "gl-texture-group") {
510 artifact.gl_texture_group = {attr.value};
511 } else if (attr.name == "device-feature-group") {
512 artifact.device_feature_group = {attr.value};
513 } else {
514 diag->Note(DiagMessage() << "Unknown artifact attribute: " << attr.name << " = "
515 << attr.value);
516 }
517 }
518 config->artifacts.push_back(artifact);
519 return true;
520};
Shane Farmer74cdea32017-05-12 16:22:36 -0700521
Shane Farmercb6c3f92017-11-27 13:19:36 -0800522bool ArtifactFormatTagHandler(PostProcessingConfiguration* config, Element* root_element,
523 IDiagnostics* /* diag */) {
Shane Farmer280be342017-06-21 15:20:15 -0700524 for (auto& node : root_element->children) {
525 xml::Text* t;
526 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
527 config->artifact_format = TrimWhitespace(t->text).to_string();
528 break;
529 }
530 }
531 return true;
532};
533
Shane Farmercb6c3f92017-11-27 13:19:36 -0800534bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
535 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700536 std::string label = GetLabel(root_element, diag);
537 if (label.empty()) {
538 return false;
539 }
540
Shane Farmer280be342017-06-21 15:20:15 -0700541 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800542 OrderedEntry<Abi>& entry = config->abi_groups[label];
Ryan Mitchell4382e442021-07-14 12:53:01 -0700543 std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800544 if (!order) {
545 valid = false;
546 } else {
547 entry.order = order.value();
548 }
549 auto& group = entry.entry;
Shane Farmer280be342017-06-21 15:20:15 -0700550
Shane Farmer39e474f2017-12-18 14:44:11 -0800551 // Special case for empty abi-group tag. Label will be used as the ABI.
552 if (root_element->GetChildElements().empty()) {
553 auto abi = kStringToAbiMap.find(label);
554 if (abi == kStringToAbiMap.end()) {
555 return false;
556 }
557 group.push_back(abi->second);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800558 return valid;
Shane Farmer39e474f2017-12-18 14:44:11 -0800559 }
560
Shane Farmer280be342017-06-21 15:20:15 -0700561 for (auto* child : root_element->GetChildElements()) {
562 if (child->name != "abi") {
563 diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
564 valid = false;
565 } else {
566 for (auto& node : child->children) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700567 xml::Text* t;
568 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Shane Farmer39e474f2017-12-18 14:44:11 -0800569 auto abi = kStringToAbiMap.find(TrimWhitespace(t->text).to_string());
570 if (abi != kStringToAbiMap.end()) {
571 group.push_back(abi->second);
572 } else {
573 diag->Error(DiagMessage() << "Could not parse ABI value: " << t->text);
574 valid = false;
575 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700576 break;
577 }
578 }
Shane Farmer280be342017-06-21 15:20:15 -0700579 }
580 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700581
Shane Farmer280be342017-06-21 15:20:15 -0700582 return valid;
583};
Shane Farmer74cdea32017-05-12 16:22:36 -0700584
Shane Farmercb6c3f92017-11-27 13:19:36 -0800585bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
586 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700587 std::string label = GetLabel(root_element, diag);
588 if (label.empty()) {
589 return false;
590 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700591
Shane Farmer280be342017-06-21 15:20:15 -0700592 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800593 OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
Ryan Mitchell4382e442021-07-14 12:53:01 -0700594 std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800595 if (!order) {
596 valid = false;
597 } else {
598 entry.order = order.value();
599 }
600 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700601
Shane Farmer39e474f2017-12-18 14:44:11 -0800602 // Special case for empty screen-density-group tag. Label will be used as the screen density.
603 if (root_element->GetChildElements().empty()) {
604 ConfigDescription config_descriptor;
605 bool parsed = ConfigDescription::Parse(label, &config_descriptor);
606 if (parsed &&
607 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
608 android::ResTable_config::CONFIG_DENSITY)) {
609 // Copy the density with the minimum SDK version stripped out.
610 group.push_back(config_descriptor.CopyWithoutSdkVersion());
611 } else {
612 diag->Error(DiagMessage()
613 << "Could not parse config descriptor for empty screen-density-group: "
614 << label);
615 valid = false;
616 }
617
618 return valid;
619 }
620
Shane Farmer280be342017-06-21 15:20:15 -0700621 for (auto* child : root_element->GetChildElements()) {
622 if (child->name != "screen-density") {
623 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
624 << child->name);
625 valid = false;
626 } else {
627 for (auto& node : child->children) {
628 xml::Text* t;
629 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
630 ConfigDescription config_descriptor;
631 const android::StringPiece& text = TrimWhitespace(t->text);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700632 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
633 if (parsed &&
634 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
635 android::ResTable_config::CONFIG_DENSITY)) {
Shane Farmer280be342017-06-21 15:20:15 -0700636 // Copy the density with the minimum SDK version stripped out.
637 group.push_back(config_descriptor.CopyWithoutSdkVersion());
638 } else {
639 diag->Error(DiagMessage()
640 << "Could not parse config descriptor for screen-density: " << text);
641 valid = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700642 }
Shane Farmer280be342017-06-21 15:20:15 -0700643 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700644 }
645 }
Shane Farmer280be342017-06-21 15:20:15 -0700646 }
647 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700648
Shane Farmer280be342017-06-21 15:20:15 -0700649 return valid;
650};
Shane Farmer74cdea32017-05-12 16:22:36 -0700651
Shane Farmercb6c3f92017-11-27 13:19:36 -0800652bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
653 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700654 std::string label = GetLabel(root_element, diag);
655 if (label.empty()) {
656 return false;
657 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700658
Shane Farmer280be342017-06-21 15:20:15 -0700659 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800660 OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
Ryan Mitchell4382e442021-07-14 12:53:01 -0700661 std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800662 if (!order) {
663 valid = false;
664 } else {
665 entry.order = order.value();
666 }
667 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700668
Shane Farmer39e474f2017-12-18 14:44:11 -0800669 // Special case to auto insert a locale for an empty group. Label will be used for locale.
670 if (root_element->GetChildElements().empty()) {
671 ConfigDescription config_descriptor;
672 bool parsed = ConfigDescription::Parse(label, &config_descriptor);
673 if (parsed &&
674 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
675 android::ResTable_config::CONFIG_LOCALE)) {
676 // Copy the locale with the minimum SDK version stripped out.
677 group.push_back(config_descriptor.CopyWithoutSdkVersion());
678 } else {
679 diag->Error(DiagMessage()
680 << "Could not parse config descriptor for empty screen-density-group: "
681 << label);
682 valid = false;
683 }
684
685 return valid;
686 }
687
Shane Farmer280be342017-06-21 15:20:15 -0700688 for (auto* child : root_element->GetChildElements()) {
689 if (child->name != "locale") {
690 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
691 << child->name);
692 valid = false;
693 } else {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700694 for (auto& node : child->children) {
695 xml::Text* t;
696 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
697 ConfigDescription config_descriptor;
698 const android::StringPiece& text = TrimWhitespace(t->text);
699 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
700 if (parsed &&
701 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
702 android::ResTable_config::CONFIG_LOCALE)) {
703 // Copy the locale with the minimum SDK version stripped out.
704 group.push_back(config_descriptor.CopyWithoutSdkVersion());
705 } else {
706 diag->Error(DiagMessage()
707 << "Could not parse config descriptor for screen-density: " << text);
708 valid = false;
709 }
710 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700711 }
712 }
Shane Farmer280be342017-06-21 15:20:15 -0700713 }
714 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700715
Shane Farmer280be342017-06-21 15:20:15 -0700716 return valid;
717};
Shane Farmer74cdea32017-05-12 16:22:36 -0700718
Shane Farmer78c43d72017-12-04 09:08:38 -0800719bool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_element,
720 IDiagnostics* diag) {
721 AndroidSdk entry = AndroidSdk::ForMinSdk(-1);
Shane Farmer280be342017-06-21 15:20:15 -0700722 bool valid = true;
Shane Farmer78c43d72017-12-04 09:08:38 -0800723 for (const auto& attr : root_element->attributes) {
724 bool valid_attr = false;
725 if (attr.name == "label") {
726 entry.label = attr.value;
727 valid_attr = true;
728 } else if (attr.name == "minSdkVersion") {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700729 std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer78c43d72017-12-04 09:08:38 -0800730 if (version) {
731 valid_attr = true;
732 entry.min_sdk_version = version.value();
733 }
734 } else if (attr.name == "targetSdkVersion") {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700735 std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer78c43d72017-12-04 09:08:38 -0800736 if (version) {
737 valid_attr = true;
738 entry.target_sdk_version = version;
739 }
740 } else if (attr.name == "maxSdkVersion") {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700741 std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer78c43d72017-12-04 09:08:38 -0800742 if (version) {
743 valid_attr = true;
744 entry.max_sdk_version = version;
745 }
746 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700747
Shane Farmer78c43d72017-12-04 09:08:38 -0800748 if (!valid_attr) {
749 diag->Error(DiagMessage() << "Invalid attribute: " << attr.name << " = " << attr.value);
Shane Farmer280be342017-06-21 15:20:15 -0700750 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700751 }
752 }
753
Shane Farmer78c43d72017-12-04 09:08:38 -0800754 if (entry.min_sdk_version == -1) {
755 diag->Error(DiagMessage() << "android-sdk is missing minSdkVersion attribute");
756 valid = false;
757 }
758
759 // TODO: Fill in the manifest details when they are finalised.
760 for (auto node : root_element->GetChildElements()) {
761 if (node->name == "manifest") {
762 if (entry.manifest) {
763 diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
764 continue;
765 }
766 entry.manifest = {AndroidManifest()};
767 }
768 }
769
770 config->android_sdks[entry.label] = entry;
Shane Farmer280be342017-06-21 15:20:15 -0700771 return valid;
772};
Shane Farmer74cdea32017-05-12 16:22:36 -0700773
Shane Farmercb6c3f92017-11-27 13:19:36 -0800774bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
775 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700776 std::string label = GetLabel(root_element, diag);
777 if (label.empty()) {
778 return false;
779 }
780
Shane Farmer280be342017-06-21 15:20:15 -0700781 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800782 OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
Ryan Mitchell4382e442021-07-14 12:53:01 -0700783 std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800784 if (!order) {
785 valid = false;
786 } else {
787 entry.order = order.value();
788 }
789 auto& group = entry.entry;
Shane Farmer280be342017-06-21 15:20:15 -0700790
791 GlTexture result;
792 for (auto* child : root_element->GetChildElements()) {
793 if (child->name != "gl-texture") {
794 diag->Error(DiagMessage() << "Unexpected element in GL texture group: " << child->name);
795 valid = false;
796 } else {
797 for (const auto& attr : child->attributes) {
798 if (attr.name == "name") {
799 result.name = attr.value;
800 break;
801 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700802 }
803
Shane Farmer280be342017-06-21 15:20:15 -0700804 for (auto* element : child->GetChildElements()) {
805 if (element->name != "texture-path") {
806 diag->Error(DiagMessage() << "Unexpected element in gl-texture element: " << child->name);
Shane Farmer74cdea32017-05-12 16:22:36 -0700807 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700808 continue;
809 }
810 for (auto& node : element->children) {
811 xml::Text* t;
812 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
813 result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
Shane Farmer74cdea32017-05-12 16:22:36 -0700814 }
815 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700816 }
Shane Farmer280be342017-06-21 15:20:15 -0700817 }
818 group.push_back(result);
819 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700820
Shane Farmer280be342017-06-21 15:20:15 -0700821 return valid;
822};
Shane Farmer74cdea32017-05-12 16:22:36 -0700823
Shane Farmercb6c3f92017-11-27 13:19:36 -0800824bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
825 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700826 std::string label = GetLabel(root_element, diag);
827 if (label.empty()) {
828 return false;
829 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700830
Shane Farmer280be342017-06-21 15:20:15 -0700831 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800832 OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
Ryan Mitchell4382e442021-07-14 12:53:01 -0700833 std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800834 if (!order) {
835 valid = false;
836 } else {
837 entry.order = order.value();
838 }
839 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700840
Shane Farmer280be342017-06-21 15:20:15 -0700841 for (auto* child : root_element->GetChildElements()) {
842 if (child->name != "supports-feature") {
843 diag->Error(DiagMessage() << "Unexpected root_element in device feature group: "
844 << child->name);
845 valid = false;
846 } else {
847 for (auto& node : child->children) {
848 xml::Text* t;
849 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
850 group.push_back(TrimWhitespace(t->text).to_string());
851 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700852 }
853 }
Shane Farmer280be342017-06-21 15:20:15 -0700854 }
855 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700856
Shane Farmer280be342017-06-21 15:20:15 -0700857 return valid;
858};
Shane Farmer74cdea32017-05-12 16:22:36 -0700859
Shane Farmercb6c3f92017-11-27 13:19:36 -0800860} // namespace handler
861} // namespace configuration
862
Shane Farmer74cdea32017-05-12 16:22:36 -0700863} // namespace aapt