blob: 902334b98d00451b483adc2d20ed5a32479b4410 [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"
Shane Farmer74cdea32017-05-12 16:22:36 -070028
29#include "ConfigDescription.h"
30#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
43namespace aapt {
44
45namespace {
46
47using ::aapt::configuration::Abi;
48using ::aapt::configuration::AndroidManifest;
49using ::aapt::configuration::AndroidSdk;
Shane Farmercb6c3f92017-11-27 13:19:36 -080050using ::aapt::configuration::ConfiguredArtifact;
51using ::aapt::configuration::DeviceFeature;
52using ::aapt::configuration::Entry;
Shane Farmer78c43d72017-12-04 09:08:38 -080053using ::aapt::configuration::ExtractConfiguration;
Shane Farmer74cdea32017-05-12 16:22:36 -070054using ::aapt::configuration::GlTexture;
55using ::aapt::configuration::Group;
56using ::aapt::configuration::Locale;
Shane Farmer78c43d72017-12-04 09:08:38 -080057using ::aapt::configuration::OrderedEntry;
Shane Farmercb6c3f92017-11-27 13:19:36 -080058using ::aapt::configuration::OutputArtifact;
59using ::aapt::configuration::PostProcessingConfiguration;
60using ::aapt::configuration::handler::AbiGroupTagHandler;
Shane Farmer78c43d72017-12-04 09:08:38 -080061using ::aapt::configuration::handler::AndroidSdkTagHandler;
Shane Farmercb6c3f92017-11-27 13:19:36 -080062using ::aapt::configuration::handler::ArtifactFormatTagHandler;
63using ::aapt::configuration::handler::ArtifactTagHandler;
64using ::aapt::configuration::handler::DeviceFeatureGroupTagHandler;
65using ::aapt::configuration::handler::GlTextureGroupTagHandler;
66using ::aapt::configuration::handler::LocaleGroupTagHandler;
67using ::aapt::configuration::handler::ScreenDensityGroupTagHandler;
Shane Farmerb1027272017-06-14 09:10:28 -070068using ::aapt::io::IFile;
69using ::aapt::io::RegularFile;
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070070using ::aapt::io::StringInputStream;
Shane Farmer74cdea32017-05-12 16:22:36 -070071using ::aapt::util::TrimWhitespace;
72using ::aapt::xml::Element;
Shane Farmer74cdea32017-05-12 16:22:36 -070073using ::aapt::xml::NodeCast;
74using ::aapt::xml::XmlActionExecutor;
75using ::aapt::xml::XmlActionExecutorPolicy;
76using ::aapt::xml::XmlNodeAction;
Shane Farmer0a5b2012017-06-22 12:24:12 -070077using ::android::StringPiece;
Shane Farmercb6c3f92017-11-27 13:19:36 -080078using ::android::base::ReadFileToString;
Shane Farmer74cdea32017-05-12 16:22:36 -070079
Shane Farmercb6c3f92017-11-27 13:19:36 -080080const std::unordered_map<StringPiece, Abi> kStringToAbiMap = {
Shane Farmer57669432017-06-19 12:52:04 -070081 {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
82 {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips},
83 {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal},
84};
Shane Farmercb6c3f92017-11-27 13:19:36 -080085const std::array<StringPiece, 8> kAbiToStringMap = {
86 {"armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64", "universal"}};
Shane Farmer74cdea32017-05-12 16:22:36 -070087
88constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
89
90/** A default noop diagnostics context. */
91class NoopDiagnostics : public IDiagnostics {
92 public:
93 void Log(Level level, DiagMessageActual& actualMsg) override {}
94};
95NoopDiagnostics noop_;
96
Shane Farmer11cdc1c2018-01-31 16:43:24 -080097/** Returns the value of the label attribute for a given element. */
Shane Farmer74cdea32017-05-12 16:22:36 -070098std::string GetLabel(const Element* element, IDiagnostics* diag) {
99 std::string label;
100 for (const auto& attr : element->attributes) {
101 if (attr.name == "label") {
102 label = attr.value;
103 break;
104 }
105 }
106
107 if (label.empty()) {
108 diag->Error(DiagMessage() << "No label found for element " << element->name);
109 }
110 return label;
111}
112
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800113/** Returns the value of the version-code-order attribute for a given element. */
114Maybe<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
115 const xml::Attribute* version = element->FindAttribute("", "version-code-order");
116 if (version == nullptr) {
117 std::string label = GetLabel(element, diag);
118 diag->Error(DiagMessage() << "No version-code-order found for element '" << element->name
119 << "' with label '" << label << "'");
120 return {};
121 }
122 return std::stoi(version->value);
123}
124
Shane Farmer74cdea32017-05-12 16:22:36 -0700125/** XML node visitor that removes all of the namespace URIs from the node and all children. */
126class NamespaceVisitor : public xml::Visitor {
127 public:
128 void Visit(xml::Element* node) override {
129 node->namespace_uri.clear();
130 VisitChildren(node);
131 }
132};
133
Shane Farmercb6c3f92017-11-27 13:19:36 -0800134/** Copies the values referenced in a configuration group to the target list. */
135template <typename T>
136bool CopyXmlReferences(const Maybe<std::string>& name, const Group<T>& groups,
137 std::vector<T>* target) {
138 // If there was no item configured, there is nothing to do and no error.
139 if (!name) {
140 return true;
141 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700142
Shane Farmercb6c3f92017-11-27 13:19:36 -0800143 // If the group could not be found, then something is wrong.
144 auto group = groups.find(name.value());
145 if (group == groups.end()) {
146 return false;
147 }
Shane Farmerb1027272017-06-14 09:10:28 -0700148
Shane Farmer78c43d72017-12-04 09:08:38 -0800149 for (const T& item : group->second.entry) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800150 target->push_back(item);
151 }
152 return true;
Shane Farmer57669432017-06-19 12:52:04 -0700153}
154
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700155/**
156 * Attempts to replace the placeholder in the name string with the provided value. Returns true on
157 * success, or false if the either the placeholder is not found in the name, or the value is not
158 * present and the placeholder was.
159 */
Shane Farmercb6c3f92017-11-27 13:19:36 -0800160bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
161 std::string* name, IDiagnostics* diag) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700162 size_t offset = name->find(placeholder.data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700163 bool found = (offset != std::string::npos);
164
165 // Make sure the placeholder was present if the desired value is present.
166 if (!found) {
167 if (value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700168 diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
169 return false;
170 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700171 return true;
172 }
173
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700174 DCHECK(found) << "Missing return path for placeholder not found";
175
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700176 // Make sure the placeholder was not present if the desired value was not present.
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700177 if (!value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700178 diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700179 return false;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700180 }
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700181
Shane Farmer0a5b2012017-06-22 12:24:12 -0700182 name->replace(offset, placeholder.length(), value.value().data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700183
184 // Make sure there was only one instance of the placeholder.
Shane Farmer0a5b2012017-06-22 12:24:12 -0700185 if (name->find(placeholder.data()) != std::string::npos) {
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700186 diag->Error(DiagMessage() << "Placeholder present multiple times: " << placeholder);
187 return false;
188 }
189 return true;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700190}
191
Shane Farmer9ecc0752017-08-24 15:55:36 -0700192/**
Shane Farmercb6c3f92017-11-27 13:19:36 -0800193 * An ActionHandler for processing XML elements in the XmlActionExecutor. Returns true if the
194 * element was successfully processed, otherwise returns false.
195 */
196using ActionHandler = std::function<bool(configuration::PostProcessingConfiguration* config,
197 xml::Element* element, IDiagnostics* diag)>;
198
199/** Binds an ActionHandler to the current configuration being populated. */
200xml::XmlNodeAction::ActionFuncWithDiag Bind(configuration::PostProcessingConfiguration* config,
201 const ActionHandler& handler) {
202 return [config, handler](xml::Element* root_element, SourcePathDiagnostics* diag) {
203 return handler(config, root_element, diag);
204 };
205}
206
Shane Farmercb6c3f92017-11-27 13:19:36 -0800207/** Converts a ConfiguredArtifact into an OutputArtifact. */
208Maybe<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
209 const std::string& apk_name,
210 const PostProcessingConfiguration& config,
211 IDiagnostics* diag) {
212 if (!artifact.name && !config.artifact_format) {
213 diag->Error(
214 DiagMessage() << "Artifact does not have a name and no global name template defined");
215 return {};
216 }
217
218 Maybe<std::string> artifact_name =
219 (artifact.name) ? artifact.Name(apk_name, diag)
220 : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
221
222 if (!artifact_name) {
223 diag->Error(DiagMessage() << "Could not determine split APK artifact name");
224 return {};
225 }
226
227 OutputArtifact output_artifact;
228 output_artifact.name = artifact_name.value();
229
230 SourcePathDiagnostics src_diag{{output_artifact.name}, diag};
231 bool has_errors = false;
232
233 if (!CopyXmlReferences(artifact.abi_group, config.abi_groups, &output_artifact.abis)) {
234 src_diag.Error(DiagMessage() << "Could not lookup required ABIs: "
235 << artifact.abi_group.value());
236 has_errors = true;
237 }
238
239 if (!CopyXmlReferences(artifact.locale_group, config.locale_groups, &output_artifact.locales)) {
240 src_diag.Error(DiagMessage() << "Could not lookup required locales: "
241 << artifact.locale_group.value());
242 has_errors = true;
243 }
244
245 if (!CopyXmlReferences(artifact.screen_density_group, config.screen_density_groups,
246 &output_artifact.screen_densities)) {
247 src_diag.Error(DiagMessage() << "Could not lookup required screen densities: "
248 << artifact.screen_density_group.value());
249 has_errors = true;
250 }
251
252 if (!CopyXmlReferences(artifact.device_feature_group, config.device_feature_groups,
253 &output_artifact.features)) {
254 src_diag.Error(DiagMessage() << "Could not lookup required device features: "
255 << artifact.device_feature_group.value());
256 has_errors = true;
257 }
258
259 if (!CopyXmlReferences(artifact.gl_texture_group, config.gl_texture_groups,
260 &output_artifact.textures)) {
261 src_diag.Error(DiagMessage() << "Could not lookup required OpenGL texture formats: "
262 << artifact.gl_texture_group.value());
263 has_errors = true;
264 }
265
Shane Farmer78c43d72017-12-04 09:08:38 -0800266 if (artifact.android_sdk) {
267 auto entry = config.android_sdks.find(artifact.android_sdk.value());
268 if (entry == config.android_sdks.end()) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800269 src_diag.Error(DiagMessage() << "Could not lookup required Android SDK version: "
Shane Farmer78c43d72017-12-04 09:08:38 -0800270 << artifact.android_sdk.value());
Shane Farmercb6c3f92017-11-27 13:19:36 -0800271 has_errors = true;
272 } else {
273 output_artifact.android_sdk = {entry->second};
274 }
275 }
276
277 if (has_errors) {
278 return {};
279 }
280 return {output_artifact};
281}
282
283} // namespace
284
285namespace configuration {
286
Shane Farmer78c43d72017-12-04 09:08:38 -0800287/** Returns the binary reprasentation of the XML configuration. */
288Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
289 const std::string& config_path,
290 IDiagnostics* diag) {
291 StringInputStream in(contents);
292 std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag, Source(config_path));
293 if (!doc) {
294 return {};
295 }
296
297 // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
298 Element* root = doc->root.get();
299 if (root == nullptr) {
300 diag->Error(DiagMessage() << "Could not find the root element in the XML document");
301 return {};
302 }
303
304 std::string& xml_ns = root->namespace_uri;
305 if (!xml_ns.empty()) {
306 if (xml_ns != kAaptXmlNs) {
307 diag->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
308 return {};
309 }
310
311 xml_ns.clear();
312 NamespaceVisitor visitor;
313 root->Accept(&visitor);
314 }
315
316 XmlActionExecutor executor;
317 XmlNodeAction& root_action = executor["post-process"];
318 XmlNodeAction& artifacts_action = root_action["artifacts"];
319
320 PostProcessingConfiguration config;
321
322 // Parse the artifact elements.
323 artifacts_action["artifact"].Action(Bind(&config, ArtifactTagHandler));
324 artifacts_action["artifact-format"].Action(Bind(&config, ArtifactFormatTagHandler));
325
326 // Parse the different configuration groups.
327 root_action["abi-groups"]["abi-group"].Action(Bind(&config, AbiGroupTagHandler));
328 root_action["screen-density-groups"]["screen-density-group"].Action(
329 Bind(&config, ScreenDensityGroupTagHandler));
330 root_action["locale-groups"]["locale-group"].Action(Bind(&config, LocaleGroupTagHandler));
331 root_action["android-sdks"]["android-sdk"].Action(Bind(&config, AndroidSdkTagHandler));
332 root_action["gl-texture-groups"]["gl-texture-group"].Action(
333 Bind(&config, GlTextureGroupTagHandler));
334 root_action["device-feature-groups"]["device-feature-group"].Action(
335 Bind(&config, DeviceFeatureGroupTagHandler));
336
337 if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag, doc.get())) {
338 diag->Error(DiagMessage() << "Could not process XML document");
339 return {};
340 }
341
342 return {config};
343}
344
Shane Farmercb6c3f92017-11-27 13:19:36 -0800345const StringPiece& AbiToString(Abi abi) {
346 return kAbiToStringMap.at(static_cast<size_t>(abi));
347}
348
349/**
Shane Farmer9ecc0752017-08-24 15:55:36 -0700350 * Returns the common artifact base name from a template string.
351 */
352Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
353 const StringPiece ext = file::GetExtension(apk_name);
354 size_t end_index = apk_name.to_string().rfind(ext.to_string());
355 const std::string base_name =
356 (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : "";
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700357
Shane Farmer9ecc0752017-08-24 15:55:36 -0700358 // Base name is optional.
359 if (result.find("${basename}") != std::string::npos) {
360 Maybe<StringPiece> maybe_base_name =
361 base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
362 if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
363 return {};
364 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700365 }
366
Shane Farmer0a5b2012017-06-22 12:24:12 -0700367 // Extension is optional.
368 if (result.find("${ext}") != std::string::npos) {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700369 // Make sure we disregard the '.' in the extension when replacing the placeholder.
370 if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700371 return {};
372 }
Shane Farmer9ecc0752017-08-24 15:55:36 -0700373 } else {
374 // If no extension is specified, and the name template does not end in the current extension,
375 // add the existing extension.
376 if (!util::EndsWith(result, ext)) {
377 result.append(ext.to_string());
378 }
Shane Farmer0a5b2012017-06-22 12:24:12 -0700379 }
380
Shane Farmer9ecc0752017-08-24 15:55:36 -0700381 return result;
382}
383
Shane Farmercb6c3f92017-11-27 13:19:36 -0800384Maybe<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
385 const StringPiece& apk_name,
386 IDiagnostics* diag) const {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700387 Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
388 if (!base) {
389 return {};
390 }
391 std::string result = std::move(base.value());
392
Shane Farmer0a5b2012017-06-22 12:24:12 -0700393 if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700394 return {};
395 }
396
Shane Farmer0a5b2012017-06-22 12:24:12 -0700397 if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700398 return {};
399 }
400
Shane Farmer0a5b2012017-06-22 12:24:12 -0700401 if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700402 return {};
403 }
404
Shane Farmer78c43d72017-12-04 09:08:38 -0800405 if (!ReplacePlaceholder("${sdk}", android_sdk, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700406 return {};
407 }
408
Shane Farmer0a5b2012017-06-22 12:24:12 -0700409 if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700410 return {};
411 }
412
Shane Farmer0a5b2012017-06-22 12:24:12 -0700413 if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) {
414 return {};
415 }
416
417 return result;
418}
419
Shane Farmercb6c3f92017-11-27 13:19:36 -0800420Maybe<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700421 if (!name) {
422 return {};
423 }
424
Shane Farmer9ecc0752017-08-24 15:55:36 -0700425 return ToBaseName(name.value(), apk_name, diag);
426}
Shane Farmer0a5b2012017-06-22 12:24:12 -0700427
Shane Farmer57669432017-06-19 12:52:04 -0700428} // namespace configuration
Shane Farmerb1027272017-06-14 09:10:28 -0700429
430/** Returns a ConfigurationParser for the file located at the provided path. */
431Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
432 std::string contents;
433 if (!ReadFileToString(path, &contents, true)) {
434 return {};
435 }
Shane Farmer78c43d72017-12-04 09:08:38 -0800436 return ConfigurationParser(contents, path);
Shane Farmerb1027272017-06-14 09:10:28 -0700437}
438
Shane Farmer78c43d72017-12-04 09:08:38 -0800439ConfigurationParser::ConfigurationParser(std::string contents, const std::string& config_path)
440 : contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700441}
442
Shane Farmercb6c3f92017-11-27 13:19:36 -0800443Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
444 const android::StringPiece& apk_path) {
Shane Farmer78c43d72017-12-04 09:08:38 -0800445 Maybe<PostProcessingConfiguration> maybe_config =
446 ExtractConfiguration(contents_, config_path_, diag_);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800447 if (!maybe_config) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700448 return {};
449 }
Shane Farmercb6c3f92017-11-27 13:19:36 -0800450
451 // Convert from a parsed configuration to a list of artifacts for processing.
Shane Farmer78c43d72017-12-04 09:08:38 -0800452 const std::string& apk_name = file::GetFilename(apk_path).to_string();
Shane Farmercb6c3f92017-11-27 13:19:36 -0800453 std::vector<OutputArtifact> output_artifacts;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800454
Shane Farmer78c43d72017-12-04 09:08:38 -0800455 PostProcessingConfiguration& config = maybe_config.value();
Shane Farmer78c43d72017-12-04 09:08:38 -0800456
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800457 bool valid = true;
Shane Farmer78c43d72017-12-04 09:08:38 -0800458 int version = 1;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800459
Shane Farmer78c43d72017-12-04 09:08:38 -0800460 for (const ConfiguredArtifact& artifact : config.artifacts) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800461 Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_);
462 if (!output_artifact) {
463 // Defer return an error condition so that all errors are reported.
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800464 valid = false;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800465 } else {
Shane Farmer78c43d72017-12-04 09:08:38 -0800466 output_artifact.value().version = version++;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800467 output_artifacts.push_back(std::move(output_artifact.value()));
468 }
469 }
470
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800471 if (!config.ValidateVersionCodeOrdering(diag_)) {
472 diag_->Error(DiagMessage() << "could not validate post processing configuration");
473 valid = false;
474 }
475
476 if (valid) {
477 // Sorting artifacts requires that all references are valid as it uses them to determine order.
478 config.SortArtifacts();
479 }
480
481 if (!valid) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800482 return {};
483 }
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800484
Shane Farmercb6c3f92017-11-27 13:19:36 -0800485 return {output_artifacts};
Shane Farmer74cdea32017-05-12 16:22:36 -0700486}
487
Shane Farmercb6c3f92017-11-27 13:19:36 -0800488namespace configuration {
489namespace handler {
490
491bool ArtifactTagHandler(PostProcessingConfiguration* config, Element* root_element,
492 IDiagnostics* diag) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800493 ConfiguredArtifact artifact{};
Shane Farmer280be342017-06-21 15:20:15 -0700494 for (const auto& attr : root_element->attributes) {
495 if (attr.name == "name") {
496 artifact.name = attr.value;
497 } else if (attr.name == "abi-group") {
498 artifact.abi_group = {attr.value};
499 } else if (attr.name == "screen-density-group") {
500 artifact.screen_density_group = {attr.value};
501 } else if (attr.name == "locale-group") {
502 artifact.locale_group = {attr.value};
Shane Farmer78c43d72017-12-04 09:08:38 -0800503 } else if (attr.name == "android-sdk") {
504 artifact.android_sdk = {attr.value};
Shane Farmer280be342017-06-21 15:20:15 -0700505 } else if (attr.name == "gl-texture-group") {
506 artifact.gl_texture_group = {attr.value};
507 } else if (attr.name == "device-feature-group") {
508 artifact.device_feature_group = {attr.value};
509 } else {
510 diag->Note(DiagMessage() << "Unknown artifact attribute: " << attr.name << " = "
511 << attr.value);
512 }
513 }
514 config->artifacts.push_back(artifact);
515 return true;
516};
Shane Farmer74cdea32017-05-12 16:22:36 -0700517
Shane Farmercb6c3f92017-11-27 13:19:36 -0800518bool ArtifactFormatTagHandler(PostProcessingConfiguration* config, Element* root_element,
519 IDiagnostics* /* diag */) {
Shane Farmer280be342017-06-21 15:20:15 -0700520 for (auto& node : root_element->children) {
521 xml::Text* t;
522 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
523 config->artifact_format = TrimWhitespace(t->text).to_string();
524 break;
525 }
526 }
527 return true;
528};
529
Shane Farmercb6c3f92017-11-27 13:19:36 -0800530bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
531 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700532 std::string label = GetLabel(root_element, diag);
533 if (label.empty()) {
534 return false;
535 }
536
Shane Farmer280be342017-06-21 15:20:15 -0700537 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800538 OrderedEntry<Abi>& entry = config->abi_groups[label];
539 Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
540 if (!order) {
541 valid = false;
542 } else {
543 entry.order = order.value();
544 }
545 auto& group = entry.entry;
Shane Farmer280be342017-06-21 15:20:15 -0700546
Shane Farmer39e474f2017-12-18 14:44:11 -0800547 // Special case for empty abi-group tag. Label will be used as the ABI.
548 if (root_element->GetChildElements().empty()) {
549 auto abi = kStringToAbiMap.find(label);
550 if (abi == kStringToAbiMap.end()) {
551 return false;
552 }
553 group.push_back(abi->second);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800554 return valid;
Shane Farmer39e474f2017-12-18 14:44:11 -0800555 }
556
Shane Farmer280be342017-06-21 15:20:15 -0700557 for (auto* child : root_element->GetChildElements()) {
558 if (child->name != "abi") {
559 diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
560 valid = false;
561 } else {
562 for (auto& node : child->children) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700563 xml::Text* t;
564 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Shane Farmer39e474f2017-12-18 14:44:11 -0800565 auto abi = kStringToAbiMap.find(TrimWhitespace(t->text).to_string());
566 if (abi != kStringToAbiMap.end()) {
567 group.push_back(abi->second);
568 } else {
569 diag->Error(DiagMessage() << "Could not parse ABI value: " << t->text);
570 valid = false;
571 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700572 break;
573 }
574 }
Shane Farmer280be342017-06-21 15:20:15 -0700575 }
576 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700577
Shane Farmer280be342017-06-21 15:20:15 -0700578 return valid;
579};
Shane Farmer74cdea32017-05-12 16:22:36 -0700580
Shane Farmercb6c3f92017-11-27 13:19:36 -0800581bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
582 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700583 std::string label = GetLabel(root_element, diag);
584 if (label.empty()) {
585 return false;
586 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700587
Shane Farmer280be342017-06-21 15:20:15 -0700588 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800589 OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
590 Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
591 if (!order) {
592 valid = false;
593 } else {
594 entry.order = order.value();
595 }
596 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700597
Shane Farmer39e474f2017-12-18 14:44:11 -0800598 // Special case for empty screen-density-group tag. Label will be used as the screen density.
599 if (root_element->GetChildElements().empty()) {
600 ConfigDescription config_descriptor;
601 bool parsed = ConfigDescription::Parse(label, &config_descriptor);
602 if (parsed &&
603 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
604 android::ResTable_config::CONFIG_DENSITY)) {
605 // Copy the density with the minimum SDK version stripped out.
606 group.push_back(config_descriptor.CopyWithoutSdkVersion());
607 } else {
608 diag->Error(DiagMessage()
609 << "Could not parse config descriptor for empty screen-density-group: "
610 << label);
611 valid = false;
612 }
613
614 return valid;
615 }
616
Shane Farmer280be342017-06-21 15:20:15 -0700617 for (auto* child : root_element->GetChildElements()) {
618 if (child->name != "screen-density") {
619 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
620 << child->name);
621 valid = false;
622 } else {
623 for (auto& node : child->children) {
624 xml::Text* t;
625 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
626 ConfigDescription config_descriptor;
627 const android::StringPiece& text = TrimWhitespace(t->text);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700628 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
629 if (parsed &&
630 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
631 android::ResTable_config::CONFIG_DENSITY)) {
Shane Farmer280be342017-06-21 15:20:15 -0700632 // Copy the density with the minimum SDK version stripped out.
633 group.push_back(config_descriptor.CopyWithoutSdkVersion());
634 } else {
635 diag->Error(DiagMessage()
636 << "Could not parse config descriptor for screen-density: " << text);
637 valid = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700638 }
Shane Farmer280be342017-06-21 15:20:15 -0700639 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700640 }
641 }
Shane Farmer280be342017-06-21 15:20:15 -0700642 }
643 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700644
Shane Farmer280be342017-06-21 15:20:15 -0700645 return valid;
646};
Shane Farmer74cdea32017-05-12 16:22:36 -0700647
Shane Farmercb6c3f92017-11-27 13:19:36 -0800648bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
649 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700650 std::string label = GetLabel(root_element, diag);
651 if (label.empty()) {
652 return false;
653 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700654
Shane Farmer280be342017-06-21 15:20:15 -0700655 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800656 OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
657 Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
658 if (!order) {
659 valid = false;
660 } else {
661 entry.order = order.value();
662 }
663 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700664
Shane Farmer39e474f2017-12-18 14:44:11 -0800665 // Special case to auto insert a locale for an empty group. Label will be used for locale.
666 if (root_element->GetChildElements().empty()) {
667 ConfigDescription config_descriptor;
668 bool parsed = ConfigDescription::Parse(label, &config_descriptor);
669 if (parsed &&
670 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
671 android::ResTable_config::CONFIG_LOCALE)) {
672 // Copy the locale with the minimum SDK version stripped out.
673 group.push_back(config_descriptor.CopyWithoutSdkVersion());
674 } else {
675 diag->Error(DiagMessage()
676 << "Could not parse config descriptor for empty screen-density-group: "
677 << label);
678 valid = false;
679 }
680
681 return valid;
682 }
683
Shane Farmer280be342017-06-21 15:20:15 -0700684 for (auto* child : root_element->GetChildElements()) {
685 if (child->name != "locale") {
686 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
687 << child->name);
688 valid = false;
689 } else {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700690 for (auto& node : child->children) {
691 xml::Text* t;
692 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
693 ConfigDescription config_descriptor;
694 const android::StringPiece& text = TrimWhitespace(t->text);
695 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
696 if (parsed &&
697 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
698 android::ResTable_config::CONFIG_LOCALE)) {
699 // Copy the locale with the minimum SDK version stripped out.
700 group.push_back(config_descriptor.CopyWithoutSdkVersion());
701 } else {
702 diag->Error(DiagMessage()
703 << "Could not parse config descriptor for screen-density: " << text);
704 valid = false;
705 }
706 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700707 }
708 }
Shane Farmer280be342017-06-21 15:20:15 -0700709 }
710 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700711
Shane Farmer280be342017-06-21 15:20:15 -0700712 return valid;
713};
Shane Farmer74cdea32017-05-12 16:22:36 -0700714
Shane Farmer78c43d72017-12-04 09:08:38 -0800715bool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_element,
716 IDiagnostics* diag) {
717 AndroidSdk entry = AndroidSdk::ForMinSdk(-1);
Shane Farmer280be342017-06-21 15:20:15 -0700718 bool valid = true;
Shane Farmer78c43d72017-12-04 09:08:38 -0800719 for (const auto& attr : root_element->attributes) {
720 bool valid_attr = false;
721 if (attr.name == "label") {
722 entry.label = attr.value;
723 valid_attr = true;
724 } else if (attr.name == "minSdkVersion") {
725 Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
726 if (version) {
727 valid_attr = true;
728 entry.min_sdk_version = version.value();
729 }
730 } else if (attr.name == "targetSdkVersion") {
731 Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
732 if (version) {
733 valid_attr = true;
734 entry.target_sdk_version = version;
735 }
736 } else if (attr.name == "maxSdkVersion") {
737 Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
738 if (version) {
739 valid_attr = true;
740 entry.max_sdk_version = version;
741 }
742 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700743
Shane Farmer78c43d72017-12-04 09:08:38 -0800744 if (!valid_attr) {
745 diag->Error(DiagMessage() << "Invalid attribute: " << attr.name << " = " << attr.value);
Shane Farmer280be342017-06-21 15:20:15 -0700746 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700747 }
748 }
749
Shane Farmer78c43d72017-12-04 09:08:38 -0800750 if (entry.min_sdk_version == -1) {
751 diag->Error(DiagMessage() << "android-sdk is missing minSdkVersion attribute");
752 valid = false;
753 }
754
755 // TODO: Fill in the manifest details when they are finalised.
756 for (auto node : root_element->GetChildElements()) {
757 if (node->name == "manifest") {
758 if (entry.manifest) {
759 diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
760 continue;
761 }
762 entry.manifest = {AndroidManifest()};
763 }
764 }
765
766 config->android_sdks[entry.label] = entry;
Shane Farmer280be342017-06-21 15:20:15 -0700767 return valid;
768};
Shane Farmer74cdea32017-05-12 16:22:36 -0700769
Shane Farmercb6c3f92017-11-27 13:19:36 -0800770bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
771 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700772 std::string label = GetLabel(root_element, diag);
773 if (label.empty()) {
774 return false;
775 }
776
Shane Farmer280be342017-06-21 15:20:15 -0700777 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800778 OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
779 Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
780 if (!order) {
781 valid = false;
782 } else {
783 entry.order = order.value();
784 }
785 auto& group = entry.entry;
Shane Farmer280be342017-06-21 15:20:15 -0700786
787 GlTexture result;
788 for (auto* child : root_element->GetChildElements()) {
789 if (child->name != "gl-texture") {
790 diag->Error(DiagMessage() << "Unexpected element in GL texture group: " << child->name);
791 valid = false;
792 } else {
793 for (const auto& attr : child->attributes) {
794 if (attr.name == "name") {
795 result.name = attr.value;
796 break;
797 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700798 }
799
Shane Farmer280be342017-06-21 15:20:15 -0700800 for (auto* element : child->GetChildElements()) {
801 if (element->name != "texture-path") {
802 diag->Error(DiagMessage() << "Unexpected element in gl-texture element: " << child->name);
Shane Farmer74cdea32017-05-12 16:22:36 -0700803 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700804 continue;
805 }
806 for (auto& node : element->children) {
807 xml::Text* t;
808 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
809 result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
Shane Farmer74cdea32017-05-12 16:22:36 -0700810 }
811 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700812 }
Shane Farmer280be342017-06-21 15:20:15 -0700813 }
814 group.push_back(result);
815 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700816
Shane Farmer280be342017-06-21 15:20:15 -0700817 return valid;
818};
Shane Farmer74cdea32017-05-12 16:22:36 -0700819
Shane Farmercb6c3f92017-11-27 13:19:36 -0800820bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
821 IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700822 std::string label = GetLabel(root_element, diag);
823 if (label.empty()) {
824 return false;
825 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700826
Shane Farmer280be342017-06-21 15:20:15 -0700827 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800828 OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
829 Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
830 if (!order) {
831 valid = false;
832 } else {
833 entry.order = order.value();
834 }
835 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700836
Shane Farmer280be342017-06-21 15:20:15 -0700837 for (auto* child : root_element->GetChildElements()) {
838 if (child->name != "supports-feature") {
839 diag->Error(DiagMessage() << "Unexpected root_element in device feature group: "
840 << child->name);
841 valid = false;
842 } else {
843 for (auto& node : child->children) {
844 xml::Text* t;
845 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
846 group.push_back(TrimWhitespace(t->text).to_string());
847 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700848 }
849 }
Shane Farmer280be342017-06-21 15:20:15 -0700850 }
851 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700852
Shane Farmer280be342017-06-21 15:20:15 -0700853 return valid;
854};
Shane Farmer74cdea32017-05-12 16:22:36 -0700855
Shane Farmercb6c3f92017-11-27 13:19:36 -0800856} // namespace handler
857} // namespace configuration
858
Shane Farmer74cdea32017-05-12 16:22:36 -0700859} // namespace aapt