Resource.cpp revision 4c488ff931fe6b2fc79ef854febc98b4cb748402
1// 2// Copyright 2006 The Android Open Source Project 3// 4// Build resource files from raw assets. 5// 6#include "AaptAssets.h" 7#include "AaptUtil.h" 8#include "AaptXml.h" 9#include "CacheUpdater.h" 10#include "CrunchCache.h" 11#include "FileFinder.h" 12#include "Images.h" 13#include "IndentPrinter.h" 14#include "Main.h" 15#include "ResourceTable.h" 16#include "StringPool.h" 17#include "Symbol.h" 18#include "WorkQueue.h" 19#include "XMLNode.h" 20 21#include <algorithm> 22 23#if HAVE_PRINTF_ZD 24# define ZD "%zd" 25# define ZD_TYPE ssize_t 26#else 27# define ZD "%ld" 28# define ZD_TYPE long 29#endif 30 31#define NOISY(x) // x 32 33// Number of threads to use for preprocessing images. 34static const size_t MAX_THREADS = 4; 35 36// ========================================================================== 37// ========================================================================== 38// ========================================================================== 39 40class PackageInfo 41{ 42public: 43 PackageInfo() 44 { 45 } 46 ~PackageInfo() 47 { 48 } 49 50 status_t parsePackage(const sp<AaptGroup>& grp); 51}; 52 53// ========================================================================== 54// ========================================================================== 55// ========================================================================== 56 57String8 parseResourceName(const String8& leaf) 58{ 59 const char* firstDot = strchr(leaf.string(), '.'); 60 const char* str = leaf.string(); 61 62 if (firstDot) { 63 return String8(str, firstDot-str); 64 } else { 65 return String8(str); 66 } 67} 68 69ResourceTypeSet::ResourceTypeSet() 70 :RefBase(), 71 KeyedVector<String8,sp<AaptGroup> >() 72{ 73} 74 75FilePathStore::FilePathStore() 76 :RefBase(), 77 Vector<String8>() 78{ 79} 80 81class ResourceDirIterator 82{ 83public: 84 ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType) 85 : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0) 86 { 87 memset(&mParams, 0, sizeof(ResTable_config)); 88 } 89 90 inline const sp<AaptGroup>& getGroup() const { return mGroup; } 91 inline const sp<AaptFile>& getFile() const { return mFile; } 92 93 inline const String8& getBaseName() const { return mBaseName; } 94 inline const String8& getLeafName() const { return mLeafName; } 95 inline String8 getPath() const { return mPath; } 96 inline const ResTable_config& getParams() const { return mParams; } 97 98 enum { 99 EOD = 1 100 }; 101 102 ssize_t next() 103 { 104 while (true) { 105 sp<AaptGroup> group; 106 sp<AaptFile> file; 107 108 // Try to get next file in this current group. 109 if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) { 110 group = mGroup; 111 file = group->getFiles().valueAt(mGroupPos++); 112 113 // Try to get the next group/file in this directory 114 } else if (mSetPos < mSet->size()) { 115 mGroup = group = mSet->valueAt(mSetPos++); 116 if (group->getFiles().size() < 1) { 117 continue; 118 } 119 file = group->getFiles().valueAt(0); 120 mGroupPos = 1; 121 122 // All done! 123 } else { 124 return EOD; 125 } 126 127 mFile = file; 128 129 String8 leaf(group->getLeaf()); 130 mLeafName = String8(leaf); 131 mParams = file->getGroupEntry().toParams(); 132 NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n", 133 group->getPath().string(), mParams.mcc, mParams.mnc, 134 mParams.language[0] ? mParams.language[0] : '-', 135 mParams.language[1] ? mParams.language[1] : '-', 136 mParams.country[0] ? mParams.country[0] : '-', 137 mParams.country[1] ? mParams.country[1] : '-', 138 mParams.orientation, mParams.uiMode, 139 mParams.density, mParams.touchscreen, mParams.keyboard, 140 mParams.inputFlags, mParams.navigation)); 141 mPath = "res"; 142 mPath.appendPath(file->getGroupEntry().toDirName(mResType)); 143 mPath.appendPath(leaf); 144 mBaseName = parseResourceName(leaf); 145 if (mBaseName == "") { 146 fprintf(stderr, "Error: malformed resource filename %s\n", 147 file->getPrintableSource().string()); 148 return UNKNOWN_ERROR; 149 } 150 151 NOISY(printf("file name=%s\n", mBaseName.string())); 152 153 return NO_ERROR; 154 } 155 } 156 157private: 158 String8 mResType; 159 160 const sp<ResourceTypeSet> mSet; 161 size_t mSetPos; 162 163 sp<AaptGroup> mGroup; 164 size_t mGroupPos; 165 166 sp<AaptFile> mFile; 167 String8 mBaseName; 168 String8 mLeafName; 169 String8 mPath; 170 ResTable_config mParams; 171}; 172 173class AnnotationProcessor { 174public: 175 AnnotationProcessor() : mDeprecated(false), mSystemApi(false) { } 176 177 void preprocessComment(String8& comment) { 178 if (comment.size() > 0) { 179 if (comment.contains("@deprecated")) { 180 mDeprecated = true; 181 } 182 if (comment.removeAll("@SystemApi")) { 183 mSystemApi = true; 184 } 185 } 186 } 187 188 void printAnnotations(FILE* fp, const char* indentStr) { 189 if (mDeprecated) { 190 fprintf(fp, "%s@Deprecated\n", indentStr); 191 } 192 if (mSystemApi) { 193 fprintf(fp, "%s@android.annotation.SystemApi\n", indentStr); 194 } 195 } 196 197private: 198 bool mDeprecated; 199 bool mSystemApi; 200}; 201 202// ========================================================================== 203// ========================================================================== 204// ========================================================================== 205 206bool isValidResourceType(const String8& type) 207{ 208 return type == "anim" || type == "animator" || type == "interpolator" 209 || type == "transition" 210 || type == "drawable" || type == "layout" 211 || type == "values" || type == "xml" || type == "raw" 212 || type == "color" || type == "menu" || type == "mipmap"; 213} 214 215static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets, 216 const sp<AaptGroup>& grp) 217{ 218 if (grp->getFiles().size() != 1) { 219 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 220 grp->getFiles().valueAt(0)->getPrintableSource().string()); 221 } 222 223 sp<AaptFile> file = grp->getFiles().valueAt(0); 224 225 ResXMLTree block; 226 status_t err = parseXMLResource(file, &block); 227 if (err != NO_ERROR) { 228 return err; 229 } 230 //printXMLBlock(&block); 231 232 ResXMLTree::event_code_t code; 233 while ((code=block.next()) != ResXMLTree::START_TAG 234 && code != ResXMLTree::END_DOCUMENT 235 && code != ResXMLTree::BAD_DOCUMENT) { 236 } 237 238 size_t len; 239 if (code != ResXMLTree::START_TAG) { 240 fprintf(stderr, "%s:%d: No start tag found\n", 241 file->getPrintableSource().string(), block.getLineNumber()); 242 return UNKNOWN_ERROR; 243 } 244 if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) { 245 fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n", 246 file->getPrintableSource().string(), block.getLineNumber(), 247 String8(block.getElementName(&len)).string()); 248 return UNKNOWN_ERROR; 249 } 250 251 ssize_t nameIndex = block.indexOfAttribute(NULL, "package"); 252 if (nameIndex < 0) { 253 fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n", 254 file->getPrintableSource().string(), block.getLineNumber()); 255 return UNKNOWN_ERROR; 256 } 257 258 assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len))); 259 260 String16 uses_sdk16("uses-sdk"); 261 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 262 && code != ResXMLTree::BAD_DOCUMENT) { 263 if (code == ResXMLTree::START_TAG) { 264 if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) { 265 ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, 266 "minSdkVersion"); 267 if (minSdkIndex >= 0) { 268 const char16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len); 269 const char* minSdk8 = strdup(String8(minSdk16).string()); 270 bundle->setManifestMinSdkVersion(minSdk8); 271 } 272 } 273 } 274 } 275 276 return NO_ERROR; 277} 278 279// ========================================================================== 280// ========================================================================== 281// ========================================================================== 282 283static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets, 284 ResourceTable* table, 285 const sp<ResourceTypeSet>& set, 286 const char* resType) 287{ 288 String8 type8(resType); 289 String16 type16(resType); 290 291 bool hasErrors = false; 292 293 ResourceDirIterator it(set, String8(resType)); 294 ssize_t res; 295 while ((res=it.next()) == NO_ERROR) { 296 if (bundle->getVerbose()) { 297 printf(" (new resource id %s from %s)\n", 298 it.getBaseName().string(), it.getFile()->getPrintableSource().string()); 299 } 300 String16 baseName(it.getBaseName()); 301 const char16_t* str = baseName.string(); 302 const char16_t* const end = str + baseName.size(); 303 while (str < end) { 304 if (!((*str >= 'a' && *str <= 'z') 305 || (*str >= '0' && *str <= '9') 306 || *str == '_' || *str == '.')) { 307 fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n", 308 it.getPath().string()); 309 hasErrors = true; 310 } 311 str++; 312 } 313 String8 resPath = it.getPath(); 314 resPath.convertToResPath(); 315 table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()), 316 type16, 317 baseName, 318 String16(resPath), 319 NULL, 320 &it.getParams()); 321 assets->addResource(it.getLeafName(), resPath, it.getFile(), type8); 322 } 323 324 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 325} 326 327class PreProcessImageWorkUnit : public WorkQueue::WorkUnit { 328public: 329 PreProcessImageWorkUnit(const Bundle* bundle, const sp<AaptAssets>& assets, 330 const sp<AaptFile>& file, volatile bool* hasErrors) : 331 mBundle(bundle), mAssets(assets), mFile(file), mHasErrors(hasErrors) { 332 } 333 334 virtual bool run() { 335 status_t status = preProcessImage(mBundle, mAssets, mFile, NULL); 336 if (status) { 337 *mHasErrors = true; 338 } 339 return true; // continue even if there are errors 340 } 341 342private: 343 const Bundle* mBundle; 344 sp<AaptAssets> mAssets; 345 sp<AaptFile> mFile; 346 volatile bool* mHasErrors; 347}; 348 349static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& assets, 350 const sp<ResourceTypeSet>& set, const char* type) 351{ 352 volatile bool hasErrors = false; 353 ssize_t res = NO_ERROR; 354 if (bundle->getUseCrunchCache() == false) { 355 WorkQueue wq(MAX_THREADS, false); 356 ResourceDirIterator it(set, String8(type)); 357 while ((res=it.next()) == NO_ERROR) { 358 PreProcessImageWorkUnit* w = new PreProcessImageWorkUnit( 359 bundle, assets, it.getFile(), &hasErrors); 360 status_t status = wq.schedule(w); 361 if (status) { 362 fprintf(stderr, "preProcessImages failed: schedule() returned %d\n", status); 363 hasErrors = true; 364 delete w; 365 break; 366 } 367 } 368 status_t status = wq.finish(); 369 if (status) { 370 fprintf(stderr, "preProcessImages failed: finish() returned %d\n", status); 371 hasErrors = true; 372 } 373 } 374 return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; 375} 376 377static void collect_files(const sp<AaptDir>& dir, 378 KeyedVector<String8, sp<ResourceTypeSet> >* resources) 379{ 380 const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles(); 381 int N = groups.size(); 382 for (int i=0; i<N; i++) { 383 String8 leafName = groups.keyAt(i); 384 const sp<AaptGroup>& group = groups.valueAt(i); 385 386 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files 387 = group->getFiles(); 388 389 if (files.size() == 0) { 390 continue; 391 } 392 393 String8 resType = files.valueAt(0)->getResourceType(); 394 395 ssize_t index = resources->indexOfKey(resType); 396 397 if (index < 0) { 398 sp<ResourceTypeSet> set = new ResourceTypeSet(); 399 NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n", 400 leafName.string(), group->getPath().string(), group.get())); 401 set->add(leafName, group); 402 resources->add(resType, set); 403 } else { 404 sp<ResourceTypeSet> set = resources->valueAt(index); 405 index = set->indexOfKey(leafName); 406 if (index < 0) { 407 NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n", 408 leafName.string(), group->getPath().string(), group.get())); 409 set->add(leafName, group); 410 } else { 411 sp<AaptGroup> existingGroup = set->valueAt(index); 412 NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n", 413 leafName.string(), group->getPath().string(), group.get())); 414 for (size_t j=0; j<files.size(); j++) { 415 NOISY(printf("Adding file %s in group %s resType %s\n", 416 files.valueAt(j)->getSourceFile().string(), 417 files.keyAt(j).toDirName(String8()).string(), 418 resType.string())); 419 status_t err = existingGroup->addFile(files.valueAt(j)); 420 } 421 } 422 } 423 } 424} 425 426static void collect_files(const sp<AaptAssets>& ass, 427 KeyedVector<String8, sp<ResourceTypeSet> >* resources) 428{ 429 const Vector<sp<AaptDir> >& dirs = ass->resDirs(); 430 int N = dirs.size(); 431 432 for (int i=0; i<N; i++) { 433 sp<AaptDir> d = dirs.itemAt(i); 434 NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(), 435 d->getLeaf().string())); 436 collect_files(d, resources); 437 438 // don't try to include the res dir 439 NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string())); 440 ass->removeDir(d->getLeaf()); 441 } 442} 443 444enum { 445 ATTR_OKAY = -1, 446 ATTR_NOT_FOUND = -2, 447 ATTR_LEADING_SPACES = -3, 448 ATTR_TRAILING_SPACES = -4 449}; 450static int validateAttr(const String8& path, const ResTable& table, 451 const ResXMLParser& parser, 452 const char* ns, const char* attr, const char* validChars, bool required) 453{ 454 size_t len; 455 456 ssize_t index = parser.indexOfAttribute(ns, attr); 457 const char16_t* str; 458 Res_value value; 459 if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) { 460 const ResStringPool* pool = &parser.getStrings(); 461 if (value.dataType == Res_value::TYPE_REFERENCE) { 462 uint32_t specFlags = 0; 463 int strIdx; 464 if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) { 465 fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n", 466 path.string(), parser.getLineNumber(), 467 String8(parser.getElementName(&len)).string(), attr, 468 value.data); 469 return ATTR_NOT_FOUND; 470 } 471 472 pool = table.getTableStringBlock(strIdx); 473 #if 0 474 if (pool != NULL) { 475 str = pool->stringAt(value.data, &len); 476 } 477 printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr, 478 specFlags, strIdx, str != NULL ? String8(str).string() : "???"); 479 #endif 480 if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) { 481 fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n", 482 path.string(), parser.getLineNumber(), 483 String8(parser.getElementName(&len)).string(), attr, 484 specFlags); 485 return ATTR_NOT_FOUND; 486 } 487 } 488 if (value.dataType == Res_value::TYPE_STRING) { 489 if (pool == NULL) { 490 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n", 491 path.string(), parser.getLineNumber(), 492 String8(parser.getElementName(&len)).string(), attr); 493 return ATTR_NOT_FOUND; 494 } 495 if ((str=pool->stringAt(value.data, &len)) == NULL) { 496 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n", 497 path.string(), parser.getLineNumber(), 498 String8(parser.getElementName(&len)).string(), attr); 499 return ATTR_NOT_FOUND; 500 } 501 } else { 502 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n", 503 path.string(), parser.getLineNumber(), 504 String8(parser.getElementName(&len)).string(), attr, 505 value.dataType); 506 return ATTR_NOT_FOUND; 507 } 508 if (validChars) { 509 for (size_t i=0; i<len; i++) { 510 char16_t c = str[i]; 511 const char* p = validChars; 512 bool okay = false; 513 while (*p) { 514 if (c == *p) { 515 okay = true; 516 break; 517 } 518 p++; 519 } 520 if (!okay) { 521 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n", 522 path.string(), parser.getLineNumber(), 523 String8(parser.getElementName(&len)).string(), attr, (char)str[i]); 524 return (int)i; 525 } 526 } 527 } 528 if (*str == ' ') { 529 fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n", 530 path.string(), parser.getLineNumber(), 531 String8(parser.getElementName(&len)).string(), attr); 532 return ATTR_LEADING_SPACES; 533 } 534 if (str[len-1] == ' ') { 535 fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n", 536 path.string(), parser.getLineNumber(), 537 String8(parser.getElementName(&len)).string(), attr); 538 return ATTR_TRAILING_SPACES; 539 } 540 return ATTR_OKAY; 541 } 542 if (required) { 543 fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n", 544 path.string(), parser.getLineNumber(), 545 String8(parser.getElementName(&len)).string(), attr); 546 return ATTR_NOT_FOUND; 547 } 548 return ATTR_OKAY; 549} 550 551static void checkForIds(const String8& path, ResXMLParser& parser) 552{ 553 ResXMLTree::event_code_t code; 554 while ((code=parser.next()) != ResXMLTree::END_DOCUMENT 555 && code > ResXMLTree::BAD_DOCUMENT) { 556 if (code == ResXMLTree::START_TAG) { 557 ssize_t index = parser.indexOfAttribute(NULL, "id"); 558 if (index >= 0) { 559 fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n", 560 path.string(), parser.getLineNumber()); 561 } 562 } 563 } 564} 565 566static bool applyFileOverlay(Bundle *bundle, 567 const sp<AaptAssets>& assets, 568 sp<ResourceTypeSet> *baseSet, 569 const char *resType) 570{ 571 if (bundle->getVerbose()) { 572 printf("applyFileOverlay for %s\n", resType); 573 } 574 575 // Replace any base level files in this category with any found from the overlay 576 // Also add any found only in the overlay. 577 sp<AaptAssets> overlay = assets->getOverlay(); 578 String8 resTypeString(resType); 579 580 // work through the linked list of overlays 581 while (overlay.get()) { 582 KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources(); 583 584 // get the overlay resources of the requested type 585 ssize_t index = overlayRes->indexOfKey(resTypeString); 586 if (index >= 0) { 587 sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index); 588 589 // for each of the resources, check for a match in the previously built 590 // non-overlay "baseset". 591 size_t overlayCount = overlaySet->size(); 592 for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) { 593 if (bundle->getVerbose()) { 594 printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string()); 595 } 596 ssize_t baseIndex = -1; 597 if (baseSet->get() != NULL) { 598 baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex)); 599 } 600 if (baseIndex >= 0) { 601 // look for same flavor. For a given file (strings.xml, for example) 602 // there may be a locale specific or other flavors - we want to match 603 // the same flavor. 604 sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex); 605 sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex); 606 607 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 608 overlayGroup->getFiles(); 609 if (bundle->getVerbose()) { 610 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles = 611 baseGroup->getFiles(); 612 for (size_t i=0; i < baseFiles.size(); i++) { 613 printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i, 614 baseFiles.keyAt(i).toString().string()); 615 } 616 for (size_t i=0; i < overlayFiles.size(); i++) { 617 printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i, 618 overlayFiles.keyAt(i).toString().string()); 619 } 620 } 621 622 size_t overlayGroupSize = overlayFiles.size(); 623 for (size_t overlayGroupIndex = 0; 624 overlayGroupIndex<overlayGroupSize; 625 overlayGroupIndex++) { 626 ssize_t baseFileIndex = 627 baseGroup->getFiles().indexOfKey(overlayFiles. 628 keyAt(overlayGroupIndex)); 629 if (baseFileIndex >= 0) { 630 if (bundle->getVerbose()) { 631 printf("found a match (" ZD ") for overlay file %s, for flavor %s\n", 632 (ZD_TYPE) baseFileIndex, 633 overlayGroup->getLeaf().string(), 634 overlayFiles.keyAt(overlayGroupIndex).toString().string()); 635 } 636 baseGroup->removeFile(baseFileIndex); 637 } else { 638 // didn't find a match fall through and add it.. 639 if (true || bundle->getVerbose()) { 640 printf("nothing matches overlay file %s, for flavor %s\n", 641 overlayGroup->getLeaf().string(), 642 overlayFiles.keyAt(overlayGroupIndex).toString().string()); 643 } 644 } 645 baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex)); 646 assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex)); 647 } 648 } else { 649 if (baseSet->get() == NULL) { 650 *baseSet = new ResourceTypeSet(); 651 assets->getResources()->add(String8(resType), *baseSet); 652 } 653 // this group doesn't exist (a file that's only in the overlay) 654 (*baseSet)->add(overlaySet->keyAt(overlayIndex), 655 overlaySet->valueAt(overlayIndex)); 656 // make sure all flavors are defined in the resources. 657 sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex); 658 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 659 overlayGroup->getFiles(); 660 size_t overlayGroupSize = overlayFiles.size(); 661 for (size_t overlayGroupIndex = 0; 662 overlayGroupIndex<overlayGroupSize; 663 overlayGroupIndex++) { 664 assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex)); 665 } 666 } 667 } 668 // this overlay didn't have resources for this type 669 } 670 // try next overlay 671 overlay = overlay->getOverlay(); 672 } 673 return true; 674} 675 676/* 677 * Inserts an attribute in a given node. 678 * If errorOnFailedInsert is true, and the attribute already exists, returns false. 679 * If replaceExisting is true, the attribute will be updated if it already exists. 680 * Returns true otherwise, even if the attribute already exists, and does not modify 681 * the existing attribute's value. 682 */ 683bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, 684 const char* attr8, const char* value, bool errorOnFailedInsert, 685 bool replaceExisting) 686{ 687 if (value == NULL) { 688 return true; 689 } 690 691 const String16 ns(ns8); 692 const String16 attr(attr8); 693 694 XMLNode::attribute_entry* existingEntry = node->editAttribute(ns, attr); 695 if (existingEntry != NULL) { 696 if (replaceExisting) { 697 NOISY(printf("Info: AndroidManifest.xml already defines %s (in %s);" 698 " overwriting existing value from manifest.\n", 699 String8(attr).string(), String8(ns).string())); 700 existingEntry->string = String16(value); 701 return true; 702 } 703 704 if (errorOnFailedInsert) { 705 fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);" 706 " cannot insert new value %s.\n", 707 String8(attr).string(), String8(ns).string(), value); 708 return false; 709 } 710 711 fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);" 712 " using existing value in manifest.\n", 713 String8(attr).string(), String8(ns).string()); 714 715 // don't stop the build. 716 return true; 717 } 718 719 node->addAttribute(ns, attr, String16(value)); 720 return true; 721} 722 723/* 724 * Inserts an attribute in a given node, only if the attribute does not 725 * exist. 726 * If errorOnFailedInsert is true, and the attribute already exists, returns false. 727 * Returns true otherwise, even if the attribute already exists. 728 */ 729bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, 730 const char* attr8, const char* value, bool errorOnFailedInsert) 731{ 732 return addTagAttribute(node, ns8, attr8, value, errorOnFailedInsert, false); 733} 734 735static void fullyQualifyClassName(const String8& package, sp<XMLNode> node, 736 const String16& attrName) { 737 XMLNode::attribute_entry* attr = node->editAttribute( 738 String16("http://schemas.android.com/apk/res/android"), attrName); 739 if (attr != NULL) { 740 String8 name(attr->string); 741 742 // asdf --> package.asdf 743 // .asdf .a.b --> package.asdf package.a.b 744 // asdf.adsf --> asdf.asdf 745 String8 className; 746 const char* p = name.string(); 747 const char* q = strchr(p, '.'); 748 if (p == q) { 749 className += package; 750 className += name; 751 } else if (q == NULL) { 752 className += package; 753 className += "."; 754 className += name; 755 } else { 756 className += name; 757 } 758 NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string())); 759 attr->string.setTo(String16(className)); 760 } 761} 762 763status_t massageManifest(Bundle* bundle, sp<XMLNode> root) 764{ 765 root = root->searchElement(String16(), String16("manifest")); 766 if (root == NULL) { 767 fprintf(stderr, "No <manifest> tag.\n"); 768 return UNKNOWN_ERROR; 769 } 770 771 bool errorOnFailedInsert = bundle->getErrorOnFailedInsert(); 772 bool replaceVersion = bundle->getReplaceVersion(); 773 774 if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode", 775 bundle->getVersionCode(), errorOnFailedInsert, replaceVersion)) { 776 return UNKNOWN_ERROR; 777 } else { 778 const XMLNode::attribute_entry* attr = root->getAttribute( 779 String16(RESOURCES_ANDROID_NAMESPACE), String16("versionCode")); 780 if (attr != NULL) { 781 bundle->setVersionCode(strdup(String8(attr->string).string())); 782 } 783 } 784 785 if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", 786 bundle->getVersionName(), errorOnFailedInsert, replaceVersion)) { 787 return UNKNOWN_ERROR; 788 } else { 789 const XMLNode::attribute_entry* attr = root->getAttribute( 790 String16(RESOURCES_ANDROID_NAMESPACE), String16("versionName")); 791 if (attr != NULL) { 792 bundle->setVersionName(strdup(String8(attr->string).string())); 793 } 794 } 795 796 sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk")); 797 if (bundle->getMinSdkVersion() != NULL 798 || bundle->getTargetSdkVersion() != NULL 799 || bundle->getMaxSdkVersion() != NULL) { 800 if (vers == NULL) { 801 vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); 802 root->insertChildAt(vers, 0); 803 } 804 805 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion", 806 bundle->getMinSdkVersion(), errorOnFailedInsert)) { 807 return UNKNOWN_ERROR; 808 } 809 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion", 810 bundle->getTargetSdkVersion(), errorOnFailedInsert)) { 811 return UNKNOWN_ERROR; 812 } 813 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion", 814 bundle->getMaxSdkVersion(), errorOnFailedInsert)) { 815 return UNKNOWN_ERROR; 816 } 817 } 818 819 if (vers != NULL) { 820 const XMLNode::attribute_entry* attr = vers->getAttribute( 821 String16(RESOURCES_ANDROID_NAMESPACE), String16("minSdkVersion")); 822 if (attr != NULL) { 823 bundle->setMinSdkVersion(strdup(String8(attr->string).string())); 824 } 825 } 826 827 if (bundle->getPlatformBuildVersionCode() != "") { 828 if (!addTagAttribute(root, "", "platformBuildVersionCode", 829 bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) { 830 return UNKNOWN_ERROR; 831 } 832 } 833 834 if (bundle->getPlatformBuildVersionName() != "") { 835 if (!addTagAttribute(root, "", "platformBuildVersionName", 836 bundle->getPlatformBuildVersionName(), errorOnFailedInsert, true)) { 837 return UNKNOWN_ERROR; 838 } 839 } 840 841 if (bundle->getDebugMode()) { 842 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 843 if (application != NULL) { 844 if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true", 845 errorOnFailedInsert)) { 846 return UNKNOWN_ERROR; 847 } 848 } 849 } 850 851 // Deal with manifest package name overrides 852 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 853 if (manifestPackageNameOverride != NULL) { 854 // Update the actual package name 855 XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package")); 856 if (attr == NULL) { 857 fprintf(stderr, "package name is required with --rename-manifest-package.\n"); 858 return UNKNOWN_ERROR; 859 } 860 String8 origPackage(attr->string); 861 attr->string.setTo(String16(manifestPackageNameOverride)); 862 NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride)); 863 864 // Make class names fully qualified 865 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 866 if (application != NULL) { 867 fullyQualifyClassName(origPackage, application, String16("name")); 868 fullyQualifyClassName(origPackage, application, String16("backupAgent")); 869 870 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren()); 871 for (size_t i = 0; i < children.size(); i++) { 872 sp<XMLNode> child = children.editItemAt(i); 873 String8 tag(child->getElementName()); 874 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 875 fullyQualifyClassName(origPackage, child, String16("name")); 876 } else if (tag == "activity-alias") { 877 fullyQualifyClassName(origPackage, child, String16("name")); 878 fullyQualifyClassName(origPackage, child, String16("targetActivity")); 879 } 880 } 881 } 882 } 883 884 // Deal with manifest package name overrides 885 const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride(); 886 if (instrumentationPackageNameOverride != NULL) { 887 // Fix up instrumentation targets. 888 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren()); 889 for (size_t i = 0; i < children.size(); i++) { 890 sp<XMLNode> child = children.editItemAt(i); 891 String8 tag(child->getElementName()); 892 if (tag == "instrumentation") { 893 XMLNode::attribute_entry* attr = child->editAttribute( 894 String16("http://schemas.android.com/apk/res/android"), String16("targetPackage")); 895 if (attr != NULL) { 896 attr->string.setTo(String16(instrumentationPackageNameOverride)); 897 } 898 } 899 } 900 } 901 902 // Generate split name if feature is present. 903 const XMLNode::attribute_entry* attr = root->getAttribute(String16(), String16("featureName")); 904 if (attr != NULL) { 905 String16 splitName("feature_"); 906 splitName.append(attr->string); 907 status_t err = root->addAttribute(String16(), String16("split"), splitName); 908 if (err != NO_ERROR) { 909 ALOGE("Failed to insert split name into AndroidManifest.xml"); 910 return err; 911 } 912 } 913 914 return NO_ERROR; 915} 916 917static int32_t getPlatformAssetCookie(const AssetManager& assets) { 918 // Find the system package (0x01). AAPT always generates attributes 919 // with the type 0x01, so we're looking for the first attribute 920 // resource in the system package. 921 const ResTable& table = assets.getResources(true); 922 Res_value val; 923 ssize_t idx = table.getResource(0x01010000, &val, true); 924 if (idx != NO_ERROR) { 925 // Try as a bag. 926 const ResTable::bag_entry* entry; 927 ssize_t cnt = table.lockBag(0x01010000, &entry); 928 if (cnt >= 0) { 929 idx = entry->stringBlock; 930 } 931 table.unlockBag(entry); 932 } 933 934 if (idx < 0) { 935 return 0; 936 } 937 return table.getTableCookie(idx); 938} 939 940enum { 941 VERSION_CODE_ATTR = 0x0101021b, 942 VERSION_NAME_ATTR = 0x0101021c, 943}; 944 945static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) { 946 size_t len; 947 ResXMLTree::event_code_t code; 948 while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 949 if (code != ResXMLTree::START_TAG) { 950 continue; 951 } 952 953 const char16_t* ctag16 = tree.getElementName(&len); 954 if (ctag16 == NULL) { 955 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); 956 return UNKNOWN_ERROR; 957 } 958 959 String8 tag(ctag16, len); 960 if (tag != "manifest") { 961 continue; 962 } 963 964 String8 error; 965 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 966 if (error != "") { 967 fprintf(stderr, "ERROR: failed to get platform version code\n"); 968 return UNKNOWN_ERROR; 969 } 970 971 if (versionCode >= 0 && bundle->getPlatformBuildVersionCode() == "") { 972 bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode)); 973 } 974 975 String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error); 976 if (error != "") { 977 fprintf(stderr, "ERROR: failed to get platform version name\n"); 978 return UNKNOWN_ERROR; 979 } 980 981 if (versionName != "" && bundle->getPlatformBuildVersionName() == "") { 982 bundle->setPlatformBuildVersionName(versionName); 983 } 984 return NO_ERROR; 985 } 986 987 fprintf(stderr, "ERROR: no <manifest> tag found in platform AndroidManifest.xml\n"); 988 return UNKNOWN_ERROR; 989} 990 991static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) { 992 int32_t cookie = getPlatformAssetCookie(assets); 993 if (cookie == 0) { 994 // No platform was loaded. 995 return NO_ERROR; 996 } 997 998 ResXMLTree tree; 999 Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING); 1000 if (asset == NULL) { 1001 fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n"); 1002 return UNKNOWN_ERROR; 1003 } 1004 1005 ssize_t result = NO_ERROR; 1006 if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) { 1007 fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); 1008 result = UNKNOWN_ERROR; 1009 } else { 1010 result = extractPlatformBuildVersion(tree, bundle); 1011 } 1012 1013 delete asset; 1014 return result; 1015} 1016 1017#define ASSIGN_IT(n) \ 1018 do { \ 1019 ssize_t index = resources->indexOfKey(String8(#n)); \ 1020 if (index >= 0) { \ 1021 n ## s = resources->valueAt(index); \ 1022 } \ 1023 } while (0) 1024 1025status_t updatePreProcessedCache(Bundle* bundle) 1026{ 1027 #if BENCHMARK 1028 fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n"); 1029 long startPNGTime = clock(); 1030 #endif /* BENCHMARK */ 1031 1032 String8 source(bundle->getResourceSourceDirs()[0]); 1033 String8 dest(bundle->getCrunchedOutputDir()); 1034 1035 FileFinder* ff = new SystemFileFinder(); 1036 CrunchCache cc(source,dest,ff); 1037 1038 CacheUpdater* cu = new SystemCacheUpdater(bundle); 1039 size_t numFiles = cc.crunch(cu); 1040 1041 if (bundle->getVerbose()) 1042 fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles); 1043 1044 delete ff; 1045 delete cu; 1046 1047 #if BENCHMARK 1048 fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n" 1049 ,(clock() - startPNGTime)/1000.0); 1050 #endif /* BENCHMARK */ 1051 return 0; 1052} 1053 1054status_t generateAndroidManifestForSplit(Bundle* bundle, const sp<AaptAssets>& assets, 1055 const sp<ApkSplit>& split, sp<AaptFile>& outFile, ResourceTable* table) { 1056 const String8 filename("AndroidManifest.xml"); 1057 const String16 androidPrefix("android"); 1058 const String16 androidNSUri("http://schemas.android.com/apk/res/android"); 1059 sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri); 1060 1061 // Build the <manifest> tag 1062 sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest")); 1063 1064 // Add the 'package' attribute which is set to the package name. 1065 const char* packageName = assets->getPackage(); 1066 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 1067 if (manifestPackageNameOverride != NULL) { 1068 packageName = manifestPackageNameOverride; 1069 } 1070 manifest->addAttribute(String16(), String16("package"), String16(packageName)); 1071 1072 // Add the 'versionCode' attribute which is set to the original version code. 1073 if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "versionCode", 1074 bundle->getVersionCode(), true, true)) { 1075 return UNKNOWN_ERROR; 1076 } 1077 1078 // Add the 'split' attribute which describes the configurations included. 1079 String8 splitName("config."); 1080 splitName.append(split->getPackageSafeName()); 1081 manifest->addAttribute(String16(), String16("split"), String16(splitName)); 1082 1083 // Build an empty <application> tag (required). 1084 sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application")); 1085 1086 // Add the 'hasCode' attribute which is never true for resource splits. 1087 if (!addTagAttribute(app, RESOURCES_ANDROID_NAMESPACE, "hasCode", 1088 "false", true, true)) { 1089 return UNKNOWN_ERROR; 1090 } 1091 1092 manifest->addChild(app); 1093 root->addChild(manifest); 1094 1095 int err = compileXmlFile(bundle, assets, String16(), root, outFile, table); 1096 if (err < NO_ERROR) { 1097 return err; 1098 } 1099 outFile->setCompressionMethod(ZipEntry::kCompressDeflated); 1100 return NO_ERROR; 1101} 1102 1103status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) 1104{ 1105 // First, look for a package file to parse. This is required to 1106 // be able to generate the resource information. 1107 sp<AaptGroup> androidManifestFile = 1108 assets->getFiles().valueFor(String8("AndroidManifest.xml")); 1109 if (androidManifestFile == NULL) { 1110 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 1111 return UNKNOWN_ERROR; 1112 } 1113 1114 status_t err = parsePackage(bundle, assets, androidManifestFile); 1115 if (err != NO_ERROR) { 1116 return err; 1117 } 1118 1119 NOISY(printf("Creating resources for package %s\n", 1120 assets->getPackage().string())); 1121 1122 ResourceTable::PackageType packageType = ResourceTable::App; 1123 if (bundle->getBuildSharedLibrary()) { 1124 packageType = ResourceTable::SharedLibrary; 1125 } else if (bundle->getExtending()) { 1126 packageType = ResourceTable::System; 1127 } else if (!bundle->getFeatureOfPackage().isEmpty()) { 1128 packageType = ResourceTable::AppFeature; 1129 } 1130 1131 ResourceTable table(bundle, String16(assets->getPackage()), packageType); 1132 err = table.addIncludedResources(bundle, assets); 1133 if (err != NO_ERROR) { 1134 return err; 1135 } 1136 1137 NOISY(printf("Found %d included resource packages\n", (int)table.size())); 1138 1139 // Standard flags for compiled XML and optional UTF-8 encoding 1140 int xmlFlags = XML_COMPILE_STANDARD_RESOURCE; 1141 1142 /* Only enable UTF-8 if the caller of aapt didn't specifically 1143 * request UTF-16 encoding and the parameters of this package 1144 * allow UTF-8 to be used. 1145 */ 1146 if (!bundle->getUTF16StringsOption()) { 1147 xmlFlags |= XML_COMPILE_UTF8; 1148 } 1149 1150 // -------------------------------------------------------------- 1151 // First, gather all resource information. 1152 // -------------------------------------------------------------- 1153 1154 // resType -> leafName -> group 1155 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1156 new KeyedVector<String8, sp<ResourceTypeSet> >; 1157 collect_files(assets, resources); 1158 1159 sp<ResourceTypeSet> drawables; 1160 sp<ResourceTypeSet> layouts; 1161 sp<ResourceTypeSet> anims; 1162 sp<ResourceTypeSet> animators; 1163 sp<ResourceTypeSet> interpolators; 1164 sp<ResourceTypeSet> transitions; 1165 sp<ResourceTypeSet> xmls; 1166 sp<ResourceTypeSet> raws; 1167 sp<ResourceTypeSet> colors; 1168 sp<ResourceTypeSet> menus; 1169 sp<ResourceTypeSet> mipmaps; 1170 1171 ASSIGN_IT(drawable); 1172 ASSIGN_IT(layout); 1173 ASSIGN_IT(anim); 1174 ASSIGN_IT(animator); 1175 ASSIGN_IT(interpolator); 1176 ASSIGN_IT(transition); 1177 ASSIGN_IT(xml); 1178 ASSIGN_IT(raw); 1179 ASSIGN_IT(color); 1180 ASSIGN_IT(menu); 1181 ASSIGN_IT(mipmap); 1182 1183 assets->setResources(resources); 1184 // now go through any resource overlays and collect their files 1185 sp<AaptAssets> current = assets->getOverlay(); 1186 while(current.get()) { 1187 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1188 new KeyedVector<String8, sp<ResourceTypeSet> >; 1189 current->setResources(resources); 1190 collect_files(current, resources); 1191 current = current->getOverlay(); 1192 } 1193 // apply the overlay files to the base set 1194 if (!applyFileOverlay(bundle, assets, &drawables, "drawable") || 1195 !applyFileOverlay(bundle, assets, &layouts, "layout") || 1196 !applyFileOverlay(bundle, assets, &anims, "anim") || 1197 !applyFileOverlay(bundle, assets, &animators, "animator") || 1198 !applyFileOverlay(bundle, assets, &interpolators, "interpolator") || 1199 !applyFileOverlay(bundle, assets, &transitions, "transition") || 1200 !applyFileOverlay(bundle, assets, &xmls, "xml") || 1201 !applyFileOverlay(bundle, assets, &raws, "raw") || 1202 !applyFileOverlay(bundle, assets, &colors, "color") || 1203 !applyFileOverlay(bundle, assets, &menus, "menu") || 1204 !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) { 1205 return UNKNOWN_ERROR; 1206 } 1207 1208 bool hasErrors = false; 1209 1210 if (drawables != NULL) { 1211 if (bundle->getOutputAPKFile() != NULL) { 1212 err = preProcessImages(bundle, assets, drawables, "drawable"); 1213 } 1214 if (err == NO_ERROR) { 1215 err = makeFileResources(bundle, assets, &table, drawables, "drawable"); 1216 if (err != NO_ERROR) { 1217 hasErrors = true; 1218 } 1219 } else { 1220 hasErrors = true; 1221 } 1222 } 1223 1224 if (mipmaps != NULL) { 1225 if (bundle->getOutputAPKFile() != NULL) { 1226 err = preProcessImages(bundle, assets, mipmaps, "mipmap"); 1227 } 1228 if (err == NO_ERROR) { 1229 err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap"); 1230 if (err != NO_ERROR) { 1231 hasErrors = true; 1232 } 1233 } else { 1234 hasErrors = true; 1235 } 1236 } 1237 1238 if (layouts != NULL) { 1239 err = makeFileResources(bundle, assets, &table, layouts, "layout"); 1240 if (err != NO_ERROR) { 1241 hasErrors = true; 1242 } 1243 } 1244 1245 if (anims != NULL) { 1246 err = makeFileResources(bundle, assets, &table, anims, "anim"); 1247 if (err != NO_ERROR) { 1248 hasErrors = true; 1249 } 1250 } 1251 1252 if (animators != NULL) { 1253 err = makeFileResources(bundle, assets, &table, animators, "animator"); 1254 if (err != NO_ERROR) { 1255 hasErrors = true; 1256 } 1257 } 1258 1259 if (transitions != NULL) { 1260 err = makeFileResources(bundle, assets, &table, transitions, "transition"); 1261 if (err != NO_ERROR) { 1262 hasErrors = true; 1263 } 1264 } 1265 1266 if (interpolators != NULL) { 1267 err = makeFileResources(bundle, assets, &table, interpolators, "interpolator"); 1268 if (err != NO_ERROR) { 1269 hasErrors = true; 1270 } 1271 } 1272 1273 if (xmls != NULL) { 1274 err = makeFileResources(bundle, assets, &table, xmls, "xml"); 1275 if (err != NO_ERROR) { 1276 hasErrors = true; 1277 } 1278 } 1279 1280 if (raws != NULL) { 1281 err = makeFileResources(bundle, assets, &table, raws, "raw"); 1282 if (err != NO_ERROR) { 1283 hasErrors = true; 1284 } 1285 } 1286 1287 // compile resources 1288 current = assets; 1289 while(current.get()) { 1290 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1291 current->getResources(); 1292 1293 ssize_t index = resources->indexOfKey(String8("values")); 1294 if (index >= 0) { 1295 ResourceDirIterator it(resources->valueAt(index), String8("values")); 1296 ssize_t res; 1297 while ((res=it.next()) == NO_ERROR) { 1298 sp<AaptFile> file = it.getFile(); 1299 res = compileResourceFile(bundle, assets, file, it.getParams(), 1300 (current!=assets), &table); 1301 if (res != NO_ERROR) { 1302 hasErrors = true; 1303 } 1304 } 1305 } 1306 current = current->getOverlay(); 1307 } 1308 1309 if (colors != NULL) { 1310 err = makeFileResources(bundle, assets, &table, colors, "color"); 1311 if (err != NO_ERROR) { 1312 hasErrors = true; 1313 } 1314 } 1315 1316 if (menus != NULL) { 1317 err = makeFileResources(bundle, assets, &table, menus, "menu"); 1318 if (err != NO_ERROR) { 1319 hasErrors = true; 1320 } 1321 } 1322 1323 // -------------------------------------------------------------------- 1324 // Assignment of resource IDs and initial generation of resource table. 1325 // -------------------------------------------------------------------- 1326 1327 if (table.hasResources()) { 1328 err = table.assignResourceIds(); 1329 if (err < NO_ERROR) { 1330 return err; 1331 } 1332 } 1333 1334 // -------------------------------------------------------------- 1335 // Finally, we can now we can compile XML files, which may reference 1336 // resources. 1337 // -------------------------------------------------------------- 1338 1339 if (layouts != NULL) { 1340 ResourceDirIterator it(layouts, String8("layout")); 1341 while ((err=it.next()) == NO_ERROR) { 1342 String8 src = it.getFile()->getPrintableSource(); 1343 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1344 it.getFile(), &table, xmlFlags); 1345 if (err == NO_ERROR) { 1346 ResXMLTree block; 1347 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1348 checkForIds(src, block); 1349 } else { 1350 hasErrors = true; 1351 } 1352 } 1353 1354 if (err < NO_ERROR) { 1355 hasErrors = true; 1356 } 1357 err = NO_ERROR; 1358 } 1359 1360 if (anims != NULL) { 1361 ResourceDirIterator it(anims, String8("anim")); 1362 while ((err=it.next()) == NO_ERROR) { 1363 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1364 it.getFile(), &table, xmlFlags); 1365 if (err != NO_ERROR) { 1366 hasErrors = true; 1367 } 1368 } 1369 1370 if (err < NO_ERROR) { 1371 hasErrors = true; 1372 } 1373 err = NO_ERROR; 1374 } 1375 1376 if (animators != NULL) { 1377 ResourceDirIterator it(animators, String8("animator")); 1378 while ((err=it.next()) == NO_ERROR) { 1379 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1380 it.getFile(), &table, xmlFlags); 1381 if (err != NO_ERROR) { 1382 hasErrors = true; 1383 } 1384 } 1385 1386 if (err < NO_ERROR) { 1387 hasErrors = true; 1388 } 1389 err = NO_ERROR; 1390 } 1391 1392 if (interpolators != NULL) { 1393 ResourceDirIterator it(interpolators, String8("interpolator")); 1394 while ((err=it.next()) == NO_ERROR) { 1395 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1396 it.getFile(), &table, xmlFlags); 1397 if (err != NO_ERROR) { 1398 hasErrors = true; 1399 } 1400 } 1401 1402 if (err < NO_ERROR) { 1403 hasErrors = true; 1404 } 1405 err = NO_ERROR; 1406 } 1407 1408 if (transitions != NULL) { 1409 ResourceDirIterator it(transitions, String8("transition")); 1410 while ((err=it.next()) == NO_ERROR) { 1411 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1412 it.getFile(), &table, xmlFlags); 1413 if (err != NO_ERROR) { 1414 hasErrors = true; 1415 } 1416 } 1417 1418 if (err < NO_ERROR) { 1419 hasErrors = true; 1420 } 1421 err = NO_ERROR; 1422 } 1423 1424 if (xmls != NULL) { 1425 ResourceDirIterator it(xmls, String8("xml")); 1426 while ((err=it.next()) == NO_ERROR) { 1427 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1428 it.getFile(), &table, xmlFlags); 1429 if (err != NO_ERROR) { 1430 hasErrors = true; 1431 } 1432 } 1433 1434 if (err < NO_ERROR) { 1435 hasErrors = true; 1436 } 1437 err = NO_ERROR; 1438 } 1439 1440 if (drawables != NULL) { 1441 ResourceDirIterator it(drawables, String8("drawable")); 1442 while ((err=it.next()) == NO_ERROR) { 1443 err = postProcessImage(bundle, assets, &table, it.getFile()); 1444 if (err != NO_ERROR) { 1445 hasErrors = true; 1446 } 1447 } 1448 1449 if (err < NO_ERROR) { 1450 hasErrors = true; 1451 } 1452 err = NO_ERROR; 1453 } 1454 1455 if (colors != NULL) { 1456 ResourceDirIterator it(colors, String8("color")); 1457 while ((err=it.next()) == NO_ERROR) { 1458 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1459 it.getFile(), &table, xmlFlags); 1460 if (err != NO_ERROR) { 1461 hasErrors = true; 1462 } 1463 } 1464 1465 if (err < NO_ERROR) { 1466 hasErrors = true; 1467 } 1468 err = NO_ERROR; 1469 } 1470 1471 if (menus != NULL) { 1472 ResourceDirIterator it(menus, String8("menu")); 1473 while ((err=it.next()) == NO_ERROR) { 1474 String8 src = it.getFile()->getPrintableSource(); 1475 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1476 it.getFile(), &table, xmlFlags); 1477 if (err == NO_ERROR) { 1478 ResXMLTree block; 1479 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1480 checkForIds(src, block); 1481 } else { 1482 hasErrors = true; 1483 } 1484 } 1485 1486 if (err < NO_ERROR) { 1487 hasErrors = true; 1488 } 1489 err = NO_ERROR; 1490 } 1491 1492 // Now compile any generated resources. 1493 std::queue<CompileResourceWorkItem>& workQueue = table.getWorkQueue(); 1494 while (!workQueue.empty()) { 1495 CompileResourceWorkItem& workItem = workQueue.front(); 1496 err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.file, &table, xmlFlags); 1497 if (err == NO_ERROR) { 1498 assets->addResource(workItem.resPath.getPathLeaf(), 1499 workItem.resPath, 1500 workItem.file, 1501 workItem.file->getResourceType()); 1502 } else { 1503 hasErrors = true; 1504 } 1505 workQueue.pop(); 1506 } 1507 1508 if (table.validateLocalizations()) { 1509 hasErrors = true; 1510 } 1511 1512 if (hasErrors) { 1513 return UNKNOWN_ERROR; 1514 } 1515 1516 // If we're not overriding the platform build versions, 1517 // extract them from the platform APK. 1518 if (packageType != ResourceTable::System && 1519 (bundle->getPlatformBuildVersionCode() == "" || 1520 bundle->getPlatformBuildVersionName() == "")) { 1521 err = extractPlatformBuildVersion(assets->getAssetManager(), bundle); 1522 if (err != NO_ERROR) { 1523 return UNKNOWN_ERROR; 1524 } 1525 } 1526 1527 const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0)); 1528 String8 manifestPath(manifestFile->getPrintableSource()); 1529 1530 // Generate final compiled manifest file. 1531 manifestFile->clearData(); 1532 sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); 1533 if (manifestTree == NULL) { 1534 return UNKNOWN_ERROR; 1535 } 1536 err = massageManifest(bundle, manifestTree); 1537 if (err < NO_ERROR) { 1538 return err; 1539 } 1540 err = compileXmlFile(bundle, assets, String16(), manifestTree, manifestFile, &table); 1541 if (err < NO_ERROR) { 1542 return err; 1543 } 1544 1545 if (table.modifyForCompat(bundle) != NO_ERROR) { 1546 return UNKNOWN_ERROR; 1547 } 1548 1549 //block.restart(); 1550 //printXMLBlock(&block); 1551 1552 // -------------------------------------------------------------- 1553 // Generate the final resource table. 1554 // Re-flatten because we may have added new resource IDs 1555 // -------------------------------------------------------------- 1556 1557 1558 ResTable finalResTable; 1559 sp<AaptFile> resFile; 1560 1561 if (table.hasResources()) { 1562 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); 1563 err = table.addSymbols(symbols); 1564 if (err < NO_ERROR) { 1565 return err; 1566 } 1567 1568 KeyedVector<Symbol, Vector<SymbolDefinition> > densityVaryingResources; 1569 if (builder->getSplits().size() > 1) { 1570 // Only look for density varying resources if we're generating 1571 // splits. 1572 table.getDensityVaryingResources(densityVaryingResources); 1573 } 1574 1575 Vector<sp<ApkSplit> >& splits = builder->getSplits(); 1576 const size_t numSplits = splits.size(); 1577 for (size_t i = 0; i < numSplits; i++) { 1578 sp<ApkSplit>& split = splits.editItemAt(i); 1579 sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"), 1580 AaptGroupEntry(), String8()); 1581 err = table.flatten(bundle, split->getResourceFilter(), 1582 flattenedTable, split->isBase()); 1583 if (err != NO_ERROR) { 1584 fprintf(stderr, "Failed to generate resource table for split '%s'\n", 1585 split->getPrintableName().string()); 1586 return err; 1587 } 1588 split->addEntry(String8("resources.arsc"), flattenedTable); 1589 1590 if (split->isBase()) { 1591 resFile = flattenedTable; 1592 err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); 1593 if (err != NO_ERROR) { 1594 fprintf(stderr, "Generated resource table is corrupt.\n"); 1595 return err; 1596 } 1597 } else { 1598 ResTable resTable; 1599 err = resTable.add(flattenedTable->getData(), flattenedTable->getSize()); 1600 if (err != NO_ERROR) { 1601 fprintf(stderr, "Generated resource table for split '%s' is corrupt.\n", 1602 split->getPrintableName().string()); 1603 return err; 1604 } 1605 1606 bool hasError = false; 1607 const std::set<ConfigDescription>& splitConfigs = split->getConfigs(); 1608 for (std::set<ConfigDescription>::const_iterator iter = splitConfigs.begin(); 1609 iter != splitConfigs.end(); 1610 ++iter) { 1611 const ConfigDescription& config = *iter; 1612 if (AaptConfig::isDensityOnly(config)) { 1613 // Each density only split must contain all 1614 // density only resources. 1615 Res_value val; 1616 resTable.setParameters(&config); 1617 const size_t densityVaryingResourceCount = densityVaryingResources.size(); 1618 for (size_t k = 0; k < densityVaryingResourceCount; k++) { 1619 const Symbol& symbol = densityVaryingResources.keyAt(k); 1620 ssize_t block = resTable.getResource(symbol.id, &val, true); 1621 if (block < 0) { 1622 // Maybe it's in the base? 1623 finalResTable.setParameters(&config); 1624 block = finalResTable.getResource(symbol.id, &val, true); 1625 } 1626 1627 if (block < 0) { 1628 hasError = true; 1629 SourcePos().error("%s has no definition for density split '%s'", 1630 symbol.toString().string(), config.toString().string()); 1631 1632 if (bundle->getVerbose()) { 1633 const Vector<SymbolDefinition>& defs = densityVaryingResources[k]; 1634 const size_t defCount = std::min(size_t(5), defs.size()); 1635 for (size_t d = 0; d < defCount; d++) { 1636 const SymbolDefinition& def = defs[d]; 1637 def.source.error("%s has definition for %s", 1638 symbol.toString().string(), def.config.toString().string()); 1639 } 1640 1641 if (defCount < defs.size()) { 1642 SourcePos().error("and %d more ...", (int) (defs.size() - defCount)); 1643 } 1644 } 1645 } 1646 } 1647 } 1648 } 1649 1650 if (hasError) { 1651 return UNKNOWN_ERROR; 1652 } 1653 1654 // Generate the AndroidManifest for this split. 1655 sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), 1656 AaptGroupEntry(), String8()); 1657 err = generateAndroidManifestForSplit(bundle, assets, split, 1658 generatedManifest, &table); 1659 if (err != NO_ERROR) { 1660 fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n", 1661 split->getPrintableName().string()); 1662 return err; 1663 } 1664 split->addEntry(String8("AndroidManifest.xml"), generatedManifest); 1665 } 1666 } 1667 1668 if (bundle->getPublicOutputFile()) { 1669 FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); 1670 if (fp == NULL) { 1671 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", 1672 (const char*)bundle->getPublicOutputFile(), strerror(errno)); 1673 return UNKNOWN_ERROR; 1674 } 1675 if (bundle->getVerbose()) { 1676 printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); 1677 } 1678 table.writePublicDefinitions(String16(assets->getPackage()), fp); 1679 fclose(fp); 1680 } 1681 1682 if (finalResTable.getTableCount() == 0 || resFile == NULL) { 1683 fprintf(stderr, "No resource table was generated.\n"); 1684 return UNKNOWN_ERROR; 1685 } 1686 } 1687 1688 // Perform a basic validation of the manifest file. This time we 1689 // parse it with the comments intact, so that we can use them to 1690 // generate java docs... so we are not going to write this one 1691 // back out to the final manifest data. 1692 sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(), 1693 manifestFile->getGroupEntry(), 1694 manifestFile->getResourceType()); 1695 err = compileXmlFile(bundle, assets, String16(), manifestFile, 1696 outManifestFile, &table, 1697 XML_COMPILE_ASSIGN_ATTRIBUTE_IDS 1698 | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES); 1699 if (err < NO_ERROR) { 1700 return err; 1701 } 1702 ResXMLTree block; 1703 block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true); 1704 String16 manifest16("manifest"); 1705 String16 permission16("permission"); 1706 String16 permission_group16("permission-group"); 1707 String16 uses_permission16("uses-permission"); 1708 String16 instrumentation16("instrumentation"); 1709 String16 application16("application"); 1710 String16 provider16("provider"); 1711 String16 service16("service"); 1712 String16 receiver16("receiver"); 1713 String16 activity16("activity"); 1714 String16 action16("action"); 1715 String16 category16("category"); 1716 String16 data16("scheme"); 1717 String16 feature_group16("feature-group"); 1718 String16 uses_feature16("uses-feature"); 1719 const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz" 1720 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789"; 1721 const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz" 1722 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1723 const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz" 1724 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$"; 1725 const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz" 1726 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:"; 1727 const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz" 1728 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;"; 1729 const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1730 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+"; 1731 const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1732 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1733 ResXMLTree::event_code_t code; 1734 sp<AaptSymbols> permissionSymbols; 1735 sp<AaptSymbols> permissionGroupSymbols; 1736 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1737 && code > ResXMLTree::BAD_DOCUMENT) { 1738 if (code == ResXMLTree::START_TAG) { 1739 size_t len; 1740 if (block.getElementNamespace(&len) != NULL) { 1741 continue; 1742 } 1743 if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) { 1744 if (validateAttr(manifestPath, finalResTable, block, NULL, "package", 1745 packageIdentChars, true) != ATTR_OKAY) { 1746 hasErrors = true; 1747 } 1748 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1749 "sharedUserId", packageIdentChars, false) != ATTR_OKAY) { 1750 hasErrors = true; 1751 } 1752 } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0 1753 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) { 1754 const bool isGroup = strcmp16(block.getElementName(&len), 1755 permission_group16.string()) == 0; 1756 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1757 "name", isGroup ? packageIdentCharsWithTheStupid 1758 : packageIdentChars, true) != ATTR_OKAY) { 1759 hasErrors = true; 1760 } 1761 SourcePos srcPos(manifestPath, block.getLineNumber()); 1762 sp<AaptSymbols> syms; 1763 if (!isGroup) { 1764 syms = permissionSymbols; 1765 if (syms == NULL) { 1766 sp<AaptSymbols> symbols = 1767 assets->getSymbolsFor(String8("Manifest")); 1768 syms = permissionSymbols = symbols->addNestedSymbol( 1769 String8("permission"), srcPos); 1770 } 1771 } else { 1772 syms = permissionGroupSymbols; 1773 if (syms == NULL) { 1774 sp<AaptSymbols> symbols = 1775 assets->getSymbolsFor(String8("Manifest")); 1776 syms = permissionGroupSymbols = symbols->addNestedSymbol( 1777 String8("permission_group"), srcPos); 1778 } 1779 } 1780 size_t len; 1781 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name"); 1782 const char16_t* id = block.getAttributeStringValue(index, &len); 1783 if (id == NULL) { 1784 fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", 1785 manifestPath.string(), block.getLineNumber(), 1786 String8(block.getElementName(&len)).string()); 1787 hasErrors = true; 1788 break; 1789 } 1790 String8 idStr(id); 1791 char* p = idStr.lockBuffer(idStr.size()); 1792 char* e = p + idStr.size(); 1793 bool begins_with_digit = true; // init to true so an empty string fails 1794 while (e > p) { 1795 e--; 1796 if (*e >= '0' && *e <= '9') { 1797 begins_with_digit = true; 1798 continue; 1799 } 1800 if ((*e >= 'a' && *e <= 'z') || 1801 (*e >= 'A' && *e <= 'Z') || 1802 (*e == '_')) { 1803 begins_with_digit = false; 1804 continue; 1805 } 1806 if (isGroup && (*e == '-')) { 1807 *e = '_'; 1808 begins_with_digit = false; 1809 continue; 1810 } 1811 e++; 1812 break; 1813 } 1814 idStr.unlockBuffer(); 1815 // verify that we stopped because we hit a period or 1816 // the beginning of the string, and that the 1817 // identifier didn't begin with a digit. 1818 if (begins_with_digit || (e != p && *(e-1) != '.')) { 1819 fprintf(stderr, 1820 "%s:%d: Permission name <%s> is not a valid Java symbol\n", 1821 manifestPath.string(), block.getLineNumber(), idStr.string()); 1822 hasErrors = true; 1823 } 1824 syms->addStringSymbol(String8(e), idStr, srcPos); 1825 const char16_t* cmt = block.getComment(&len); 1826 if (cmt != NULL && *cmt != 0) { 1827 //printf("Comment of %s: %s\n", String8(e).string(), 1828 // String8(cmt).string()); 1829 syms->appendComment(String8(e), String16(cmt), srcPos); 1830 } else { 1831 //printf("No comment for %s\n", String8(e).string()); 1832 } 1833 syms->makeSymbolPublic(String8(e), srcPos); 1834 } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) { 1835 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1836 "name", packageIdentChars, true) != ATTR_OKAY) { 1837 hasErrors = true; 1838 } 1839 } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) { 1840 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1841 "name", classIdentChars, true) != ATTR_OKAY) { 1842 hasErrors = true; 1843 } 1844 if (validateAttr(manifestPath, finalResTable, block, 1845 RESOURCES_ANDROID_NAMESPACE, "targetPackage", 1846 packageIdentChars, true) != ATTR_OKAY) { 1847 hasErrors = true; 1848 } 1849 } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) { 1850 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1851 "name", classIdentChars, false) != ATTR_OKAY) { 1852 hasErrors = true; 1853 } 1854 if (validateAttr(manifestPath, finalResTable, block, 1855 RESOURCES_ANDROID_NAMESPACE, "permission", 1856 packageIdentChars, false) != ATTR_OKAY) { 1857 hasErrors = true; 1858 } 1859 if (validateAttr(manifestPath, finalResTable, block, 1860 RESOURCES_ANDROID_NAMESPACE, "process", 1861 processIdentChars, false) != ATTR_OKAY) { 1862 hasErrors = true; 1863 } 1864 if (validateAttr(manifestPath, finalResTable, block, 1865 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1866 processIdentChars, false) != ATTR_OKAY) { 1867 hasErrors = true; 1868 } 1869 } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) { 1870 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1871 "name", classIdentChars, true) != ATTR_OKAY) { 1872 hasErrors = true; 1873 } 1874 if (validateAttr(manifestPath, finalResTable, block, 1875 RESOURCES_ANDROID_NAMESPACE, "authorities", 1876 authoritiesIdentChars, true) != ATTR_OKAY) { 1877 hasErrors = true; 1878 } 1879 if (validateAttr(manifestPath, finalResTable, block, 1880 RESOURCES_ANDROID_NAMESPACE, "permission", 1881 packageIdentChars, false) != ATTR_OKAY) { 1882 hasErrors = true; 1883 } 1884 if (validateAttr(manifestPath, finalResTable, block, 1885 RESOURCES_ANDROID_NAMESPACE, "process", 1886 processIdentChars, false) != ATTR_OKAY) { 1887 hasErrors = true; 1888 } 1889 } else if (strcmp16(block.getElementName(&len), service16.string()) == 0 1890 || strcmp16(block.getElementName(&len), receiver16.string()) == 0 1891 || strcmp16(block.getElementName(&len), activity16.string()) == 0) { 1892 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1893 "name", classIdentChars, true) != ATTR_OKAY) { 1894 hasErrors = true; 1895 } 1896 if (validateAttr(manifestPath, finalResTable, block, 1897 RESOURCES_ANDROID_NAMESPACE, "permission", 1898 packageIdentChars, false) != ATTR_OKAY) { 1899 hasErrors = true; 1900 } 1901 if (validateAttr(manifestPath, finalResTable, block, 1902 RESOURCES_ANDROID_NAMESPACE, "process", 1903 processIdentChars, false) != ATTR_OKAY) { 1904 hasErrors = true; 1905 } 1906 if (validateAttr(manifestPath, finalResTable, block, 1907 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1908 processIdentChars, false) != ATTR_OKAY) { 1909 hasErrors = true; 1910 } 1911 } else if (strcmp16(block.getElementName(&len), action16.string()) == 0 1912 || strcmp16(block.getElementName(&len), category16.string()) == 0) { 1913 if (validateAttr(manifestPath, finalResTable, block, 1914 RESOURCES_ANDROID_NAMESPACE, "name", 1915 packageIdentChars, true) != ATTR_OKAY) { 1916 hasErrors = true; 1917 } 1918 } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) { 1919 if (validateAttr(manifestPath, finalResTable, block, 1920 RESOURCES_ANDROID_NAMESPACE, "mimeType", 1921 typeIdentChars, true) != ATTR_OKAY) { 1922 hasErrors = true; 1923 } 1924 if (validateAttr(manifestPath, finalResTable, block, 1925 RESOURCES_ANDROID_NAMESPACE, "scheme", 1926 schemeIdentChars, true) != ATTR_OKAY) { 1927 hasErrors = true; 1928 } 1929 } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) { 1930 int depth = 1; 1931 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1932 && code > ResXMLTree::BAD_DOCUMENT) { 1933 if (code == ResXMLTree::START_TAG) { 1934 depth++; 1935 if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) { 1936 ssize_t idx = block.indexOfAttribute( 1937 RESOURCES_ANDROID_NAMESPACE, "required"); 1938 if (idx < 0) { 1939 continue; 1940 } 1941 1942 int32_t data = block.getAttributeData(idx); 1943 if (data == 0) { 1944 fprintf(stderr, "%s:%d: Tag <uses-feature> can not have " 1945 "android:required=\"false\" when inside a " 1946 "<feature-group> tag.\n", 1947 manifestPath.string(), block.getLineNumber()); 1948 hasErrors = true; 1949 } 1950 } 1951 } else if (code == ResXMLTree::END_TAG) { 1952 depth--; 1953 if (depth == 0) { 1954 break; 1955 } 1956 } 1957 } 1958 } 1959 } 1960 } 1961 1962 if (hasErrors) { 1963 return UNKNOWN_ERROR; 1964 } 1965 1966 if (resFile != NULL) { 1967 // These resources are now considered to be a part of the included 1968 // resources, for others to reference. 1969 err = assets->addIncludedResources(resFile); 1970 if (err < NO_ERROR) { 1971 fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n"); 1972 return err; 1973 } 1974 } 1975 1976 return err; 1977} 1978 1979static const char* getIndentSpace(int indent) 1980{ 1981static const char whitespace[] = 1982" "; 1983 1984 return whitespace + sizeof(whitespace) - 1 - indent*4; 1985} 1986 1987static String8 flattenSymbol(const String8& symbol) { 1988 String8 result(symbol); 1989 ssize_t first; 1990 if ((first = symbol.find(":", 0)) >= 0 1991 || (first = symbol.find(".", 0)) >= 0) { 1992 size_t size = symbol.size(); 1993 char* buf = result.lockBuffer(size); 1994 for (size_t i = first; i < size; i++) { 1995 if (buf[i] == ':' || buf[i] == '.') { 1996 buf[i] = '_'; 1997 } 1998 } 1999 result.unlockBuffer(size); 2000 } 2001 return result; 2002} 2003 2004static String8 getSymbolPackage(const String8& symbol, const sp<AaptAssets>& assets, bool pub) { 2005 ssize_t colon = symbol.find(":", 0); 2006 if (colon >= 0) { 2007 return String8(symbol.string(), colon); 2008 } 2009 return pub ? assets->getPackage() : assets->getSymbolsPrivatePackage(); 2010} 2011 2012static String8 getSymbolName(const String8& symbol) { 2013 ssize_t colon = symbol.find(":", 0); 2014 if (colon >= 0) { 2015 return String8(symbol.string() + colon + 1); 2016 } 2017 return symbol; 2018} 2019 2020static String16 getAttributeComment(const sp<AaptAssets>& assets, 2021 const String8& name, 2022 String16* outTypeComment = NULL) 2023{ 2024 sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R")); 2025 if (asym != NULL) { 2026 //printf("Got R symbols!\n"); 2027 asym = asym->getNestedSymbols().valueFor(String8("attr")); 2028 if (asym != NULL) { 2029 //printf("Got attrs symbols! comment %s=%s\n", 2030 // name.string(), String8(asym->getComment(name)).string()); 2031 if (outTypeComment != NULL) { 2032 *outTypeComment = asym->getTypeComment(name); 2033 } 2034 return asym->getComment(name); 2035 } 2036 } 2037 return String16(); 2038} 2039 2040static status_t writeResourceLoadedCallbackForLayoutClasses( 2041 FILE* fp, const sp<AaptAssets>& assets, 2042 const sp<AaptSymbols>& symbols, int indent, bool includePrivate) 2043{ 2044 String16 attr16("attr"); 2045 String16 package16(assets->getPackage()); 2046 2047 const char* indentStr = getIndentSpace(indent); 2048 bool hasErrors = false; 2049 2050 size_t i; 2051 size_t N = symbols->getNestedSymbols().size(); 2052 for (i=0; i<N; i++) { 2053 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2054 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2055 String8 nclassName(flattenSymbol(realClassName)); 2056 2057 fprintf(fp, 2058 "%sfor(int i = 0; i < styleable.%s.length; ++i) {\n" 2059 "%sstyleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (packageId << 24);\n" 2060 "%s}\n", 2061 indentStr, nclassName.string(), 2062 getIndentSpace(indent+1), nclassName.string(), nclassName.string(), 2063 indentStr); 2064 } 2065 2066 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 2067} 2068 2069static status_t writeResourceLoadedCallback( 2070 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2071 const sp<AaptSymbols>& symbols, const String8& className, int indent) 2072{ 2073 size_t i; 2074 status_t err = NO_ERROR; 2075 2076 size_t N = symbols->getSymbols().size(); 2077 for (i=0; i<N; i++) { 2078 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2079 if (sym.typeCode == AaptSymbolEntry::TYPE_UNKNOWN) { 2080 continue; 2081 } 2082 if (!assets->isJavaSymbol(sym, includePrivate)) { 2083 continue; 2084 } 2085 String8 flat_name(flattenSymbol(sym.name)); 2086 fprintf(fp, 2087 "%s%s.%s = (%s.%s & 0x00ffffff) | (packageId << 24);\n", 2088 getIndentSpace(indent), className.string(), flat_name.string(), 2089 className.string(), flat_name.string()); 2090 } 2091 2092 N = symbols->getNestedSymbols().size(); 2093 for (i=0; i<N; i++) { 2094 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2095 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2096 if (nclassName == "styleable") { 2097 err = writeResourceLoadedCallbackForLayoutClasses( 2098 fp, assets, nsymbols, indent, includePrivate); 2099 } else { 2100 err = writeResourceLoadedCallback(fp, assets, includePrivate, nsymbols, 2101 nclassName, indent); 2102 } 2103 if (err != NO_ERROR) { 2104 return err; 2105 } 2106 } 2107 2108 return NO_ERROR; 2109} 2110 2111static status_t writeLayoutClasses( 2112 FILE* fp, const sp<AaptAssets>& assets, 2113 const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId) 2114{ 2115 const char* indentStr = getIndentSpace(indent); 2116 if (!includePrivate) { 2117 fprintf(fp, "%s/** @doconly */\n", indentStr); 2118 } 2119 fprintf(fp, "%spublic static final class styleable {\n", indentStr); 2120 indent++; 2121 2122 String16 attr16("attr"); 2123 String16 package16(assets->getPackage()); 2124 2125 indentStr = getIndentSpace(indent); 2126 bool hasErrors = false; 2127 2128 size_t i; 2129 size_t N = symbols->getNestedSymbols().size(); 2130 for (i=0; i<N; i++) { 2131 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2132 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2133 String8 nclassName(flattenSymbol(realClassName)); 2134 2135 SortedVector<uint32_t> idents; 2136 Vector<uint32_t> origOrder; 2137 Vector<bool> publicFlags; 2138 2139 size_t a; 2140 size_t NA = nsymbols->getSymbols().size(); 2141 for (a=0; a<NA; a++) { 2142 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 2143 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 2144 ? sym.int32Val : 0; 2145 bool isPublic = true; 2146 if (code == 0) { 2147 String16 name16(sym.name); 2148 uint32_t typeSpecFlags; 2149 code = assets->getIncludedResources().identifierForName( 2150 name16.string(), name16.size(), 2151 attr16.string(), attr16.size(), 2152 package16.string(), package16.size(), &typeSpecFlags); 2153 if (code == 0) { 2154 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 2155 nclassName.string(), sym.name.string()); 2156 hasErrors = true; 2157 } 2158 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2159 } 2160 idents.add(code); 2161 origOrder.add(code); 2162 publicFlags.add(isPublic); 2163 } 2164 2165 NA = idents.size(); 2166 2167 String16 comment = symbols->getComment(realClassName); 2168 AnnotationProcessor ann; 2169 fprintf(fp, "%s/** ", indentStr); 2170 if (comment.size() > 0) { 2171 String8 cmt(comment); 2172 ann.preprocessComment(cmt); 2173 fprintf(fp, "%s\n", cmt.string()); 2174 } else { 2175 fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string()); 2176 } 2177 bool hasTable = false; 2178 for (a=0; a<NA; a++) { 2179 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2180 if (pos >= 0) { 2181 if (!hasTable) { 2182 hasTable = true; 2183 fprintf(fp, 2184 "%s <p>Includes the following attributes:</p>\n" 2185 "%s <table>\n" 2186 "%s <colgroup align=\"left\" />\n" 2187 "%s <colgroup align=\"left\" />\n" 2188 "%s <tr><th>Attribute</th><th>Description</th></tr>\n", 2189 indentStr, 2190 indentStr, 2191 indentStr, 2192 indentStr, 2193 indentStr); 2194 } 2195 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2196 if (!publicFlags.itemAt(a) && !includePrivate) { 2197 continue; 2198 } 2199 String8 name8(sym.name); 2200 String16 comment(sym.comment); 2201 if (comment.size() <= 0) { 2202 comment = getAttributeComment(assets, name8); 2203 } 2204 if (comment.size() > 0) { 2205 const char16_t* p = comment.string(); 2206 while (*p != 0 && *p != '.') { 2207 if (*p == '{') { 2208 while (*p != 0 && *p != '}') { 2209 p++; 2210 } 2211 } else { 2212 p++; 2213 } 2214 } 2215 if (*p == '.') { 2216 p++; 2217 } 2218 comment = String16(comment.string(), p-comment.string()); 2219 } 2220 fprintf(fp, "%s <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n", 2221 indentStr, nclassName.string(), 2222 flattenSymbol(name8).string(), 2223 getSymbolPackage(name8, assets, true).string(), 2224 getSymbolName(name8).string(), 2225 String8(comment).string()); 2226 } 2227 } 2228 if (hasTable) { 2229 fprintf(fp, "%s </table>\n", indentStr); 2230 } 2231 for (a=0; a<NA; a++) { 2232 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2233 if (pos >= 0) { 2234 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2235 if (!publicFlags.itemAt(a) && !includePrivate) { 2236 continue; 2237 } 2238 fprintf(fp, "%s @see #%s_%s\n", 2239 indentStr, nclassName.string(), 2240 flattenSymbol(sym.name).string()); 2241 } 2242 } 2243 fprintf(fp, "%s */\n", getIndentSpace(indent)); 2244 2245 ann.printAnnotations(fp, indentStr); 2246 2247 fprintf(fp, 2248 "%spublic static final int[] %s = {\n" 2249 "%s", 2250 indentStr, nclassName.string(), 2251 getIndentSpace(indent+1)); 2252 2253 for (a=0; a<NA; a++) { 2254 if (a != 0) { 2255 if ((a&3) == 0) { 2256 fprintf(fp, ",\n%s", getIndentSpace(indent+1)); 2257 } else { 2258 fprintf(fp, ", "); 2259 } 2260 } 2261 fprintf(fp, "0x%08x", idents[a]); 2262 } 2263 2264 fprintf(fp, "\n%s};\n", indentStr); 2265 2266 for (a=0; a<NA; a++) { 2267 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2268 if (pos >= 0) { 2269 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2270 if (!publicFlags.itemAt(a) && !includePrivate) { 2271 continue; 2272 } 2273 String8 name8(sym.name); 2274 String16 comment(sym.comment); 2275 String16 typeComment; 2276 if (comment.size() <= 0) { 2277 comment = getAttributeComment(assets, name8, &typeComment); 2278 } else { 2279 getAttributeComment(assets, name8, &typeComment); 2280 } 2281 2282 uint32_t typeSpecFlags = 0; 2283 String16 name16(sym.name); 2284 assets->getIncludedResources().identifierForName( 2285 name16.string(), name16.size(), 2286 attr16.string(), attr16.size(), 2287 package16.string(), package16.size(), &typeSpecFlags); 2288 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 2289 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 2290 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2291 2292 AnnotationProcessor ann; 2293 fprintf(fp, "%s/**\n", indentStr); 2294 if (comment.size() > 0) { 2295 String8 cmt(comment); 2296 ann.preprocessComment(cmt); 2297 fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr); 2298 fprintf(fp, "%s %s\n", indentStr, cmt.string()); 2299 } else { 2300 fprintf(fp, 2301 "%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n" 2302 "%s attribute's value can be found in the {@link #%s} array.\n", 2303 indentStr, 2304 getSymbolPackage(name8, assets, pub).string(), 2305 getSymbolName(name8).string(), 2306 indentStr, nclassName.string()); 2307 } 2308 if (typeComment.size() > 0) { 2309 String8 cmt(typeComment); 2310 ann.preprocessComment(cmt); 2311 fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string()); 2312 } 2313 if (comment.size() > 0) { 2314 if (pub) { 2315 fprintf(fp, 2316 "%s <p>This corresponds to the global attribute\n" 2317 "%s resource symbol {@link %s.R.attr#%s}.\n", 2318 indentStr, indentStr, 2319 getSymbolPackage(name8, assets, true).string(), 2320 getSymbolName(name8).string()); 2321 } else { 2322 fprintf(fp, 2323 "%s <p>This is a private symbol.\n", indentStr); 2324 } 2325 } 2326 fprintf(fp, "%s @attr name %s:%s\n", indentStr, 2327 getSymbolPackage(name8, assets, pub).string(), 2328 getSymbolName(name8).string()); 2329 fprintf(fp, "%s*/\n", indentStr); 2330 ann.printAnnotations(fp, indentStr); 2331 2332 const char * id_format = nonConstantId ? 2333 "%spublic static int %s_%s = %d;\n" : 2334 "%spublic static final int %s_%s = %d;\n"; 2335 2336 fprintf(fp, 2337 id_format, 2338 indentStr, nclassName.string(), 2339 flattenSymbol(name8).string(), (int)pos); 2340 } 2341 } 2342 } 2343 2344 indent--; 2345 fprintf(fp, "%s};\n", getIndentSpace(indent)); 2346 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 2347} 2348 2349static status_t writeTextLayoutClasses( 2350 FILE* fp, const sp<AaptAssets>& assets, 2351 const sp<AaptSymbols>& symbols, bool includePrivate) 2352{ 2353 String16 attr16("attr"); 2354 String16 package16(assets->getPackage()); 2355 2356 bool hasErrors = false; 2357 2358 size_t i; 2359 size_t N = symbols->getNestedSymbols().size(); 2360 for (i=0; i<N; i++) { 2361 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2362 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2363 String8 nclassName(flattenSymbol(realClassName)); 2364 2365 SortedVector<uint32_t> idents; 2366 Vector<uint32_t> origOrder; 2367 Vector<bool> publicFlags; 2368 2369 size_t a; 2370 size_t NA = nsymbols->getSymbols().size(); 2371 for (a=0; a<NA; a++) { 2372 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 2373 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 2374 ? sym.int32Val : 0; 2375 bool isPublic = true; 2376 if (code == 0) { 2377 String16 name16(sym.name); 2378 uint32_t typeSpecFlags; 2379 code = assets->getIncludedResources().identifierForName( 2380 name16.string(), name16.size(), 2381 attr16.string(), attr16.size(), 2382 package16.string(), package16.size(), &typeSpecFlags); 2383 if (code == 0) { 2384 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 2385 nclassName.string(), sym.name.string()); 2386 hasErrors = true; 2387 } 2388 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2389 } 2390 idents.add(code); 2391 origOrder.add(code); 2392 publicFlags.add(isPublic); 2393 } 2394 2395 NA = idents.size(); 2396 2397 fprintf(fp, "int[] styleable %s {", nclassName.string()); 2398 2399 for (a=0; a<NA; a++) { 2400 if (a != 0) { 2401 fprintf(fp, ","); 2402 } 2403 fprintf(fp, " 0x%08x", idents[a]); 2404 } 2405 2406 fprintf(fp, " }\n"); 2407 2408 for (a=0; a<NA; a++) { 2409 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2410 if (pos >= 0) { 2411 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2412 if (!publicFlags.itemAt(a) && !includePrivate) { 2413 continue; 2414 } 2415 String8 name8(sym.name); 2416 String16 comment(sym.comment); 2417 String16 typeComment; 2418 if (comment.size() <= 0) { 2419 comment = getAttributeComment(assets, name8, &typeComment); 2420 } else { 2421 getAttributeComment(assets, name8, &typeComment); 2422 } 2423 2424 uint32_t typeSpecFlags = 0; 2425 String16 name16(sym.name); 2426 assets->getIncludedResources().identifierForName( 2427 name16.string(), name16.size(), 2428 attr16.string(), attr16.size(), 2429 package16.string(), package16.size(), &typeSpecFlags); 2430 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 2431 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 2432 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2433 2434 fprintf(fp, 2435 "int styleable %s_%s %d\n", 2436 nclassName.string(), 2437 flattenSymbol(name8).string(), (int)pos); 2438 } 2439 } 2440 } 2441 2442 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 2443} 2444 2445static status_t writeSymbolClass( 2446 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2447 const sp<AaptSymbols>& symbols, const String8& className, int indent, 2448 bool nonConstantId, bool emitCallback) 2449{ 2450 fprintf(fp, "%spublic %sfinal class %s {\n", 2451 getIndentSpace(indent), 2452 indent != 0 ? "static " : "", className.string()); 2453 indent++; 2454 2455 size_t i; 2456 status_t err = NO_ERROR; 2457 2458 const char * id_format = nonConstantId ? 2459 "%spublic static int %s=0x%08x;\n" : 2460 "%spublic static final int %s=0x%08x;\n"; 2461 2462 size_t N = symbols->getSymbols().size(); 2463 for (i=0; i<N; i++) { 2464 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2465 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2466 continue; 2467 } 2468 if (!assets->isJavaSymbol(sym, includePrivate)) { 2469 continue; 2470 } 2471 String8 name8(sym.name); 2472 String16 comment(sym.comment); 2473 bool haveComment = false; 2474 AnnotationProcessor ann; 2475 if (comment.size() > 0) { 2476 haveComment = true; 2477 String8 cmt(comment); 2478 ann.preprocessComment(cmt); 2479 fprintf(fp, 2480 "%s/** %s\n", 2481 getIndentSpace(indent), cmt.string()); 2482 } else if (sym.isPublic && !includePrivate) { 2483 sym.sourcePos.warning("No comment for public symbol %s:%s/%s", 2484 assets->getPackage().string(), className.string(), 2485 String8(sym.name).string()); 2486 } 2487 String16 typeComment(sym.typeComment); 2488 if (typeComment.size() > 0) { 2489 String8 cmt(typeComment); 2490 ann.preprocessComment(cmt); 2491 if (!haveComment) { 2492 haveComment = true; 2493 fprintf(fp, 2494 "%s/** %s\n", getIndentSpace(indent), cmt.string()); 2495 } else { 2496 fprintf(fp, 2497 "%s %s\n", getIndentSpace(indent), cmt.string()); 2498 } 2499 } 2500 if (haveComment) { 2501 fprintf(fp,"%s */\n", getIndentSpace(indent)); 2502 } 2503 ann.printAnnotations(fp, getIndentSpace(indent)); 2504 fprintf(fp, id_format, 2505 getIndentSpace(indent), 2506 flattenSymbol(name8).string(), (int)sym.int32Val); 2507 } 2508 2509 for (i=0; i<N; i++) { 2510 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2511 if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) { 2512 continue; 2513 } 2514 if (!assets->isJavaSymbol(sym, includePrivate)) { 2515 continue; 2516 } 2517 String8 name8(sym.name); 2518 String16 comment(sym.comment); 2519 AnnotationProcessor ann; 2520 if (comment.size() > 0) { 2521 String8 cmt(comment); 2522 ann.preprocessComment(cmt); 2523 fprintf(fp, 2524 "%s/** %s\n" 2525 "%s */\n", 2526 getIndentSpace(indent), cmt.string(), 2527 getIndentSpace(indent)); 2528 } else if (sym.isPublic && !includePrivate) { 2529 sym.sourcePos.warning("No comment for public symbol %s:%s/%s", 2530 assets->getPackage().string(), className.string(), 2531 String8(sym.name).string()); 2532 } 2533 ann.printAnnotations(fp, getIndentSpace(indent)); 2534 fprintf(fp, "%spublic static final String %s=\"%s\";\n", 2535 getIndentSpace(indent), 2536 flattenSymbol(name8).string(), sym.stringVal.string()); 2537 } 2538 2539 sp<AaptSymbols> styleableSymbols; 2540 2541 N = symbols->getNestedSymbols().size(); 2542 for (i=0; i<N; i++) { 2543 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2544 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2545 if (nclassName == "styleable") { 2546 styleableSymbols = nsymbols; 2547 } else { 2548 err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, 2549 indent, nonConstantId, false); 2550 } 2551 if (err != NO_ERROR) { 2552 return err; 2553 } 2554 } 2555 2556 if (styleableSymbols != NULL) { 2557 err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate, nonConstantId); 2558 if (err != NO_ERROR) { 2559 return err; 2560 } 2561 } 2562 2563 if (emitCallback) { 2564 fprintf(fp, "%spublic static void onResourcesLoaded(int packageId) {\n", 2565 getIndentSpace(indent)); 2566 writeResourceLoadedCallback(fp, assets, includePrivate, symbols, className, indent + 1); 2567 fprintf(fp, "%s}\n", getIndentSpace(indent)); 2568 } 2569 2570 indent--; 2571 fprintf(fp, "%s}\n", getIndentSpace(indent)); 2572 return NO_ERROR; 2573} 2574 2575static status_t writeTextSymbolClass( 2576 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2577 const sp<AaptSymbols>& symbols, const String8& className) 2578{ 2579 size_t i; 2580 status_t err = NO_ERROR; 2581 2582 size_t N = symbols->getSymbols().size(); 2583 for (i=0; i<N; i++) { 2584 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2585 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2586 continue; 2587 } 2588 2589 if (!assets->isJavaSymbol(sym, includePrivate)) { 2590 continue; 2591 } 2592 2593 String8 name8(sym.name); 2594 fprintf(fp, "int %s %s 0x%08x\n", 2595 className.string(), 2596 flattenSymbol(name8).string(), (int)sym.int32Val); 2597 } 2598 2599 N = symbols->getNestedSymbols().size(); 2600 for (i=0; i<N; i++) { 2601 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2602 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2603 if (nclassName == "styleable") { 2604 err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate); 2605 } else { 2606 err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName); 2607 } 2608 if (err != NO_ERROR) { 2609 return err; 2610 } 2611 } 2612 2613 return NO_ERROR; 2614} 2615 2616status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, 2617 const String8& package, bool includePrivate, bool emitCallback) 2618{ 2619 if (!bundle->getRClassDir()) { 2620 return NO_ERROR; 2621 } 2622 2623 const char* textSymbolsDest = bundle->getOutputTextSymbols(); 2624 2625 String8 R("R"); 2626 const size_t N = assets->getSymbols().size(); 2627 for (size_t i=0; i<N; i++) { 2628 sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i); 2629 String8 className(assets->getSymbols().keyAt(i)); 2630 String8 dest(bundle->getRClassDir()); 2631 2632 if (bundle->getMakePackageDirs()) { 2633 String8 pkg(package); 2634 const char* last = pkg.string(); 2635 const char* s = last-1; 2636 do { 2637 s++; 2638 if (s > last && (*s == '.' || *s == 0)) { 2639 String8 part(last, s-last); 2640 dest.appendPath(part); 2641#ifdef HAVE_MS_C_RUNTIME 2642 _mkdir(dest.string()); 2643#else 2644 mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); 2645#endif 2646 last = s+1; 2647 } 2648 } while (*s); 2649 } 2650 dest.appendPath(className); 2651 dest.append(".java"); 2652 FILE* fp = fopen(dest.string(), "w+"); 2653 if (fp == NULL) { 2654 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 2655 dest.string(), strerror(errno)); 2656 return UNKNOWN_ERROR; 2657 } 2658 if (bundle->getVerbose()) { 2659 printf(" Writing symbols for class %s.\n", className.string()); 2660 } 2661 2662 fprintf(fp, 2663 "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" 2664 " *\n" 2665 " * This class was automatically generated by the\n" 2666 " * aapt tool from the resource data it found. It\n" 2667 " * should not be modified by hand.\n" 2668 " */\n" 2669 "\n" 2670 "package %s;\n\n", package.string()); 2671 2672 status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, 2673 className, 0, bundle->getNonConstantId(), emitCallback); 2674 fclose(fp); 2675 if (err != NO_ERROR) { 2676 return err; 2677 } 2678 2679 if (textSymbolsDest != NULL && R == className) { 2680 String8 textDest(textSymbolsDest); 2681 textDest.appendPath(className); 2682 textDest.append(".txt"); 2683 2684 FILE* fp = fopen(textDest.string(), "w+"); 2685 if (fp == NULL) { 2686 fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n", 2687 textDest.string(), strerror(errno)); 2688 return UNKNOWN_ERROR; 2689 } 2690 if (bundle->getVerbose()) { 2691 printf(" Writing text symbols for class %s.\n", className.string()); 2692 } 2693 2694 status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols, 2695 className); 2696 fclose(fp); 2697 if (err != NO_ERROR) { 2698 return err; 2699 } 2700 } 2701 2702 // If we were asked to generate a dependency file, we'll go ahead and add this R.java 2703 // as a target in the dependency file right next to it. 2704 if (bundle->getGenDependencies() && R == className) { 2705 // Add this R.java to the dependency file 2706 String8 dependencyFile(bundle->getRClassDir()); 2707 dependencyFile.appendPath("R.java.d"); 2708 2709 FILE *fp = fopen(dependencyFile.string(), "a"); 2710 fprintf(fp,"%s \\\n", dest.string()); 2711 fclose(fp); 2712 } 2713 } 2714 2715 return NO_ERROR; 2716} 2717 2718 2719class ProguardKeepSet 2720{ 2721public: 2722 // { rule --> { file locations } } 2723 KeyedVector<String8, SortedVector<String8> > rules; 2724 2725 void add(const String8& rule, const String8& where); 2726}; 2727 2728void ProguardKeepSet::add(const String8& rule, const String8& where) 2729{ 2730 ssize_t index = rules.indexOfKey(rule); 2731 if (index < 0) { 2732 index = rules.add(rule, SortedVector<String8>()); 2733 } 2734 rules.editValueAt(index).add(where); 2735} 2736 2737void 2738addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName, 2739 const char* pkg, const String8& srcName, int line) 2740{ 2741 String8 className(inClassName); 2742 if (pkg != NULL) { 2743 // asdf --> package.asdf 2744 // .asdf .a.b --> package.asdf package.a.b 2745 // asdf.adsf --> asdf.asdf 2746 const char* p = className.string(); 2747 const char* q = strchr(p, '.'); 2748 if (p == q) { 2749 className = pkg; 2750 className.append(inClassName); 2751 } else if (q == NULL) { 2752 className = pkg; 2753 className.append("."); 2754 className.append(inClassName); 2755 } 2756 } 2757 2758 String8 rule("-keep class "); 2759 rule += className; 2760 rule += " { <init>(...); }"; 2761 2762 String8 location("view "); 2763 location += srcName; 2764 char lineno[20]; 2765 sprintf(lineno, ":%d", line); 2766 location += lineno; 2767 2768 keep->add(rule, location); 2769} 2770 2771void 2772addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName, 2773 const char* pkg, const String8& srcName, int line) 2774{ 2775 String8 rule("-keepclassmembers class * { *** "); 2776 rule += memberName; 2777 rule += "(...); }"; 2778 2779 String8 location("onClick "); 2780 location += srcName; 2781 char lineno[20]; 2782 sprintf(lineno, ":%d", line); 2783 location += lineno; 2784 2785 keep->add(rule, location); 2786} 2787 2788status_t 2789writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 2790{ 2791 status_t err; 2792 ResXMLTree tree; 2793 size_t len; 2794 ResXMLTree::event_code_t code; 2795 int depth = 0; 2796 bool inApplication = false; 2797 String8 error; 2798 sp<AaptGroup> assGroup; 2799 sp<AaptFile> assFile; 2800 String8 pkg; 2801 2802 // First, look for a package file to parse. This is required to 2803 // be able to generate the resource information. 2804 assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml")); 2805 if (assGroup == NULL) { 2806 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 2807 return -1; 2808 } 2809 2810 if (assGroup->getFiles().size() != 1) { 2811 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 2812 assGroup->getFiles().valueAt(0)->getPrintableSource().string()); 2813 } 2814 2815 assFile = assGroup->getFiles().valueAt(0); 2816 2817 err = parseXMLResource(assFile, &tree); 2818 if (err != NO_ERROR) { 2819 return err; 2820 } 2821 2822 tree.restart(); 2823 2824 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2825 if (code == ResXMLTree::END_TAG) { 2826 if (/* name == "Application" && */ depth == 2) { 2827 inApplication = false; 2828 } 2829 depth--; 2830 continue; 2831 } 2832 if (code != ResXMLTree::START_TAG) { 2833 continue; 2834 } 2835 depth++; 2836 String8 tag(tree.getElementName(&len)); 2837 // printf("Depth %d tag %s\n", depth, tag.string()); 2838 bool keepTag = false; 2839 if (depth == 1) { 2840 if (tag != "manifest") { 2841 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 2842 return -1; 2843 } 2844 pkg = AaptXml::getAttribute(tree, NULL, "package"); 2845 } else if (depth == 2) { 2846 if (tag == "application") { 2847 inApplication = true; 2848 keepTag = true; 2849 2850 String8 agent = AaptXml::getAttribute(tree, 2851 "http://schemas.android.com/apk/res/android", 2852 "backupAgent", &error); 2853 if (agent.length() > 0) { 2854 addProguardKeepRule(keep, agent, pkg.string(), 2855 assFile->getPrintableSource(), tree.getLineNumber()); 2856 } 2857 } else if (tag == "instrumentation") { 2858 keepTag = true; 2859 } 2860 } 2861 if (!keepTag && inApplication && depth == 3) { 2862 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 2863 keepTag = true; 2864 } 2865 } 2866 if (keepTag) { 2867 String8 name = AaptXml::getAttribute(tree, 2868 "http://schemas.android.com/apk/res/android", "name", &error); 2869 if (error != "") { 2870 fprintf(stderr, "ERROR: %s\n", error.string()); 2871 return -1; 2872 } 2873 if (name.length() > 0) { 2874 addProguardKeepRule(keep, name, pkg.string(), 2875 assFile->getPrintableSource(), tree.getLineNumber()); 2876 } 2877 } 2878 } 2879 2880 return NO_ERROR; 2881} 2882 2883struct NamespaceAttributePair { 2884 const char* ns; 2885 const char* attr; 2886 2887 NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {} 2888 NamespaceAttributePair() : ns(NULL), attr(NULL) {} 2889}; 2890 2891status_t 2892writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, 2893 const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs) 2894{ 2895 status_t err; 2896 ResXMLTree tree; 2897 size_t len; 2898 ResXMLTree::event_code_t code; 2899 2900 err = parseXMLResource(layoutFile, &tree); 2901 if (err != NO_ERROR) { 2902 return err; 2903 } 2904 2905 tree.restart(); 2906 2907 if (!startTags.isEmpty()) { 2908 bool haveStart = false; 2909 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2910 if (code != ResXMLTree::START_TAG) { 2911 continue; 2912 } 2913 String8 tag(tree.getElementName(&len)); 2914 const size_t numStartTags = startTags.size(); 2915 for (size_t i = 0; i < numStartTags; i++) { 2916 if (tag == startTags[i]) { 2917 haveStart = true; 2918 } 2919 } 2920 break; 2921 } 2922 if (!haveStart) { 2923 return NO_ERROR; 2924 } 2925 } 2926 2927 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2928 if (code != ResXMLTree::START_TAG) { 2929 continue; 2930 } 2931 String8 tag(tree.getElementName(&len)); 2932 2933 // If there is no '.', we'll assume that it's one of the built in names. 2934 if (strchr(tag.string(), '.')) { 2935 addProguardKeepRule(keep, tag, NULL, 2936 layoutFile->getPrintableSource(), tree.getLineNumber()); 2937 } else if (tagAttrPairs != NULL) { 2938 ssize_t tagIndex = tagAttrPairs->indexOfKey(tag); 2939 if (tagIndex >= 0) { 2940 const Vector<NamespaceAttributePair>& nsAttrVector = tagAttrPairs->valueAt(tagIndex); 2941 for (size_t i = 0; i < nsAttrVector.size(); i++) { 2942 const NamespaceAttributePair& nsAttr = nsAttrVector[i]; 2943 2944 ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr); 2945 if (attrIndex < 0) { 2946 // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n", 2947 // layoutFile->getPrintableSource().string(), tree.getLineNumber(), 2948 // tag.string(), nsAttr.ns, nsAttr.attr); 2949 } else { 2950 size_t len; 2951 addProguardKeepRule(keep, 2952 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 2953 layoutFile->getPrintableSource(), tree.getLineNumber()); 2954 } 2955 } 2956 } 2957 } 2958 ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick"); 2959 if (attrIndex >= 0) { 2960 size_t len; 2961 addProguardKeepMethodRule(keep, 2962 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 2963 layoutFile->getPrintableSource(), tree.getLineNumber()); 2964 } 2965 } 2966 2967 return NO_ERROR; 2968} 2969 2970static void addTagAttrPair(KeyedVector<String8, Vector<NamespaceAttributePair> >* dest, 2971 const char* tag, const char* ns, const char* attr) { 2972 String8 tagStr(tag); 2973 ssize_t index = dest->indexOfKey(tagStr); 2974 2975 if (index < 0) { 2976 Vector<NamespaceAttributePair> vector; 2977 vector.add(NamespaceAttributePair(ns, attr)); 2978 dest->add(tagStr, vector); 2979 } else { 2980 dest->editValueAt(index).add(NamespaceAttributePair(ns, attr)); 2981 } 2982} 2983 2984status_t 2985writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 2986{ 2987 status_t err; 2988 const String8 kTransition("transition"); 2989 const String8 kTransitionPrefix("transition-"); 2990 2991 // tag:attribute pairs that should be checked in layout files. 2992 KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs; 2993 addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class"); 2994 addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class"); 2995 addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name"); 2996 2997 // tag:attribute pairs that should be checked in xml files. 2998 KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs; 2999 addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment"); 3000 addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment"); 3001 3002 // tag:attribute pairs that should be checked in transition files. 3003 KeyedVector<String8, Vector<NamespaceAttributePair> > kTransitionTagAttrPairs; 3004 addTagAttrPair(&kTransitionTagAttrPairs, kTransition.string(), NULL, "class"); 3005 3006 const Vector<sp<AaptDir> >& dirs = assets->resDirs(); 3007 const size_t K = dirs.size(); 3008 for (size_t k=0; k<K; k++) { 3009 const sp<AaptDir>& d = dirs.itemAt(k); 3010 const String8& dirName = d->getLeaf(); 3011 Vector<String8> startTags; 3012 const char* startTag = NULL; 3013 const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL; 3014 if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { 3015 tagAttrPairs = &kLayoutTagAttrPairs; 3016 } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) { 3017 startTags.add(String8("PreferenceScreen")); 3018 startTags.add(String8("preference-headers")); 3019 tagAttrPairs = &kXmlTagAttrPairs; 3020 } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) { 3021 startTags.add(String8("menu")); 3022 tagAttrPairs = NULL; 3023 } else if (dirName == kTransition || (strncmp(dirName.string(), kTransitionPrefix.string(), 3024 kTransitionPrefix.size()) == 0)) { 3025 tagAttrPairs = &kTransitionTagAttrPairs; 3026 } else { 3027 continue; 3028 } 3029 3030 const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles(); 3031 const size_t N = groups.size(); 3032 for (size_t i=0; i<N; i++) { 3033 const sp<AaptGroup>& group = groups.valueAt(i); 3034 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles(); 3035 const size_t M = files.size(); 3036 for (size_t j=0; j<M; j++) { 3037 err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs); 3038 if (err < 0) { 3039 return err; 3040 } 3041 } 3042 } 3043 } 3044 // Handle the overlays 3045 sp<AaptAssets> overlay = assets->getOverlay(); 3046 if (overlay.get()) { 3047 return writeProguardForLayouts(keep, overlay); 3048 } 3049 3050 return NO_ERROR; 3051} 3052 3053status_t 3054writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets) 3055{ 3056 status_t err = -1; 3057 3058 if (!bundle->getProguardFile()) { 3059 return NO_ERROR; 3060 } 3061 3062 ProguardKeepSet keep; 3063 3064 err = writeProguardForAndroidManifest(&keep, assets); 3065 if (err < 0) { 3066 return err; 3067 } 3068 3069 err = writeProguardForLayouts(&keep, assets); 3070 if (err < 0) { 3071 return err; 3072 } 3073 3074 FILE* fp = fopen(bundle->getProguardFile(), "w+"); 3075 if (fp == NULL) { 3076 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 3077 bundle->getProguardFile(), strerror(errno)); 3078 return UNKNOWN_ERROR; 3079 } 3080 3081 const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules; 3082 const size_t N = rules.size(); 3083 for (size_t i=0; i<N; i++) { 3084 const SortedVector<String8>& locations = rules.valueAt(i); 3085 const size_t M = locations.size(); 3086 for (size_t j=0; j<M; j++) { 3087 fprintf(fp, "# %s\n", locations.itemAt(j).string()); 3088 } 3089 fprintf(fp, "%s\n\n", rules.keyAt(i).string()); 3090 } 3091 fclose(fp); 3092 3093 return err; 3094} 3095 3096// Loops through the string paths and writes them to the file pointer 3097// Each file path is written on its own line with a terminating backslash. 3098status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp) 3099{ 3100 status_t deps = -1; 3101 for (size_t file_i = 0; file_i < files->size(); ++file_i) { 3102 // Add the full file path to the dependency file 3103 fprintf(fp, "%s \\\n", files->itemAt(file_i).string()); 3104 deps++; 3105 } 3106 return deps; 3107} 3108 3109status_t 3110writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw) 3111{ 3112 status_t deps = -1; 3113 deps += writePathsToFile(assets->getFullResPaths(), fp); 3114 if (includeRaw) { 3115 deps += writePathsToFile(assets->getFullAssetPaths(), fp); 3116 } 3117 return deps; 3118} 3119