blob: 5d11c404cfe3aa564c6ba891e159284dbbdac202 [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/XmlFlattener.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080028#include "proto/ProtoSerialize.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070029#include "util/Files.h"
30#include "util/Maybe.h"
31#include "util/Util.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080032#include "xml/XmlDom.h"
33#include "xml/XmlPullParser.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070034
Adam Lesinski59e04c62016-02-04 15:59:23 -080035#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
36#include <google/protobuf/io/coded_stream.h>
37
Adam Lesinskia40e9722015-11-24 19:11:46 -080038#include <dirent.h>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070039#include <fstream>
40#include <string>
41
42namespace aapt {
43
44struct ResourcePathData {
45 Source source;
46 std::u16string resourceDir;
47 std::u16string name;
48 std::string extension;
49
50 // Original config str. We keep this because when we parse the config, we may add on
51 // version qualifiers. We want to preserve the original input so the output is easily
52 // computed before hand.
53 std::string configStr;
54 ConfigDescription config;
55};
56
57/**
58 * Resource file paths are expected to look like:
59 * [--/res/]type[-config]/name
60 */
61static Maybe<ResourcePathData> extractResourcePathData(const std::string& path,
62 std::string* outError) {
63 std::vector<std::string> parts = util::split(path, file::sDirSep);
64 if (parts.size() < 2) {
65 if (outError) *outError = "bad resource path";
66 return {};
67 }
68
69 std::string& dir = parts[parts.size() - 2];
70 StringPiece dirStr = dir;
71
72 StringPiece configStr;
73 ConfigDescription config;
74 size_t dashPos = dir.find('-');
75 if (dashPos != std::string::npos) {
76 configStr = dirStr.substr(dashPos + 1, dir.size() - (dashPos + 1));
77 if (!ConfigDescription::parse(configStr, &config)) {
78 if (outError) {
79 std::stringstream errStr;
80 errStr << "invalid configuration '" << configStr << "'";
81 *outError = errStr.str();
82 }
83 return {};
84 }
85 dirStr = dirStr.substr(0, dashPos);
86 }
87
88 std::string& filename = parts[parts.size() - 1];
89 StringPiece name = filename;
90 StringPiece extension;
91 size_t dotPos = filename.find('.');
92 if (dotPos != std::string::npos) {
93 extension = name.substr(dotPos + 1, filename.size() - (dotPos + 1));
94 name = name.substr(0, dotPos);
95 }
96
97 return ResourcePathData{
Adam Lesinskia40e9722015-11-24 19:11:46 -080098 Source(path),
Adam Lesinski1ab598f2015-08-14 14:26:04 -070099 util::utf8ToUtf16(dirStr),
100 util::utf8ToUtf16(name),
101 extension.toString(),
102 configStr.toString(),
103 config
104 };
105}
106
107struct CompileOptions {
108 std::string outputPath;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800109 Maybe<std::string> resDir;
Adam Lesinski393b5f02015-12-17 13:03:11 -0800110 bool pseudolocalize = false;
Adam Lesinski979ccb22016-01-11 10:42:19 -0800111 bool legacyMode = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700112 bool verbose = false;
113};
114
Adam Lesinskia40e9722015-11-24 19:11:46 -0800115static std::string buildIntermediateFilename(const ResourcePathData& data) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700116 std::stringstream name;
117 name << data.resourceDir;
118 if (!data.configStr.empty()) {
119 name << "-" << data.configStr;
120 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800121 name << "_" << data.name;
122 if (!data.extension.empty()) {
123 name << "." << data.extension;
124 }
125 name << ".flat";
Adam Lesinskia40e9722015-11-24 19:11:46 -0800126 return name.str();
127}
128
129static bool isHidden(const StringPiece& filename) {
130 return util::stringStartsWith<char>(filename, ".");
131}
132
133/**
134 * Walks the res directory structure, looking for resource files.
135 */
136static bool loadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
137 std::vector<ResourcePathData>* outPathData) {
138 const std::string& rootDir = options.resDir.value();
139 std::unique_ptr<DIR, decltype(closedir)*> d(opendir(rootDir.data()), closedir);
140 if (!d) {
141 context->getDiagnostics()->error(DiagMessage() << strerror(errno));
142 return false;
143 }
144
145 while (struct dirent* entry = readdir(d.get())) {
146 if (isHidden(entry->d_name)) {
147 continue;
148 }
149
150 std::string prefixPath = rootDir;
151 file::appendPath(&prefixPath, entry->d_name);
152
153 if (file::getFileType(prefixPath) != file::FileType::kDirectory) {
154 continue;
155 }
156
157 std::unique_ptr<DIR, decltype(closedir)*> subDir(opendir(prefixPath.data()), closedir);
158 if (!subDir) {
159 context->getDiagnostics()->error(DiagMessage() << strerror(errno));
160 return false;
161 }
162
163 while (struct dirent* leafEntry = readdir(subDir.get())) {
164 if (isHidden(leafEntry->d_name)) {
165 continue;
166 }
167
168 std::string fullPath = prefixPath;
169 file::appendPath(&fullPath, leafEntry->d_name);
170
171 std::string errStr;
172 Maybe<ResourcePathData> pathData = extractResourcePathData(fullPath, &errStr);
173 if (!pathData) {
174 context->getDiagnostics()->error(DiagMessage() << errStr);
175 return false;
176 }
177
178 outPathData->push_back(std::move(pathData.value()));
179 }
180 }
181 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700182}
183
184static bool compileTable(IAaptContext* context, const CompileOptions& options,
Adam Lesinskia40e9722015-11-24 19:11:46 -0800185 const ResourcePathData& pathData, IArchiveWriter* writer,
186 const std::string& outputPath) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700187 ResourceTable table;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700188 {
189 std::ifstream fin(pathData.source.path, std::ifstream::binary);
190 if (!fin) {
191 context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
192 return false;
193 }
194
195
196 // Parse the values file from XML.
Adam Lesinski467f1712015-11-16 17:35:44 -0800197 xml::XmlPullParser xmlParser(fin);
Adam Lesinski9f222042015-11-04 13:51:45 -0800198
199 ResourceParserOptions parserOptions;
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 Lesinski59e04c62016-02-04 15:59:23 -0800235 // Create the file/zip entry.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800236 if (!writer->startEntry(outputPath, 0)) {
237 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700238 return false;
239 }
240
Adam Lesinski59e04c62016-02-04 15:59:23 -0800241 std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(&table);
242
243 // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
244 {
245 google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
246
247 if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
248 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
249 return false;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800250 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700251 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800252
Adam Lesinski59e04c62016-02-04 15:59:23 -0800253 if (!writer->finishEntry()) {
254 context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to finish entry");
255 return false;
256 }
257 return true;
258}
259
260static bool writeHeaderAndBufferToWriter(const StringPiece& outputPath, const ResourceFile& file,
261 const BigBuffer& buffer, IArchiveWriter* writer,
262 IDiagnostics* diag) {
263 // Start the entry so we can write the header.
264 if (!writer->startEntry(outputPath, 0)) {
265 diag->error(DiagMessage(outputPath) << "failed to open file");
266 return false;
267 }
268
269 // Create the header.
270 std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(file);
271
272 {
273 // The stream must be destroyed before we finish the entry, or else
274 // some data won't be flushed.
275 // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
276 // interface.
277 google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
278 CompiledFileOutputStream outputStream(&adaptor, pbCompiledFile.get());
279 for (const BigBuffer::Block& block : buffer) {
280 if (!outputStream.Write(block.buffer.get(), block.size)) {
281 diag->error(DiagMessage(outputPath) << "failed to write data");
282 return false;
283 }
284 }
285 }
286
287 if (!writer->finishEntry()) {
288 diag->error(DiagMessage(outputPath) << "failed to finish writing data");
289 return false;
290 }
291 return true;
292}
293
294static bool writeHeaderAndMmapToWriter(const StringPiece& outputPath, const ResourceFile& file,
295 const android::FileMap& map, IArchiveWriter* writer,
296 IDiagnostics* diag) {
297 // Start the entry so we can write the header.
298 if (!writer->startEntry(outputPath, 0)) {
299 diag->error(DiagMessage(outputPath) << "failed to open file");
300 return false;
301 }
302
303 // Create the header.
304 std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(file);
305
306 {
307 // The stream must be destroyed before we finish the entry, or else
308 // some data won't be flushed.
309 // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
310 // interface.
311 google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
312 CompiledFileOutputStream outputStream(&adaptor, pbCompiledFile.get());
313 if (!outputStream.Write(map.getDataPtr(), map.getDataLength())) {
314 diag->error(DiagMessage(outputPath) << "failed to write data");
315 return false;
316 }
317 }
318
319 if (!writer->finishEntry()) {
320 diag->error(DiagMessage(outputPath) << "failed to finish writing data");
321 return false;
322 }
323 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700324}
325
326static bool compileXml(IAaptContext* context, const CompileOptions& options,
Adam Lesinskia40e9722015-11-24 19:11:46 -0800327 const ResourcePathData& pathData, IArchiveWriter* writer,
328 const std::string& outputPath) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700329
Adam Lesinski467f1712015-11-16 17:35:44 -0800330 std::unique_ptr<xml::XmlResource> xmlRes;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700331 {
332 std::ifstream fin(pathData.source.path, std::ifstream::binary);
333 if (!fin) {
334 context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
335 return false;
336 }
337
338 xmlRes = xml::inflate(&fin, context->getDiagnostics(), pathData.source);
339
340 fin.close();
341 }
342
343 if (!xmlRes) {
344 return false;
345 }
346
347 // Collect IDs that are defined here.
348 XmlIdCollector collector;
349 if (!collector.consume(context, xmlRes.get())) {
350 return false;
351 }
352
Adam Lesinskia40e9722015-11-24 19:11:46 -0800353 xmlRes->file.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700354 xmlRes->file.config = pathData.config;
355 xmlRes->file.source = pathData.source;
356
357 BigBuffer buffer(1024);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700358 XmlFlattenerOptions xmlFlattenerOptions;
359 xmlFlattenerOptions.keepRawValues = true;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800360 XmlFlattener flattener(&buffer, xmlFlattenerOptions);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700361 if (!flattener.consume(context, xmlRes.get())) {
362 return false;
363 }
364
Adam Lesinski59e04c62016-02-04 15:59:23 -0800365 if (!writeHeaderAndBufferToWriter(outputPath, xmlRes->file, buffer, writer,
366 context->getDiagnostics())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700367 return false;
368 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800369 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700370}
371
372static bool compilePng(IAaptContext* context, const CompileOptions& options,
Adam Lesinskia40e9722015-11-24 19:11:46 -0800373 const ResourcePathData& pathData, IArchiveWriter* writer,
374 const std::string& outputPath) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700375 BigBuffer buffer(4096);
376 ResourceFile resFile;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800377 resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700378 resFile.config = pathData.config;
379 resFile.source = pathData.source;
380
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700381 {
382 std::ifstream fin(pathData.source.path, std::ifstream::binary);
383 if (!fin) {
384 context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
385 return false;
386 }
387
388 Png png(context->getDiagnostics());
Adam Lesinski59e04c62016-02-04 15:59:23 -0800389 if (!png.process(pathData.source, &fin, &buffer, {})) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700390 return false;
391 }
392 }
393
Adam Lesinski59e04c62016-02-04 15:59:23 -0800394 if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
395 context->getDiagnostics())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700396 return false;
397 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800398 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700399}
400
401static bool compileFile(IAaptContext* context, const CompileOptions& options,
Adam Lesinskia40e9722015-11-24 19:11:46 -0800402 const ResourcePathData& pathData, IArchiveWriter* writer,
403 const std::string& outputPath) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700404 BigBuffer buffer(256);
405 ResourceFile resFile;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800406 resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700407 resFile.config = pathData.config;
408 resFile.source = pathData.source;
409
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700410 std::string errorStr;
411 Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
412 if (!f) {
413 context->getDiagnostics()->error(DiagMessage(pathData.source) << errorStr);
414 return false;
415 }
416
Adam Lesinski59e04c62016-02-04 15:59:23 -0800417 if (!writeHeaderAndMmapToWriter(outputPath, resFile, f.value(), writer,
418 context->getDiagnostics())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700419 return false;
420 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800421 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700422}
423
424class CompileContext : public IAaptContext {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700425public:
Adam Lesinski355f2852016-02-13 20:26:45 -0800426 void setVerbose(bool val) {
427 mVerbose = val;
428 }
429
430 bool verbose() override {
431 return mVerbose;
432 }
433
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700434 IDiagnostics* getDiagnostics() override {
435 return &mDiagnostics;
436 }
437
438 NameMangler* getNameMangler() override {
439 abort();
440 return nullptr;
441 }
442
Adam Lesinski64587af2016-02-18 18:33:06 -0800443 const std::u16string& getCompilationPackage() override {
444 static std::u16string empty;
445 return empty;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700446 }
447
448 uint8_t getPackageId() override {
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700449 return 0x0;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700450 }
451
Adam Lesinski64587af2016-02-18 18:33:06 -0800452 SymbolTable* getExternalSymbols() override {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700453 abort();
454 return nullptr;
455 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800456
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700457 int getMinSdkVersion() override {
458 return 0;
459 }
460
Adam Lesinski64587af2016-02-18 18:33:06 -0800461private:
462 StdErrDiagnostics mDiagnostics;
463 bool mVerbose = false;
464
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700465};
466
467/**
468 * Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
469 */
470int compile(const std::vector<StringPiece>& args) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800471 CompileContext context;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700472 CompileOptions options;
473
Adam Lesinski355f2852016-02-13 20:26:45 -0800474 bool verbose = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700475 Flags flags = Flags()
476 .requiredFlag("-o", "Output path", &options.outputPath)
Adam Lesinskia40e9722015-11-24 19:11:46 -0800477 .optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
Adam Lesinski393b5f02015-12-17 13:03:11 -0800478 .optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
479 "(en-XA and ar-XB)", &options.pseudolocalize)
Adam Lesinski979ccb22016-01-11 10:42:19 -0800480 .optionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
481 &options.legacyMode)
Adam Lesinski355f2852016-02-13 20:26:45 -0800482 .optionalSwitch("-v", "Enables verbose logging", &verbose);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700483 if (!flags.parse("aapt2 compile", args, &std::cerr)) {
484 return 1;
485 }
486
Adam Lesinski355f2852016-02-13 20:26:45 -0800487 context.setVerbose(verbose);
488
Adam Lesinskia40e9722015-11-24 19:11:46 -0800489 std::unique_ptr<IArchiveWriter> archiveWriter;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700490
491 std::vector<ResourcePathData> inputData;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800492 if (options.resDir) {
493 if (!flags.getArgs().empty()) {
494 // Can't have both files and a resource directory.
495 context.getDiagnostics()->error(DiagMessage() << "files given but --dir specified");
496 flags.usage("aapt2 compile", &std::cerr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700497 return 1;
498 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800499
500 if (!loadInputFilesFromDir(&context, options, &inputData)) {
501 return 1;
502 }
503
504 archiveWriter = createZipFileArchiveWriter(context.getDiagnostics(), options.outputPath);
505
506 } else {
507 inputData.reserve(flags.getArgs().size());
508
509 // Collect data from the path for each input file.
510 for (const std::string& arg : flags.getArgs()) {
511 std::string errorStr;
512 if (Maybe<ResourcePathData> pathData = extractResourcePathData(arg, &errorStr)) {
513 inputData.push_back(std::move(pathData.value()));
514 } else {
515 context.getDiagnostics()->error(DiagMessage() << errorStr << " (" << arg << ")");
516 return 1;
517 }
518 }
519
520 archiveWriter = createDirectoryArchiveWriter(context.getDiagnostics(), options.outputPath);
521 }
522
523 if (!archiveWriter) {
524 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700525 }
526
527 bool error = false;
528 for (ResourcePathData& pathData : inputData) {
529 if (options.verbose) {
530 context.getDiagnostics()->note(DiagMessage(pathData.source) << "processing");
531 }
532
533 if (pathData.resourceDir == u"values") {
534 // Overwrite the extension.
535 pathData.extension = "arsc";
536
Adam Lesinskia40e9722015-11-24 19:11:46 -0800537 const std::string outputFilename = buildIntermediateFilename(pathData);
538 if (!compileTable(&context, options, pathData, archiveWriter.get(), outputFilename)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700539 error = true;
540 }
541
542 } else {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800543 const std::string outputFilename = buildIntermediateFilename(pathData);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700544 if (const ResourceType* type = parseResourceType(pathData.resourceDir)) {
545 if (*type != ResourceType::kRaw) {
546 if (pathData.extension == "xml") {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800547 if (!compileXml(&context, options, pathData, archiveWriter.get(),
548 outputFilename)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700549 error = true;
550 }
551 } else if (pathData.extension == "png" || pathData.extension == "9.png") {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800552 if (!compilePng(&context, options, pathData, archiveWriter.get(),
553 outputFilename)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700554 error = true;
555 }
556 } else {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800557 if (!compileFile(&context, options, pathData, archiveWriter.get(),
558 outputFilename)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700559 error = true;
560 }
561 }
562 } else {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800563 if (!compileFile(&context, options, pathData, archiveWriter.get(),
564 outputFilename)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700565 error = true;
566 }
567 }
568 } else {
569 context.getDiagnostics()->error(
570 DiagMessage() << "invalid file path '" << pathData.source << "'");
571 error = true;
572 }
573 }
574 }
575
576 if (error) {
577 return 1;
578 }
579 return 0;
580}
581
582} // namespace aapt