blob: 689ace6e6aa1686a8244a5e8d3e7f1a8b09e8bc5 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
2 * Copyright (C) 2015 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 "ConfigDescription.h"
18#include "Diagnostics.h"
19#include "Flags.h"
20#include "ResourceParser.h"
21#include "ResourceTable.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070022#include "compile/IdAssigner.h"
23#include "compile/Png.h"
Adam Lesinski393b5f02015-12-17 13:03:11 -080024#include "compile/PseudolocaleGenerator.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070025#include "compile/XmlIdCollector.h"
Adam Lesinskia40e9722015-11-24 19:11:46 -080026#include "flatten/Archive.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070027#include "flatten/FileExportWriter.h"
28#include "flatten/TableFlattener.h"
29#include "flatten/XmlFlattener.h"
30#include "util/Files.h"
31#include "util/Maybe.h"
32#include "util/Util.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080033#include "xml/XmlDom.h"
34#include "xml/XmlPullParser.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070035
Adam Lesinskia40e9722015-11-24 19:11:46 -080036#include <dirent.h>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070037#include <fstream>
38#include <string>
39
40namespace aapt {
41
42struct ResourcePathData {
43 Source source;
44 std::u16string resourceDir;
45 std::u16string name;
46 std::string extension;
47
48 // Original config str. We keep this because when we parse the config, we may add on
49 // version qualifiers. We want to preserve the original input so the output is easily
50 // computed before hand.
51 std::string configStr;
52 ConfigDescription config;
53};
54
55/**
56 * Resource file paths are expected to look like:
57 * [--/res/]type[-config]/name
58 */
59static Maybe<ResourcePathData> extractResourcePathData(const std::string& path,
60 std::string* outError) {
61 std::vector<std::string> parts = util::split(path, file::sDirSep);
62 if (parts.size() < 2) {
63 if (outError) *outError = "bad resource path";
64 return {};
65 }
66
67 std::string& dir = parts[parts.size() - 2];
68 StringPiece dirStr = dir;
69
70 StringPiece configStr;
71 ConfigDescription config;
72 size_t dashPos = dir.find('-');
73 if (dashPos != std::string::npos) {
74 configStr = dirStr.substr(dashPos + 1, dir.size() - (dashPos + 1));
75 if (!ConfigDescription::parse(configStr, &config)) {
76 if (outError) {
77 std::stringstream errStr;
78 errStr << "invalid configuration '" << configStr << "'";
79 *outError = errStr.str();
80 }
81 return {};
82 }
83 dirStr = dirStr.substr(0, dashPos);
84 }
85
86 std::string& filename = parts[parts.size() - 1];
87 StringPiece name = filename;
88 StringPiece extension;
89 size_t dotPos = filename.find('.');
90 if (dotPos != std::string::npos) {
91 extension = name.substr(dotPos + 1, filename.size() - (dotPos + 1));
92 name = name.substr(0, dotPos);
93 }
94
95 return ResourcePathData{
Adam Lesinskia40e9722015-11-24 19:11:46 -080096 Source(path),
Adam Lesinski1ab598f2015-08-14 14:26:04 -070097 util::utf8ToUtf16(dirStr),
98 util::utf8ToUtf16(name),
99 extension.toString(),
100 configStr.toString(),
101 config
102 };
103}
104
105struct CompileOptions {
106 std::string outputPath;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800107 Maybe<std::string> resDir;
Adam Lesinski7751afc2016-01-06 15:45:28 -0800108 std::vector<std::u16string> products;
Adam Lesinski393b5f02015-12-17 13:03:11 -0800109 bool pseudolocalize = false;
Adam Lesinski979ccb22016-01-11 10:42:19 -0800110 bool legacyMode = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700111 bool verbose = false;
112};
113
Adam Lesinskia40e9722015-11-24 19:11:46 -0800114static std::string buildIntermediateFilename(const ResourcePathData& data) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700115 std::stringstream name;
116 name << data.resourceDir;
117 if (!data.configStr.empty()) {
118 name << "-" << data.configStr;
119 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800120 name << "_" << data.name;
121 if (!data.extension.empty()) {
122 name << "." << data.extension;
123 }
124 name << ".flat";
Adam Lesinskia40e9722015-11-24 19:11:46 -0800125 return name.str();
126}
127
128static bool isHidden(const StringPiece& filename) {
129 return util::stringStartsWith<char>(filename, ".");
130}
131
132/**
133 * Walks the res directory structure, looking for resource files.
134 */
135static bool loadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
136 std::vector<ResourcePathData>* outPathData) {
137 const std::string& rootDir = options.resDir.value();
138 std::unique_ptr<DIR, decltype(closedir)*> d(opendir(rootDir.data()), closedir);
139 if (!d) {
140 context->getDiagnostics()->error(DiagMessage() << strerror(errno));
141 return false;
142 }
143
144 while (struct dirent* entry = readdir(d.get())) {
145 if (isHidden(entry->d_name)) {
146 continue;
147 }
148
149 std::string prefixPath = rootDir;
150 file::appendPath(&prefixPath, entry->d_name);
151
152 if (file::getFileType(prefixPath) != file::FileType::kDirectory) {
153 continue;
154 }
155
156 std::unique_ptr<DIR, decltype(closedir)*> subDir(opendir(prefixPath.data()), closedir);
157 if (!subDir) {
158 context->getDiagnostics()->error(DiagMessage() << strerror(errno));
159 return false;
160 }
161
162 while (struct dirent* leafEntry = readdir(subDir.get())) {
163 if (isHidden(leafEntry->d_name)) {
164 continue;
165 }
166
167 std::string fullPath = prefixPath;
168 file::appendPath(&fullPath, leafEntry->d_name);
169
170 std::string errStr;
171 Maybe<ResourcePathData> pathData = extractResourcePathData(fullPath, &errStr);
172 if (!pathData) {
173 context->getDiagnostics()->error(DiagMessage() << errStr);
174 return false;
175 }
176
177 outPathData->push_back(std::move(pathData.value()));
178 }
179 }
180 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700181}
182
183static bool compileTable(IAaptContext* context, const CompileOptions& options,
Adam Lesinskia40e9722015-11-24 19:11:46 -0800184 const ResourcePathData& pathData, IArchiveWriter* writer,
185 const std::string& outputPath) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700186 ResourceTable table;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700187 {
188 std::ifstream fin(pathData.source.path, std::ifstream::binary);
189 if (!fin) {
190 context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
191 return false;
192 }
193
194
195 // Parse the values file from XML.
Adam Lesinski467f1712015-11-16 17:35:44 -0800196 xml::XmlPullParser xmlParser(fin);
Adam Lesinski9f222042015-11-04 13:51:45 -0800197
198 ResourceParserOptions parserOptions;
Adam Lesinski7751afc2016-01-06 15:45:28 -0800199 parserOptions.products = options.products;
Adam Lesinski979ccb22016-01-11 10:42:19 -0800200 parserOptions.errorOnPositionalArguments = !options.legacyMode;
Adam Lesinski9f222042015-11-04 13:51:45 -0800201
202 // If the filename includes donottranslate, then the default translatable is false.
203 parserOptions.translatable = pathData.name.find(u"donottranslate") == std::string::npos;
204
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700205 ResourceParser resParser(context->getDiagnostics(), &table, pathData.source,
Adam Lesinski9f222042015-11-04 13:51:45 -0800206 pathData.config, parserOptions);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700207 if (!resParser.parse(&xmlParser)) {
208 return false;
209 }
210
211 fin.close();
212 }
213
Adam Lesinski393b5f02015-12-17 13:03:11 -0800214 if (options.pseudolocalize) {
215 // Generate pseudo-localized strings (en-XA and ar-XB).
216 // These are created as weak symbols, and are only generated from default configuration
217 // strings and plurals.
218 PseudolocaleGenerator pseudolocaleGenerator;
219 if (!pseudolocaleGenerator.consume(context, &table)) {
220 return false;
221 }
222 }
223
Adam Lesinski83f22552015-11-07 11:51:23 -0800224 // Ensure we have the compilation package at least.
225 table.createPackage(context->getCompilationPackage());
226
Adam Lesinskia40e9722015-11-24 19:11:46 -0800227 // Assign an ID to any package that has resources.
Adam Lesinski83f22552015-11-07 11:51:23 -0800228 for (auto& pkg : table.packages) {
229 if (!pkg->id) {
230 // If no package ID was set while parsing (public identifiers), auto assign an ID.
231 pkg->id = context->getPackageId();
232 }
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700233 }
234
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700235 // Assign IDs to prepare the table for flattening.
236 IdAssigner idAssigner;
237 if (!idAssigner.consume(context, &table)) {
238 return false;
239 }
240
241 // Flatten the table.
242 BigBuffer buffer(1024);
243 TableFlattenerOptions tableFlattenerOptions;
244 tableFlattenerOptions.useExtendedChunks = true;
245 TableFlattener flattener(&buffer, tableFlattenerOptions);
246 if (!flattener.consume(context, &table)) {
247 return false;
248 }
249
Adam Lesinskia40e9722015-11-24 19:11:46 -0800250 if (!writer->startEntry(outputPath, 0)) {
251 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700252 return false;
253 }
254
Adam Lesinskia40e9722015-11-24 19:11:46 -0800255 if (writer->writeEntry(buffer)) {
256 if (writer->finishEntry()) {
257 return true;
258 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700259 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800260
261 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
262 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700263}
264
265static bool compileXml(IAaptContext* context, const CompileOptions& options,
Adam Lesinskia40e9722015-11-24 19:11:46 -0800266 const ResourcePathData& pathData, IArchiveWriter* writer,
267 const std::string& outputPath) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700268
Adam Lesinski467f1712015-11-16 17:35:44 -0800269 std::unique_ptr<xml::XmlResource> xmlRes;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700270
271 {
272 std::ifstream fin(pathData.source.path, std::ifstream::binary);
273 if (!fin) {
274 context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
275 return false;
276 }
277
278 xmlRes = xml::inflate(&fin, context->getDiagnostics(), pathData.source);
279
280 fin.close();
281 }
282
283 if (!xmlRes) {
284 return false;
285 }
286
287 // Collect IDs that are defined here.
288 XmlIdCollector collector;
289 if (!collector.consume(context, xmlRes.get())) {
290 return false;
291 }
292
Adam Lesinskia40e9722015-11-24 19:11:46 -0800293 xmlRes->file.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700294 xmlRes->file.config = pathData.config;
295 xmlRes->file.source = pathData.source;
296
297 BigBuffer buffer(1024);
298 ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &xmlRes->file);
299
300 XmlFlattenerOptions xmlFlattenerOptions;
301 xmlFlattenerOptions.keepRawValues = true;
302 XmlFlattener flattener(fileExportWriter.getBuffer(), xmlFlattenerOptions);
303 if (!flattener.consume(context, xmlRes.get())) {
304 return false;
305 }
306
307 fileExportWriter.finish();
308
Adam Lesinskia40e9722015-11-24 19:11:46 -0800309 if (!writer->startEntry(outputPath, 0)) {
310 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700311 return false;
312 }
313
Adam Lesinskia40e9722015-11-24 19:11:46 -0800314 if (writer->writeEntry(buffer)) {
315 if (writer->finishEntry()) {
316 return true;
317 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700318 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800319
320 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
321 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700322}
323
324static bool compilePng(IAaptContext* context, const CompileOptions& options,
Adam Lesinskia40e9722015-11-24 19:11:46 -0800325 const ResourcePathData& pathData, IArchiveWriter* writer,
326 const std::string& outputPath) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700327 BigBuffer buffer(4096);
328 ResourceFile resFile;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800329 resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700330 resFile.config = pathData.config;
331 resFile.source = pathData.source;
332
333 ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &resFile);
334
335 {
336 std::ifstream fin(pathData.source.path, std::ifstream::binary);
337 if (!fin) {
338 context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
339 return false;
340 }
341
342 Png png(context->getDiagnostics());
343 if (!png.process(pathData.source, &fin, fileExportWriter.getBuffer(), {})) {
344 return false;
345 }
346 }
347
348 fileExportWriter.finish();
349
Adam Lesinskia40e9722015-11-24 19:11:46 -0800350 if (!writer->startEntry(outputPath, 0)) {
351 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700352 return false;
353 }
354
Adam Lesinskia40e9722015-11-24 19:11:46 -0800355 if (writer->writeEntry(buffer)) {
356 if (writer->finishEntry()) {
357 return true;
358 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700359 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800360
361 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
362 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700363}
364
365static bool compileFile(IAaptContext* context, const CompileOptions& options,
Adam Lesinskia40e9722015-11-24 19:11:46 -0800366 const ResourcePathData& pathData, IArchiveWriter* writer,
367 const std::string& outputPath) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700368 BigBuffer buffer(256);
369 ResourceFile resFile;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800370 resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700371 resFile.config = pathData.config;
372 resFile.source = pathData.source;
373
374 ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &resFile);
375
376 std::string errorStr;
377 Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
378 if (!f) {
379 context->getDiagnostics()->error(DiagMessage(pathData.source) << errorStr);
380 return false;
381 }
382
Adam Lesinskia40e9722015-11-24 19:11:46 -0800383 if (!writer->startEntry(outputPath, 0)) {
384 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700385 return false;
386 }
387
388 // Manually set the size and don't call finish(). This is because we are not copying from
389 // the buffer the entire file.
390 fileExportWriter.getChunkHeader()->size =
391 util::hostToDevice32(buffer.size() + f.value().getDataLength());
Adam Lesinskia40e9722015-11-24 19:11:46 -0800392
Adam Lesinski52364f72016-01-11 13:10:24 -0800393 if (!writer->writeEntry(buffer)) {
394 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
395 return false;
396 }
397
398 // Only write if we have something to write. This is because mmap fails with length of 0,
399 // but we still want to compile the file to get the resource ID.
400 if (f.value().getDataPtr() && f.value().getDataLength() > 0) {
401 if (!writer->writeEntry(f.value().getDataPtr(), f.value().getDataLength())) {
402 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
403 return false;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800404 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700405 }
406
Adam Lesinski52364f72016-01-11 13:10:24 -0800407 if (!writer->finishEntry()) {
408 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
409 return false;
410 }
411
412 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700413}
414
415class CompileContext : public IAaptContext {
416private:
417 StdErrDiagnostics mDiagnostics;
418
419public:
420 IDiagnostics* getDiagnostics() override {
421 return &mDiagnostics;
422 }
423
424 NameMangler* getNameMangler() override {
425 abort();
426 return nullptr;
427 }
428
429 StringPiece16 getCompilationPackage() override {
430 return {};
431 }
432
433 uint8_t getPackageId() override {
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700434 return 0x0;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700435 }
436
437 ISymbolTable* getExternalSymbols() override {
438 abort();
439 return nullptr;
440 }
441};
442
443/**
444 * Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
445 */
446int compile(const std::vector<StringPiece>& args) {
447 CompileOptions options;
448
Adam Lesinski7751afc2016-01-06 15:45:28 -0800449 Maybe<std::string> productList;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700450 Flags flags = Flags()
451 .requiredFlag("-o", "Output path", &options.outputPath)
Adam Lesinski7751afc2016-01-06 15:45:28 -0800452 .optionalFlag("--product", "Comma separated list of product types to compile",
453 &productList)
Adam Lesinskia40e9722015-11-24 19:11:46 -0800454 .optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
Adam Lesinski393b5f02015-12-17 13:03:11 -0800455 .optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
456 "(en-XA and ar-XB)", &options.pseudolocalize)
Adam Lesinski979ccb22016-01-11 10:42:19 -0800457 .optionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
458 &options.legacyMode)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700459 .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
460 if (!flags.parse("aapt2 compile", args, &std::cerr)) {
461 return 1;
462 }
463
Adam Lesinski7751afc2016-01-06 15:45:28 -0800464 if (productList) {
465 for (StringPiece part : util::tokenize<char>(productList.value(), ',')) {
466 options.products.push_back(util::utf8ToUtf16(part));
467 }
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700468 }
469
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700470 CompileContext context;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800471 std::unique_ptr<IArchiveWriter> archiveWriter;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700472
473 std::vector<ResourcePathData> inputData;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800474 if (options.resDir) {
475 if (!flags.getArgs().empty()) {
476 // Can't have both files and a resource directory.
477 context.getDiagnostics()->error(DiagMessage() << "files given but --dir specified");
478 flags.usage("aapt2 compile", &std::cerr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700479 return 1;
480 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800481
482 if (!loadInputFilesFromDir(&context, options, &inputData)) {
483 return 1;
484 }
485
486 archiveWriter = createZipFileArchiveWriter(context.getDiagnostics(), options.outputPath);
487
488 } else {
489 inputData.reserve(flags.getArgs().size());
490
491 // Collect data from the path for each input file.
492 for (const std::string& arg : flags.getArgs()) {
493 std::string errorStr;
494 if (Maybe<ResourcePathData> pathData = extractResourcePathData(arg, &errorStr)) {
495 inputData.push_back(std::move(pathData.value()));
496 } else {
497 context.getDiagnostics()->error(DiagMessage() << errorStr << " (" << arg << ")");
498 return 1;
499 }
500 }
501
502 archiveWriter = createDirectoryArchiveWriter(context.getDiagnostics(), options.outputPath);
503 }
504
505 if (!archiveWriter) {
506 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700507 }
508
509 bool error = false;
510 for (ResourcePathData& pathData : inputData) {
511 if (options.verbose) {
512 context.getDiagnostics()->note(DiagMessage(pathData.source) << "processing");
513 }
514
515 if (pathData.resourceDir == u"values") {
516 // Overwrite the extension.
517 pathData.extension = "arsc";
518
Adam Lesinskia40e9722015-11-24 19:11:46 -0800519 const std::string outputFilename = buildIntermediateFilename(pathData);
520 if (!compileTable(&context, options, pathData, archiveWriter.get(), outputFilename)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700521 error = true;
522 }
523
524 } else {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800525 const std::string outputFilename = buildIntermediateFilename(pathData);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700526 if (const ResourceType* type = parseResourceType(pathData.resourceDir)) {
527 if (*type != ResourceType::kRaw) {
528 if (pathData.extension == "xml") {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800529 if (!compileXml(&context, options, pathData, archiveWriter.get(),
530 outputFilename)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700531 error = true;
532 }
533 } else if (pathData.extension == "png" || pathData.extension == "9.png") {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800534 if (!compilePng(&context, options, pathData, archiveWriter.get(),
535 outputFilename)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700536 error = true;
537 }
538 } else {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800539 if (!compileFile(&context, options, pathData, archiveWriter.get(),
540 outputFilename)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700541 error = true;
542 }
543 }
544 } else {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800545 if (!compileFile(&context, options, pathData, archiveWriter.get(),
546 outputFilename)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700547 error = true;
548 }
549 }
550 } else {
551 context.getDiagnostics()->error(
552 DiagMessage() << "invalid file path '" << pathData.source << "'");
553 error = true;
554 }
555 }
556 }
557
558 if (error) {
559 return 1;
560 }
561 return 0;
562}
563
564} // namespace aapt