]> git.saurik.com Git - android/aapt.git/blob - Command.cpp
auto import from //branches/cupcake/...@130745
[android/aapt.git] / Command.cpp
1 //
2 // Copyright 2006 The Android Open Source Project
3 //
4 // Android Asset Packaging Tool main entry point.
5 //
6 #include "Main.h"
7 #include "Bundle.h"
8 #include "ResourceTable.h"
9 #include "XMLNode.h"
10
11 #include <utils.h>
12 #include <utils/ZipFile.h>
13
14 #include <fcntl.h>
15 #include <errno.h>
16
17 using namespace android;
18
19 /*
20 * Show version info. All the cool kids do it.
21 */
22 int doVersion(Bundle* bundle)
23 {
24 if (bundle->getFileSpecCount() != 0)
25 printf("(ignoring extra arguments)\n");
26 printf("Android Asset Packaging Tool, v0.2\n");
27
28 return 0;
29 }
30
31
32 /*
33 * Open the file read only. The call fails if the file doesn't exist.
34 *
35 * Returns NULL on failure.
36 */
37 ZipFile* openReadOnly(const char* fileName)
38 {
39 ZipFile* zip;
40 status_t result;
41
42 zip = new ZipFile;
43 result = zip->open(fileName, ZipFile::kOpenReadOnly);
44 if (result != NO_ERROR) {
45 if (result == NAME_NOT_FOUND)
46 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
47 else if (result == PERMISSION_DENIED)
48 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
49 else
50 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
51 fileName);
52 delete zip;
53 return NULL;
54 }
55
56 return zip;
57 }
58
59 /*
60 * Open the file read-write. The file will be created if it doesn't
61 * already exist and "okayToCreate" is set.
62 *
63 * Returns NULL on failure.
64 */
65 ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
66 {
67 ZipFile* zip = NULL;
68 status_t result;
69 int flags;
70
71 flags = ZipFile::kOpenReadWrite;
72 if (okayToCreate)
73 flags |= ZipFile::kOpenCreate;
74
75 zip = new ZipFile;
76 result = zip->open(fileName, flags);
77 if (result != NO_ERROR) {
78 delete zip;
79 zip = NULL;
80 goto bail;
81 }
82
83 bail:
84 return zip;
85 }
86
87
88 /*
89 * Return a short string describing the compression method.
90 */
91 const char* compressionName(int method)
92 {
93 if (method == ZipEntry::kCompressStored)
94 return "Stored";
95 else if (method == ZipEntry::kCompressDeflated)
96 return "Deflated";
97 else
98 return "Unknown";
99 }
100
101 /*
102 * Return the percent reduction in size (0% == no compression).
103 */
104 int calcPercent(long uncompressedLen, long compressedLen)
105 {
106 if (!uncompressedLen)
107 return 0;
108 else
109 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
110 }
111
112 /*
113 * Handle the "list" command, which can be a simple file dump or
114 * a verbose listing.
115 *
116 * The verbose listing closely matches the output of the Info-ZIP "unzip"
117 * command.
118 */
119 int doList(Bundle* bundle)
120 {
121 int result = 1;
122 ZipFile* zip = NULL;
123 const ZipEntry* entry;
124 long totalUncLen, totalCompLen;
125 const char* zipFileName;
126
127 if (bundle->getFileSpecCount() != 1) {
128 fprintf(stderr, "ERROR: specify zip file name (only)\n");
129 goto bail;
130 }
131 zipFileName = bundle->getFileSpecEntry(0);
132
133 zip = openReadOnly(zipFileName);
134 if (zip == NULL)
135 goto bail;
136
137 int count, i;
138
139 if (bundle->getVerbose()) {
140 printf("Archive: %s\n", zipFileName);
141 printf(
142 " Length Method Size Ratio Date Time CRC-32 Name\n");
143 printf(
144 "-------- ------ ------- ----- ---- ---- ------ ----\n");
145 }
146
147 totalUncLen = totalCompLen = 0;
148
149 count = zip->getNumEntries();
150 for (i = 0; i < count; i++) {
151 entry = zip->getEntryByIndex(i);
152 if (bundle->getVerbose()) {
153 char dateBuf[32];
154 time_t when;
155
156 when = entry->getModWhen();
157 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
158 localtime(&when));
159
160 printf("%8ld %-7.7s %7ld %3d%% %s %08lx %s\n",
161 (long) entry->getUncompressedLen(),
162 compressionName(entry->getCompressionMethod()),
163 (long) entry->getCompressedLen(),
164 calcPercent(entry->getUncompressedLen(),
165 entry->getCompressedLen()),
166 dateBuf,
167 entry->getCRC32(),
168 entry->getFileName());
169 } else {
170 printf("%s\n", entry->getFileName());
171 }
172
173 totalUncLen += entry->getUncompressedLen();
174 totalCompLen += entry->getCompressedLen();
175 }
176
177 if (bundle->getVerbose()) {
178 printf(
179 "-------- ------- --- -------\n");
180 printf("%8ld %7ld %2d%% %d files\n",
181 totalUncLen,
182 totalCompLen,
183 calcPercent(totalUncLen, totalCompLen),
184 zip->getNumEntries());
185 }
186
187 if (bundle->getAndroidList()) {
188 AssetManager assets;
189 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
190 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
191 goto bail;
192 }
193
194 const ResTable& res = assets.getResources(false);
195 if (&res == NULL) {
196 printf("\nNo resource table found.\n");
197 } else {
198 printf("\nResource table:\n");
199 res.print();
200 }
201
202 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
203 Asset::ACCESS_BUFFER);
204 if (manifestAsset == NULL) {
205 printf("\nNo AndroidManifest.xml found.\n");
206 } else {
207 printf("\nAndroid manifest:\n");
208 ResXMLTree tree;
209 tree.setTo(manifestAsset->getBuffer(true),
210 manifestAsset->getLength());
211 printXMLBlock(&tree);
212 }
213 delete manifestAsset;
214 }
215
216 result = 0;
217
218 bail:
219 delete zip;
220 return result;
221 }
222
223 static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
224 {
225 size_t N = tree.getAttributeCount();
226 for (size_t i=0; i<N; i++) {
227 if (tree.getAttributeNameResID(i) == attrRes) {
228 return (ssize_t)i;
229 }
230 }
231 return -1;
232 }
233
234 static String8 getAttribute(const ResXMLTree& tree, const char* ns,
235 const char* attr, String8* outError)
236 {
237 ssize_t idx = tree.indexOfAttribute(ns, attr);
238 if (idx < 0) {
239 return String8();
240 }
241 Res_value value;
242 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
243 if (value.dataType != Res_value::TYPE_STRING) {
244 if (outError != NULL) *outError = "attribute is not a string value";
245 return String8();
246 }
247 }
248 size_t len;
249 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
250 return str ? String8(str, len) : String8();
251 }
252
253 static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
254 {
255 ssize_t idx = indexOfAttribute(tree, attrRes);
256 if (idx < 0) {
257 return String8();
258 }
259 Res_value value;
260 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
261 if (value.dataType != Res_value::TYPE_STRING) {
262 if (outError != NULL) *outError = "attribute is not a string value";
263 return String8();
264 }
265 }
266 size_t len;
267 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
268 return str ? String8(str, len) : String8();
269 }
270
271 static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
272 {
273 ssize_t idx = indexOfAttribute(tree, attrRes);
274 if (idx < 0) {
275 return -1;
276 }
277 Res_value value;
278 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
279 if (value.dataType != Res_value::TYPE_INT_DEC) {
280 if (outError != NULL) *outError = "attribute is not an integer value";
281 return -1;
282 }
283 }
284 return value.data;
285 }
286
287 static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
288 uint32_t attrRes, String8* outError)
289 {
290 ssize_t idx = indexOfAttribute(tree, attrRes);
291 if (idx < 0) {
292 return String8();
293 }
294 Res_value value;
295 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
296 if (value.dataType == Res_value::TYPE_STRING) {
297 size_t len;
298 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
299 return str ? String8(str, len) : String8();
300 }
301 resTable->resolveReference(&value, 0);
302 if (value.dataType != Res_value::TYPE_STRING) {
303 if (outError != NULL) *outError = "attribute is not a string value";
304 return String8();
305 }
306 }
307 size_t len;
308 const Res_value* value2 = &value;
309 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
310 return str ? String8(str, len) : String8();
311 }
312
313 // These are attribute resource constants for the platform, as found
314 // in android.R.attr
315 enum {
316 NAME_ATTR = 0x01010003,
317 VERSION_CODE_ATTR = 0x0101021b,
318 VERSION_NAME_ATTR = 0x0101021c,
319 LABEL_ATTR = 0x01010001,
320 ICON_ATTR = 0x01010002,
321 };
322
323 /*
324 * Handle the "dump" command, to extract select data from an archive.
325 */
326 int doDump(Bundle* bundle)
327 {
328 status_t result = UNKNOWN_ERROR;
329 Asset* asset = NULL;
330
331 if (bundle->getFileSpecCount() < 1) {
332 fprintf(stderr, "ERROR: no dump option specified\n");
333 return 1;
334 }
335
336 if (bundle->getFileSpecCount() < 2) {
337 fprintf(stderr, "ERROR: no dump file specified\n");
338 return 1;
339 }
340
341 const char* option = bundle->getFileSpecEntry(0);
342 const char* filename = bundle->getFileSpecEntry(1);
343
344 AssetManager assets;
345 if (!assets.addAssetPath(String8(filename), NULL)) {
346 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
347 return 1;
348 }
349
350 const ResTable& res = assets.getResources(false);
351 if (&res == NULL) {
352 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
353 goto bail;
354 }
355
356 if (strcmp("resources", option) == 0) {
357 res.print();
358
359 } else if (strcmp("xmltree", option) == 0) {
360 if (bundle->getFileSpecCount() < 3) {
361 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
362 goto bail;
363 }
364
365 for (int i=2; i<bundle->getFileSpecCount(); i++) {
366 const char* resname = bundle->getFileSpecEntry(i);
367 ResXMLTree tree;
368 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
369 if (asset == NULL) {
370 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
371 goto bail;
372 }
373
374 if (tree.setTo(asset->getBuffer(true),
375 asset->getLength()) != NO_ERROR) {
376 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
377 goto bail;
378 }
379 tree.restart();
380 printXMLBlock(&tree);
381 delete asset;
382 asset = NULL;
383 }
384
385 } else if (strcmp("xmlstrings", option) == 0) {
386 if (bundle->getFileSpecCount() < 3) {
387 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
388 goto bail;
389 }
390
391 for (int i=2; i<bundle->getFileSpecCount(); i++) {
392 const char* resname = bundle->getFileSpecEntry(i);
393 ResXMLTree tree;
394 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
395 if (asset == NULL) {
396 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
397 goto bail;
398 }
399
400 if (tree.setTo(asset->getBuffer(true),
401 asset->getLength()) != NO_ERROR) {
402 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
403 goto bail;
404 }
405 printStringPool(&tree.getStrings());
406 delete asset;
407 asset = NULL;
408 }
409
410 } else {
411 ResXMLTree tree;
412 asset = assets.openNonAsset("AndroidManifest.xml",
413 Asset::ACCESS_BUFFER);
414 if (asset == NULL) {
415 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
416 goto bail;
417 }
418
419 if (tree.setTo(asset->getBuffer(true),
420 asset->getLength()) != NO_ERROR) {
421 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
422 goto bail;
423 }
424 tree.restart();
425
426 if (strcmp("permissions", option) == 0) {
427 size_t len;
428 ResXMLTree::event_code_t code;
429 int depth = 0;
430 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
431 if (code == ResXMLTree::END_TAG) {
432 depth--;
433 continue;
434 }
435 if (code != ResXMLTree::START_TAG) {
436 continue;
437 }
438 depth++;
439 String8 tag(tree.getElementName(&len));
440 //printf("Depth %d tag %s\n", depth, tag.string());
441 if (depth == 1) {
442 if (tag != "manifest") {
443 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
444 goto bail;
445 }
446 String8 pkg = getAttribute(tree, NULL, "package", NULL);
447 printf("package: %s\n", pkg.string());
448 } else if (depth == 2 && tag == "permission") {
449 String8 error;
450 String8 name = getAttribute(tree, NAME_ATTR, &error);
451 if (error != "") {
452 fprintf(stderr, "ERROR: %s\n", error.string());
453 goto bail;
454 }
455 printf("permission: %s\n", name.string());
456 } else if (depth == 2 && tag == "uses-permission") {
457 String8 error;
458 String8 name = getAttribute(tree, NAME_ATTR, &error);
459 if (error != "") {
460 fprintf(stderr, "ERROR: %s\n", error.string());
461 goto bail;
462 }
463 printf("uses-permission: %s\n", name.string());
464 }
465 }
466 } else if (strcmp("badging", option) == 0) {
467 size_t len;
468 ResXMLTree::event_code_t code;
469 int depth = 0;
470 String8 error;
471 bool withinActivity = false;
472 bool isMainActivity = false;
473 bool isLauncherActivity = false;
474 String8 activityName;
475 String8 activityLabel;
476 String8 activityIcon;
477 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
478 if (code == ResXMLTree::END_TAG) {
479 depth--;
480 continue;
481 }
482 if (code != ResXMLTree::START_TAG) {
483 continue;
484 }
485 depth++;
486 String8 tag(tree.getElementName(&len));
487 //printf("Depth %d tag %s\n", depth, tag.string());
488 if (depth == 1) {
489 if (tag != "manifest") {
490 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
491 goto bail;
492 }
493 String8 pkg = getAttribute(tree, NULL, "package", NULL);
494 printf("package: name='%s' ", pkg.string());
495 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
496 if (error != "") {
497 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
498 goto bail;
499 }
500 if (versionCode > 0) {
501 printf("versionCode='%d' ", versionCode);
502 } else {
503 printf("versionCode='' ");
504 }
505 String8 versionName = getAttribute(tree, VERSION_NAME_ATTR, &error);
506 if (error != "") {
507 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
508 goto bail;
509 }
510 printf("versionName='%s'\n", versionName.string());
511 } else if (depth == 2 && tag == "application") {
512 String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
513 if (error != "") {
514 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
515 goto bail;
516 }
517 printf("application: label='%s' ", label.string());
518
519 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
520 if (error != "") {
521 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
522 goto bail;
523 }
524 printf("icon='%s'\n", icon.string());
525 } else if (depth == 3 && tag == "activity") {
526 withinActivity = true;
527 //printf("LOG: withinActivity==true\n");
528
529 activityName = getAttribute(tree, NAME_ATTR, &error);
530 if (error != "") {
531 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
532 goto bail;
533 }
534
535 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
536 if (error != "") {
537 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
538 goto bail;
539 }
540
541 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
542 if (error != "") {
543 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
544 goto bail;
545 }
546 } else if (depth == 5 && withinActivity) {
547 if (tag == "action") {
548 //printf("LOG: action tag\n");
549 String8 action = getAttribute(tree, NAME_ATTR, &error);
550 if (error != "") {
551 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
552 goto bail;
553 }
554 if (action == "android.intent.action.MAIN") {
555 isMainActivity = true;
556 //printf("LOG: isMainActivity==true\n");
557 }
558 } else if (tag == "category") {
559 String8 category = getAttribute(tree, NAME_ATTR, &error);
560 if (error != "") {
561 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
562 goto bail;
563 }
564 if (category == "android.intent.category.LAUNCHER") {
565 isLauncherActivity = true;
566 //printf("LOG: isLauncherActivity==true\n");
567 }
568 }
569 }
570
571 if (depth < 3) {
572 //if (withinActivity) printf("LOG: withinActivity==false\n");
573 withinActivity = false;
574 }
575
576 if (depth < 5) {
577 //if (isMainActivity) printf("LOG: isMainActivity==false\n");
578 //if (isLauncherActivity) printf("LOG: isLauncherActivity==false\n");
579 isMainActivity = false;
580 isLauncherActivity = false;
581 }
582
583 if (withinActivity && isMainActivity && isLauncherActivity) {
584 printf("launchable activity: name='%s' label='%s' icon='%s'\n",
585 activityName.string(), activityLabel.string(),
586 activityIcon.string());
587 }
588 }
589 printf("locales:");
590 Vector<String8> locales;
591 res.getLocales(&locales);
592 const size_t N = locales.size();
593 for (size_t i=0; i<N; i++) {
594 const char* localeStr = locales[i].string();
595 if (localeStr == NULL || strlen(localeStr) == 0) {
596 localeStr = "--_--";
597 }
598 printf(" '%s'", localeStr);
599 }
600 printf("\n");
601 } else if (strcmp("configurations", option) == 0) {
602 Vector<ResTable_config> configs;
603 res.getConfigurations(&configs);
604 const size_t N = configs.size();
605 for (size_t i=0; i<N; i++) {
606 printf("%s\n", configs[i].toString().string());
607 }
608 } else {
609 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
610 goto bail;
611 }
612 }
613
614 result = NO_ERROR;
615
616 bail:
617 if (asset) {
618 delete asset;
619 }
620 return (result != NO_ERROR);
621 }
622
623
624 /*
625 * Handle the "add" command, which wants to add files to a new or
626 * pre-existing archive.
627 */
628 int doAdd(Bundle* bundle)
629 {
630 ZipFile* zip = NULL;
631 status_t result = UNKNOWN_ERROR;
632 const char* zipFileName;
633
634 if (bundle->getUpdate()) {
635 /* avoid confusion */
636 fprintf(stderr, "ERROR: can't use '-u' with add\n");
637 goto bail;
638 }
639
640 if (bundle->getFileSpecCount() < 1) {
641 fprintf(stderr, "ERROR: must specify zip file name\n");
642 goto bail;
643 }
644 zipFileName = bundle->getFileSpecEntry(0);
645
646 if (bundle->getFileSpecCount() < 2) {
647 fprintf(stderr, "NOTE: nothing to do\n");
648 goto bail;
649 }
650
651 zip = openReadWrite(zipFileName, true);
652 if (zip == NULL) {
653 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
654 goto bail;
655 }
656
657 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
658 const char* fileName = bundle->getFileSpecEntry(i);
659
660 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
661 printf(" '%s'... (from gzip)\n", fileName);
662 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
663 } else {
664 printf(" '%s'...\n", fileName);
665 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
666 }
667 if (result != NO_ERROR) {
668 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
669 if (result == NAME_NOT_FOUND)
670 fprintf(stderr, ": file not found\n");
671 else if (result == ALREADY_EXISTS)
672 fprintf(stderr, ": already exists in archive\n");
673 else
674 fprintf(stderr, "\n");
675 goto bail;
676 }
677 }
678
679 result = NO_ERROR;
680
681 bail:
682 delete zip;
683 return (result != NO_ERROR);
684 }
685
686
687 /*
688 * Delete files from an existing archive.
689 */
690 int doRemove(Bundle* bundle)
691 {
692 ZipFile* zip = NULL;
693 status_t result = UNKNOWN_ERROR;
694 const char* zipFileName;
695
696 if (bundle->getFileSpecCount() < 1) {
697 fprintf(stderr, "ERROR: must specify zip file name\n");
698 goto bail;
699 }
700 zipFileName = bundle->getFileSpecEntry(0);
701
702 if (bundle->getFileSpecCount() < 2) {
703 fprintf(stderr, "NOTE: nothing to do\n");
704 goto bail;
705 }
706
707 zip = openReadWrite(zipFileName, false);
708 if (zip == NULL) {
709 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
710 zipFileName);
711 goto bail;
712 }
713
714 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
715 const char* fileName = bundle->getFileSpecEntry(i);
716 ZipEntry* entry;
717
718 entry = zip->getEntryByName(fileName);
719 if (entry == NULL) {
720 printf(" '%s' NOT FOUND\n", fileName);
721 continue;
722 }
723
724 result = zip->remove(entry);
725
726 if (result != NO_ERROR) {
727 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
728 bundle->getFileSpecEntry(i), zipFileName);
729 goto bail;
730 }
731 }
732
733 /* update the archive */
734 zip->flush();
735
736 bail:
737 delete zip;
738 return (result != NO_ERROR);
739 }
740
741
742 /*
743 * Package up an asset directory and associated application files.
744 */
745 int doPackage(Bundle* bundle)
746 {
747 const char* outputAPKFile;
748 int retVal = 1;
749 status_t err;
750 sp<AaptAssets> assets;
751 int N;
752
753 // -c zz_ZZ means do pseudolocalization
754 ResourceFilter filter;
755 err = filter.parse(bundle->getConfigurations());
756 if (err != NO_ERROR) {
757 goto bail;
758 }
759 if (filter.containsPseudo()) {
760 bundle->setPseudolocalize(true);
761 }
762
763 N = bundle->getFileSpecCount();
764 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
765 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
766 fprintf(stderr, "ERROR: no input files\n");
767 goto bail;
768 }
769
770 outputAPKFile = bundle->getOutputAPKFile();
771
772 // Make sure the filenames provided exist and are of the appropriate type.
773 if (outputAPKFile) {
774 FileType type;
775 type = getFileType(outputAPKFile);
776 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
777 fprintf(stderr,
778 "ERROR: output file '%s' exists but is not regular file\n",
779 outputAPKFile);
780 goto bail;
781 }
782 }
783
784 // Load the assets.
785 assets = new AaptAssets();
786 err = assets->slurpFromArgs(bundle);
787 if (err < 0) {
788 goto bail;
789 }
790
791 if (bundle->getVerbose()) {
792 assets->print();
793 }
794
795 // If they asked for any files that need to be compiled, do so.
796 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
797 err = buildResources(bundle, assets);
798 if (err != 0) {
799 goto bail;
800 }
801 }
802
803 // At this point we've read everything and processed everything. From here
804 // on out it's just writing output files.
805 if (SourcePos::hasErrors()) {
806 goto bail;
807 }
808
809 // Write out R.java constants
810 if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
811 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
812 if (err < 0) {
813 goto bail;
814 }
815 } else {
816 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
817 if (err < 0) {
818 goto bail;
819 }
820 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
821 if (err < 0) {
822 goto bail;
823 }
824 }
825
826 // Write the apk
827 if (outputAPKFile) {
828 err = writeAPK(bundle, assets, String8(outputAPKFile));
829 if (err != NO_ERROR) {
830 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
831 goto bail;
832 }
833 }
834
835 retVal = 0;
836 bail:
837 if (SourcePos::hasErrors()) {
838 SourcePos::printErrors(stderr);
839 }
840 return retVal;
841 }