blob: 4afa8f0a907dca1a47a8a6e6d71ca402831c281f [file] [log] [blame]
Adam Lesinskid0f492d2017-04-03 18:12:45 -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 <memory>
18#include <vector>
19
Luke Nicholsonb0643302017-12-01 15:29:03 -080020#include "android-base/file.h"
Shane Farmer57669432017-06-19 12:52:04 -070021#include "android-base/stringprintf.h"
Adam Lesinskid3ffa8442017-09-28 13:34:35 -070022
Shane Farmer0a5b2012017-06-22 12:24:12 -070023#include "androidfw/ResourceTypes.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070024#include "androidfw/StringPiece.h"
25
26#include "Diagnostics.h"
27#include "Flags.h"
28#include "LoadedApk.h"
29#include "ResourceUtils.h"
30#include "SdkConstants.h"
31#include "ValueVisitor.h"
32#include "cmd/Util.h"
Shane Farmer57669432017-06-19 12:52:04 -070033#include "configuration/ConfigurationParser.h"
34#include "filter/AbiFilter.h"
Adam Lesinski46708052017-09-29 14:49:15 -070035#include "format/binary/TableFlattener.h"
36#include "format/binary/XmlFlattener.h"
Adam Lesinski00451162017-10-03 07:44:08 -070037#include "io/BigBufferStream.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070038#include "io/Util.h"
Shane Farmer0a5b2012017-06-22 12:24:12 -070039#include "optimize/MultiApkGenerator.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070040#include "optimize/ResourceDeduper.h"
Mohamed Heikald3c5fb62018-01-12 11:37:26 -050041#include "optimize/ResourceFilter.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070042#include "optimize/VersionCollapser.h"
43#include "split/TableSplitter.h"
Shane Farmer57669432017-06-19 12:52:04 -070044#include "util/Files.h"
Shane Farmer0a5b2012017-06-22 12:24:12 -070045#include "util/Util.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070046
Shane Farmer57669432017-06-19 12:52:04 -070047using ::aapt::configuration::Abi;
Shane Farmercb6c3f92017-11-27 13:19:36 -080048using ::aapt::configuration::OutputArtifact;
Shane Farmer0a5b2012017-06-22 12:24:12 -070049using ::android::ResTable_config;
Shane Farmer57669432017-06-19 12:52:04 -070050using ::android::StringPiece;
Luke Nicholsonb0643302017-12-01 15:29:03 -080051using ::android::base::ReadFileToString;
Shane Farmer0a5b2012017-06-22 12:24:12 -070052using ::android::base::StringAppendF;
Shane Farmer57669432017-06-19 12:52:04 -070053using ::android::base::StringPrintf;
Adam Lesinskid0f492d2017-04-03 18:12:45 -070054
55namespace aapt {
56
57struct OptimizeOptions {
58 // Path to the output APK.
Shane Farmer57669432017-06-19 12:52:04 -070059 Maybe<std::string> output_path;
60 // Path to the output APK directory for splits.
61 Maybe<std::string> output_dir;
Adam Lesinskid0f492d2017-04-03 18:12:45 -070062
63 // Details of the app extracted from the AndroidManifest.xml
64 AppInfo app_info;
65
Mohamed Heikald3c5fb62018-01-12 11:37:26 -050066 // Blacklist of unused resources that should be removed from the apk.
67 std::unordered_set<ResourceName> resources_blacklist;
68
Adam Lesinskid0f492d2017-04-03 18:12:45 -070069 // Split APK options.
70 TableSplitterOptions table_splitter_options;
71
72 // List of output split paths. These are in the same order as `split_constraints`.
73 std::vector<std::string> split_paths;
74
75 // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`.
76 std::vector<SplitConstraints> split_constraints;
77
78 TableFlattenerOptions table_flattener_options;
Shane Farmer57669432017-06-19 12:52:04 -070079
Shane Farmercb6c3f92017-11-27 13:19:36 -080080 Maybe<std::vector<OutputArtifact>> apk_artifacts;
Shane Farmer666de342017-11-29 16:07:51 -080081
82 // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
83 // are kept and will be written as output.
84 std::unordered_set<std::string> kept_artifacts;
Adam Lesinskid0f492d2017-04-03 18:12:45 -070085};
86
87class OptimizeContext : public IAaptContext {
88 public:
Adam Lesinskib522f042017-04-21 16:57:59 -070089 OptimizeContext() = default;
90
91 PackageType GetPackageType() override {
92 // Not important here. Using anything other than kApp adds EXTRA validation, which we want to
93 // avoid.
94 return PackageType::kApp;
95 }
96
Adam Lesinskid0f492d2017-04-03 18:12:45 -070097 IDiagnostics* GetDiagnostics() override {
98 return &diagnostics_;
99 }
100
101 NameMangler* GetNameMangler() override {
102 UNIMPLEMENTED(FATAL);
103 return nullptr;
104 }
105
106 const std::string& GetCompilationPackage() override {
107 static std::string empty;
108 return empty;
109 }
110
111 uint8_t GetPackageId() override {
112 return 0;
113 }
114
115 SymbolTable* GetExternalSymbols() override {
116 UNIMPLEMENTED(FATAL);
117 return nullptr;
118 }
119
120 bool IsVerbose() override {
121 return verbose_;
122 }
123
124 void SetVerbose(bool val) {
125 verbose_ = val;
126 }
127
128 void SetMinSdkVersion(int sdk_version) {
129 sdk_version_ = sdk_version;
130 }
131
132 int GetMinSdkVersion() override {
133 return sdk_version_;
134 }
135
Chris Warrington481f0272018-02-06 14:03:39 +0000136 bool IsAutoNamespace() override {
137 return false;
138 }
139
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700140 private:
Adam Lesinskib522f042017-04-21 16:57:59 -0700141 DISALLOW_COPY_AND_ASSIGN(OptimizeContext);
142
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700143 StdErrDiagnostics diagnostics_;
144 bool verbose_ = false;
145 int sdk_version_ = 0;
146};
147
148class OptimizeCommand {
149 public:
150 OptimizeCommand(OptimizeContext* context, const OptimizeOptions& options)
151 : options_(options), context_(context) {
152 }
153
154 int Run(std::unique_ptr<LoadedApk> apk) {
155 if (context_->IsVerbose()) {
156 context_->GetDiagnostics()->Note(DiagMessage() << "Optimizing APK...");
157 }
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500158 if (!options_.resources_blacklist.empty()) {
159 ResourceFilter filter(options_.resources_blacklist);
160 if (!filter.Consume(context_, apk->GetResourceTable())) {
161 context_->GetDiagnostics()->Error(DiagMessage() << "failed filtering resources");
162 return 1;
163 }
164 }
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700165
166 VersionCollapser collapser;
167 if (!collapser.Consume(context_, apk->GetResourceTable())) {
168 return 1;
169 }
170
171 ResourceDeduper deduper;
172 if (!deduper.Consume(context_, apk->GetResourceTable())) {
173 context_->GetDiagnostics()->Error(DiagMessage() << "failed deduping resources");
174 return 1;
175 }
176
177 // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or
178 // equal to the minSdk.
179 options_.split_constraints =
180 AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints);
181
182 // Stripping the APK using the TableSplitter. The resource table is modified in place in the
183 // LoadedApk.
184 TableSplitter splitter(options_.split_constraints, options_.table_splitter_options);
185 if (!splitter.VerifySplitConstraints(context_)) {
186 return 1;
187 }
188 splitter.SplitTable(apk->GetResourceTable());
189
190 auto path_iter = options_.split_paths.begin();
191 auto split_constraints_iter = options_.split_constraints.begin();
192 for (std::unique_ptr<ResourceTable>& split_table : splitter.splits()) {
193 if (context_->IsVerbose()) {
194 context_->GetDiagnostics()->Note(
195 DiagMessage(*path_iter) << "generating split with configurations '"
196 << util::Joiner(split_constraints_iter->configs, ", ") << "'");
197 }
198
199 // Generate an AndroidManifest.xml for each split.
200 std::unique_ptr<xml::XmlResource> split_manifest =
201 GenerateSplitManifest(options_.app_info, *split_constraints_iter);
202 std::unique_ptr<IArchiveWriter> split_writer =
203 CreateZipFileArchiveWriter(context_->GetDiagnostics(), *path_iter);
204 if (!split_writer) {
205 return 1;
206 }
207
208 if (!WriteSplitApk(split_table.get(), split_manifest.get(), split_writer.get())) {
209 return 1;
210 }
211
212 ++path_iter;
213 ++split_constraints_iter;
214 }
215
Shane Farmercb6c3f92017-11-27 13:19:36 -0800216 if (options_.apk_artifacts && options_.output_dir) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700217 MultiApkGenerator generator{apk.get(), context_};
Shane Farmer666de342017-11-29 16:07:51 -0800218 MultiApkGeneratorOptions generator_options = {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800219 options_.output_dir.value(), options_.apk_artifacts.value(),
220 options_.table_flattener_options, options_.kept_artifacts};
Shane Farmerefe45392017-08-21 14:39:28 -0700221 if (!generator.FromBaseApk(generator_options)) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700222 return 1;
Shane Farmer57669432017-06-19 12:52:04 -0700223 }
224 }
225
226 if (options_.output_path) {
227 std::unique_ptr<IArchiveWriter> writer =
228 CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path.value());
229 if (!apk->WriteToArchive(context_, options_.table_flattener_options, writer.get())) {
230 return 1;
231 }
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700232 }
233
234 return 0;
235 }
236
237 private:
238 bool WriteSplitApk(ResourceTable* table, xml::XmlResource* manifest, IArchiveWriter* writer) {
239 BigBuffer manifest_buffer(4096);
240 XmlFlattener xml_flattener(&manifest_buffer, {});
241 if (!xml_flattener.Consume(context_, manifest)) {
242 return false;
243 }
244
245 io::BigBufferInputStream manifest_buffer_in(&manifest_buffer);
246 if (!io::CopyInputStreamToArchive(context_, &manifest_buffer_in, "AndroidManifest.xml",
247 ArchiveEntry::kCompress, writer)) {
248 return false;
249 }
250
251 std::map<std::pair<ConfigDescription, StringPiece>, FileReference*> config_sorted_files;
252 for (auto& pkg : table->packages) {
253 for (auto& type : pkg->types) {
254 // Sort by config and name, so that we get better locality in the zip file.
255 config_sorted_files.clear();
256
257 for (auto& entry : type->entries) {
258 for (auto& config_value : entry->values) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700259 auto* file_ref = ValueCast<FileReference>(config_value->value.get());
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700260 if (file_ref == nullptr) {
261 continue;
262 }
263
264 if (file_ref->file == nullptr) {
265 ResourceNameRef name(pkg->name, type->type, entry->name);
Adam Lesinski742888f2017-04-28 15:34:52 -0700266 context_->GetDiagnostics()->Warn(DiagMessage(file_ref->GetSource())
Shane Farmer57669432017-06-19 12:52:04 -0700267 << "file for resource " << name << " with config '"
268 << config_value->config << "' not found");
Adam Lesinski742888f2017-04-28 15:34:52 -0700269 continue;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700270 }
271
272 const StringPiece entry_name = entry->name;
273 config_sorted_files[std::make_pair(config_value->config, entry_name)] = file_ref;
274 }
275 }
276
277 for (auto& entry : config_sorted_files) {
278 FileReference* file_ref = entry.second;
Pierre Lecesned55bef72017-11-10 22:31:01 +0000279 if (!io::CopyFileToArchivePreserveCompression(context_, file_ref->file, *file_ref->path,
280 writer)) {
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700281 return false;
282 }
283 }
284 }
285 }
286
287 BigBuffer table_buffer(4096);
288 TableFlattener table_flattener(options_.table_flattener_options, &table_buffer);
289 if (!table_flattener.Consume(context_, table)) {
290 return false;
291 }
292
293 io::BigBufferInputStream table_buffer_in(&table_buffer);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700294 return io::CopyInputStreamToArchive(context_, &table_buffer_in, "resources.arsc",
295 ArchiveEntry::kAlign, writer);
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700296 }
297
298 OptimizeOptions options_;
299 OptimizeContext* context_;
300};
301
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500302bool ExtractObfuscationWhitelistFromConfig(const std::string& path, OptimizeContext* context,
303 OptimizeOptions* options) {
Luke Nicholsonb0643302017-12-01 15:29:03 -0800304 std::string contents;
305 if (!ReadFileToString(path, &contents, true)) {
306 context->GetDiagnostics()->Error(DiagMessage()
307 << "failed to parse whitelist from config file: " << path);
308 return false;
309 }
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500310 for (StringPiece resource_name : util::Tokenize(contents, ',')) {
311 options->table_flattener_options.whitelisted_resources.insert(
312 resource_name.to_string());
313 }
314 return true;
315}
316
317bool ExtractConfig(const std::string& path, OptimizeContext* context,
318 OptimizeOptions* options) {
319 std::string content;
320 if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
321 context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading whitelist");
322 return false;
323 }
324
325 size_t line_no = 0;
326 for (StringPiece line : util::Tokenize(content, '\n')) {
327 line_no++;
328 line = util::TrimWhitespace(line);
329 if (line.empty()) {
330 continue;
331 }
332
333 auto split_line = util::Split(line, '#');
334 if (split_line.size() < 2) {
335 context->GetDiagnostics()->Error(DiagMessage(line) << "No # found in line");
336 return false;
337 }
338 StringPiece resource_string = split_line[0];
339 StringPiece directives = split_line[1];
340 ResourceNameRef resource_name;
341 if (!ResourceUtils::ParseResourceName(resource_string, &resource_name)) {
342 context->GetDiagnostics()->Error(DiagMessage(line) << "Malformed resource name");
343 return false;
344 }
345 if (!resource_name.package.empty()) {
346 context->GetDiagnostics()->Error(DiagMessage(line)
347 << "Package set for resource. Only use type/name");
348 return false;
349 }
350 for (StringPiece directive : util::Tokenize(directives, ',')) {
351 if (directive == "remove") {
352 options->resources_blacklist.insert(resource_name.ToResourceName());
353 } else if (directive == "no_obfuscate") {
354 options->table_flattener_options.whitelisted_resources.insert(
355 resource_name.entry.to_string());
356 }
357 }
Luke Nicholsonb0643302017-12-01 15:29:03 -0800358 }
359 return true;
360}
361
Adam Lesinski8780eb62017-10-31 17:44:39 -0700362bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700363 OptimizeOptions* out_options) {
Adam Lesinski8780eb62017-10-31 17:44:39 -0700364 const xml::XmlResource* manifest = apk->GetManifest();
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700365 if (manifest == nullptr) {
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700366 return false;
367 }
368
Adam Lesinski8780eb62017-10-31 17:44:39 -0700369 Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*manifest, context->GetDiagnostics());
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700370 if (!app_info) {
371 context->GetDiagnostics()->Error(DiagMessage()
372 << "failed to extract data from AndroidManifest.xml");
373 return false;
374 }
375
376 out_options->app_info = std::move(app_info.value());
377 context->SetMinSdkVersion(out_options->app_info.min_sdk_version.value_or_default(0));
378 return true;
379}
380
381int Optimize(const std::vector<StringPiece>& args) {
382 OptimizeContext context;
383 OptimizeOptions options;
Shane Farmer57669432017-06-19 12:52:04 -0700384 Maybe<std::string> config_path;
Luke Nicholsonb0643302017-12-01 15:29:03 -0800385 Maybe<std::string> whitelist_path;
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500386 Maybe<std::string> resources_config_path;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700387 Maybe<std::string> target_densities;
388 std::vector<std::string> configs;
389 std::vector<std::string> split_args;
Shane Farmer666de342017-11-29 16:07:51 -0800390 std::unordered_set<std::string> kept_artifacts;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700391 bool verbose = false;
Shane Farmer9ecc0752017-08-24 15:55:36 -0700392 bool print_only = false;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700393 Flags flags =
394 Flags()
Shane Farmer57669432017-06-19 12:52:04 -0700395 .OptionalFlag("-o", "Path to the output APK.", &options.output_path)
396 .OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir)
397 .OptionalFlag("-x", "Path to XML configuration file.", &config_path)
Shane Farmer9ecc0752017-08-24 15:55:36 -0700398 .OptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only)
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700399 .OptionalFlag(
400 "--target-densities",
401 "Comma separated list of the screen densities that the APK will be optimized for.\n"
402 "All the resources that would be unused on devices of the given densities will be \n"
403 "removed from the APK.",
404 &target_densities)
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500405 .OptionalFlag("--whitelist-path",
Luke Nicholsonb0643302017-12-01 15:29:03 -0800406 "Path to the whitelist.cfg file containing whitelisted resources \n"
407 "whose names should not be altered in final resource tables.",
408 &whitelist_path)
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500409 .OptionalFlag("--resources-config-path",
410 "Path to the resources.cfg file containing the list of resources and \n"
411 "directives to each resource. \n"
412 "Format: type/resource_name#[directive][,directive]",
413 &resources_config_path)
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700414 .OptionalFlagList("-c",
415 "Comma separated list of configurations to include. The default\n"
416 "is all configurations.",
417 &configs)
418 .OptionalFlagList("--split",
419 "Split resources matching a set of configs out to a "
Adam Lesinskidb091572017-04-13 12:48:56 -0700420 "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
421 "On Windows, use a semicolon ';' separator instead.",
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700422 &split_args)
Shane Farmer666de342017-11-29 16:07:51 -0800423 .OptionalFlagList("--keep-artifacts",
424 "Comma separated list of artifacts to keep. If none are specified,\n"
425 "all artifacts will be kept.",
426 &kept_artifacts)
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700427 .OptionalSwitch("--enable-sparse-encoding",
428 "Enables encoding sparse entries using a binary search tree.\n"
429 "This decreases APK size at the cost of resource retrieval performance.",
430 &options.table_flattener_options.use_sparse_entries)
Luke Nicholsonb0643302017-12-01 15:29:03 -0800431 .OptionalSwitch("--enable-resource-obfuscation",
432 "Enables obfuscation of key string pool to single value",
433 &options.table_flattener_options.collapse_key_stringpool)
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700434 .OptionalSwitch("-v", "Enables verbose logging", &verbose);
435
436 if (!flags.Parse("aapt2 optimize", args, &std::cerr)) {
437 return 1;
438 }
439
440 if (flags.GetArgs().size() != 1u) {
441 std::cerr << "must have one APK as argument.\n\n";
442 flags.Usage("aapt2 optimize", &std::cerr);
443 return 1;
444 }
445
Shane Farmer0a5b2012017-06-22 12:24:12 -0700446 const std::string& apk_path = flags.GetArgs()[0];
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700447
448 context.SetVerbose(verbose);
Shane Farmer9ecc0752017-08-24 15:55:36 -0700449 IDiagnostics* diag = context.GetDiagnostics();
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700450
Shane Farmer57669432017-06-19 12:52:04 -0700451 if (config_path) {
Shane Farmer57669432017-06-19 12:52:04 -0700452 std::string& path = config_path.value();
453 Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
454 if (for_path) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800455 options.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
456 if (!options.apk_artifacts) {
457 diag->Error(DiagMessage() << "Failed to parse the output artifact list");
458 return 1;
459 }
460
Shane Farmer57669432017-06-19 12:52:04 -0700461 } else {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700462 diag->Error(DiagMessage() << "Could not parse config file " << path);
Shane Farmer57669432017-06-19 12:52:04 -0700463 return 1;
464 }
Shane Farmer9ecc0752017-08-24 15:55:36 -0700465
466 if (print_only) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800467 for (const OutputArtifact& artifact : options.apk_artifacts.value()) {
468 std::cout << artifact.name << std::endl;
Shane Farmer9ecc0752017-08-24 15:55:36 -0700469 }
470 return 0;
471 }
472
Shane Farmer666de342017-11-29 16:07:51 -0800473 if (!kept_artifacts.empty()) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800474 for (const std::string& artifact_str : kept_artifacts) {
475 for (const StringPiece& artifact : util::Tokenize(artifact_str, ',')) {
Shane Farmer666de342017-11-29 16:07:51 -0800476 options.kept_artifacts.insert(artifact.to_string());
477 }
478 }
479 }
480
Shane Farmer9ecc0752017-08-24 15:55:36 -0700481 // Since we know that we are going to process the APK (not just print targets), make sure we
482 // have somewhere to write them to.
483 if (!options.output_dir) {
484 diag->Error(DiagMessage() << "Output directory is required when using a configuration file");
485 return 1;
486 }
487 } else if (print_only) {
488 diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations");
489 return 1;
Shane Farmer57669432017-06-19 12:52:04 -0700490 }
491
Shane Farmer2c122412017-12-15 16:55:54 -0800492 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, context.GetDiagnostics());
493 if (!apk) {
494 return 1;
495 }
496
497 if (target_densities) {
498 // Parse the target screen densities.
499 for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) {
500 Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
501 if (!target_density) {
502 return 1;
503 }
504 options.table_splitter_options.preferred_densities.push_back(target_density.value());
505 }
506 }
507
508 std::unique_ptr<IConfigFilter> filter;
509 if (!configs.empty()) {
510 filter = ParseConfigFilterParameters(configs, diag);
511 if (filter == nullptr) {
512 return 1;
513 }
514 options.table_splitter_options.config_filter = filter.get();
515 }
516
517 // Parse the split parameters.
518 for (const std::string& split_arg : split_args) {
519 options.split_paths.emplace_back();
520 options.split_constraints.emplace_back();
521 if (!ParseSplitParameter(split_arg, diag, &options.split_paths.back(),
522 &options.split_constraints.back())) {
523 return 1;
524 }
525 }
526
Luke Nicholsonb0643302017-12-01 15:29:03 -0800527 if (options.table_flattener_options.collapse_key_stringpool) {
528 if (whitelist_path) {
529 std::string& path = whitelist_path.value();
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500530 if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options)) {
Luke Nicholsonb0643302017-12-01 15:29:03 -0800531 return 1;
532 }
533 }
534 }
535
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500536 if (resources_config_path) {
537 std::string& path = resources_config_path.value();
538 if (!ExtractConfig(path, &context, &options)) {
539 return 1;
540 }
541 }
542
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700543 if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
544 return 1;
545 }
546
547 OptimizeCommand cmd(&context, options);
548 return cmd.Run(std::move(apk));
549}
550
551} // namespace aapt