Home | History | Annotate | Download | only in aapt
      1 //
      2 // Copyright 2006 The Android Open Source Project
      3 //
      4 // Build resource files from raw assets.
      5 //
      6 #include "Main.h"
      7 #include "AaptAssets.h"
      8 #include "StringPool.h"
      9 #include "XMLNode.h"
     10 #include "ResourceTable.h"
     11 #include "Images.h"
     12 
     13 #include "CrunchCache.h"
     14 #include "FileFinder.h"
     15 #include "CacheUpdater.h"
     16 
     17 #if HAVE_PRINTF_ZD
     18 #  define ZD "%zd"
     19 #  define ZD_TYPE ssize_t
     20 #else
     21 #  define ZD "%ld"
     22 #  define ZD_TYPE long
     23 #endif
     24 
     25 #define NOISY(x) // x
     26 
     27 // ==========================================================================
     28 // ==========================================================================
     29 // ==========================================================================
     30 
     31 class PackageInfo
     32 {
     33 public:
     34     PackageInfo()
     35     {
     36     }
     37     ~PackageInfo()
     38     {
     39     }
     40 
     41     status_t parsePackage(const sp<AaptGroup>& grp);
     42 };
     43 
     44 // ==========================================================================
     45 // ==========================================================================
     46 // ==========================================================================
     47 
     48 static String8 parseResourceName(const String8& leaf)
     49 {
     50     const char* firstDot = strchr(leaf.string(), '.');
     51     const char* str = leaf.string();
     52 
     53     if (firstDot) {
     54         return String8(str, firstDot-str);
     55     } else {
     56         return String8(str);
     57     }
     58 }
     59 
     60 ResourceTypeSet::ResourceTypeSet()
     61     :RefBase(),
     62      KeyedVector<String8,sp<AaptGroup> >()
     63 {
     64 }
     65 
     66 FilePathStore::FilePathStore()
     67     :RefBase(),
     68      Vector<String8>()
     69 {
     70 }
     71 
     72 class ResourceDirIterator
     73 {
     74 public:
     75     ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType)
     76         : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0)
     77     {
     78     }
     79 
     80     inline const sp<AaptGroup>& getGroup() const { return mGroup; }
     81     inline const sp<AaptFile>& getFile() const { return mFile; }
     82 
     83     inline const String8& getBaseName() const { return mBaseName; }
     84     inline const String8& getLeafName() const { return mLeafName; }
     85     inline String8 getPath() const { return mPath; }
     86     inline const ResTable_config& getParams() const { return mParams; }
     87 
     88     enum {
     89         EOD = 1
     90     };
     91 
     92     ssize_t next()
     93     {
     94         while (true) {
     95             sp<AaptGroup> group;
     96             sp<AaptFile> file;
     97 
     98             // Try to get next file in this current group.
     99             if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) {
    100                 group = mGroup;
    101                 file = group->getFiles().valueAt(mGroupPos++);
    102 
    103             // Try to get the next group/file in this directory
    104             } else if (mSetPos < mSet->size()) {
    105                 mGroup = group = mSet->valueAt(mSetPos++);
    106                 if (group->getFiles().size() < 1) {
    107                     continue;
    108                 }
    109                 file = group->getFiles().valueAt(0);
    110                 mGroupPos = 1;
    111 
    112             // All done!
    113             } else {
    114                 return EOD;
    115             }
    116 
    117             mFile = file;
    118 
    119             String8 leaf(group->getLeaf());
    120             mLeafName = String8(leaf);
    121             mParams = file->getGroupEntry().toParams();
    122             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",
    123                    group->getPath().string(), mParams.mcc, mParams.mnc,
    124                    mParams.language[0] ? mParams.language[0] : '-',
    125                    mParams.language[1] ? mParams.language[1] : '-',
    126                    mParams.country[0] ? mParams.country[0] : '-',
    127                    mParams.country[1] ? mParams.country[1] : '-',
    128                    mParams.orientation, mParams.uiMode,
    129                    mParams.density, mParams.touchscreen, mParams.keyboard,
    130                    mParams.inputFlags, mParams.navigation));
    131             mPath = "res";
    132             mPath.appendPath(file->getGroupEntry().toDirName(mResType));
    133             mPath.appendPath(leaf);
    134             mBaseName = parseResourceName(leaf);
    135             if (mBaseName == "") {
    136                 fprintf(stderr, "Error: malformed resource filename %s\n",
    137                         file->getPrintableSource().string());
    138                 return UNKNOWN_ERROR;
    139             }
    140 
    141             NOISY(printf("file name=%s\n", mBaseName.string()));
    142 
    143             return NO_ERROR;
    144         }
    145     }
    146 
    147 private:
    148     String8 mResType;
    149 
    150     const sp<ResourceTypeSet> mSet;
    151     size_t mSetPos;
    152 
    153     sp<AaptGroup> mGroup;
    154     size_t mGroupPos;
    155 
    156     sp<AaptFile> mFile;
    157     String8 mBaseName;
    158     String8 mLeafName;
    159     String8 mPath;
    160     ResTable_config mParams;
    161 };
    162 
    163 // ==========================================================================
    164 // ==========================================================================
    165 // ==========================================================================
    166 
    167 bool isValidResourceType(const String8& type)
    168 {
    169     return type == "anim" || type == "animator" || type == "interpolator"
    170         || type == "drawable" || type == "layout"
    171         || type == "values" || type == "xml" || type == "raw"
    172         || type == "color" || type == "menu" || type == "mipmap";
    173 }
    174 
    175 static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
    176 {
    177     sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
    178     sp<AaptFile> file;
    179     if (group != NULL) {
    180         file = group->getFiles().valueFor(AaptGroupEntry());
    181         if (file != NULL) {
    182             return file;
    183         }
    184     }
    185 
    186     if (!makeIfNecessary) {
    187         return NULL;
    188     }
    189     return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
    190                             NULL, String8());
    191 }
    192 
    193 static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
    194     const sp<AaptGroup>& grp)
    195 {
    196     if (grp->getFiles().size() != 1) {
    197         fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
    198                 grp->getFiles().valueAt(0)->getPrintableSource().string());
    199     }
    200 
    201     sp<AaptFile> file = grp->getFiles().valueAt(0);
    202 
    203     ResXMLTree block;
    204     status_t err = parseXMLResource(file, &block);
    205     if (err != NO_ERROR) {
    206         return err;
    207     }
    208     //printXMLBlock(&block);
    209 
    210     ResXMLTree::event_code_t code;
    211     while ((code=block.next()) != ResXMLTree::START_TAG
    212            && code != ResXMLTree::END_DOCUMENT
    213            && code != ResXMLTree::BAD_DOCUMENT) {
    214     }
    215 
    216     size_t len;
    217     if (code != ResXMLTree::START_TAG) {
    218         fprintf(stderr, "%s:%d: No start tag found\n",
    219                 file->getPrintableSource().string(), block.getLineNumber());
    220         return UNKNOWN_ERROR;
    221     }
    222     if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) {
    223         fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n",
    224                 file->getPrintableSource().string(), block.getLineNumber(),
    225                 String8(block.getElementName(&len)).string());
    226         return UNKNOWN_ERROR;
    227     }
    228 
    229     ssize_t nameIndex = block.indexOfAttribute(NULL, "package");
    230     if (nameIndex < 0) {
    231         fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n",
    232                 file->getPrintableSource().string(), block.getLineNumber());
    233         return UNKNOWN_ERROR;
    234     }
    235 
    236     assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len)));
    237 
    238     String16 uses_sdk16("uses-sdk");
    239     while ((code=block.next()) != ResXMLTree::END_DOCUMENT
    240            && code != ResXMLTree::BAD_DOCUMENT) {
    241         if (code == ResXMLTree::START_TAG) {
    242             if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) {
    243                 ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
    244                                                              "minSdkVersion");
    245                 if (minSdkIndex >= 0) {
    246                     const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
    247                     const char* minSdk8 = strdup(String8(minSdk16).string());
    248                     bundle->setManifestMinSdkVersion(minSdk8);
    249                 }
    250             }
    251         }
    252     }
    253 
    254     return NO_ERROR;
    255 }
    256 
    257 // ==========================================================================
    258 // ==========================================================================
    259 // ==========================================================================
    260 
    261 static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,
    262                                   ResourceTable* table,
    263                                   const sp<ResourceTypeSet>& set,
    264                                   const char* resType)
    265 {
    266     String8 type8(resType);
    267     String16 type16(resType);
    268 
    269     bool hasErrors = false;
    270 
    271     ResourceDirIterator it(set, String8(resType));
    272     ssize_t res;
    273     while ((res=it.next()) == NO_ERROR) {
    274         if (bundle->getVerbose()) {
    275             printf("    (new resource id %s from %s)\n",
    276                    it.getBaseName().string(), it.getFile()->getPrintableSource().string());
    277         }
    278         String16 baseName(it.getBaseName());
    279         const char16_t* str = baseName.string();
    280         const char16_t* const end = str + baseName.size();
    281         while (str < end) {
    282             if (!((*str >= 'a' && *str <= 'z')
    283                     || (*str >= '0' && *str <= '9')
    284                     || *str == '_' || *str == '.')) {
    285                 fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",
    286                         it.getPath().string());
    287                 hasErrors = true;
    288             }
    289             str++;
    290         }
    291         String8 resPath = it.getPath();
    292         resPath.convertToResPath();
    293         table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),
    294                         type16,
    295                         baseName,
    296                         String16(resPath),
    297                         NULL,
    298                         &it.getParams());
    299         assets->addResource(it.getLeafName(), resPath, it.getFile(), type8);
    300     }
    301 
    302     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
    303 }
    304 
    305 static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
    306                           const sp<ResourceTypeSet>& set, const char* type)
    307 {
    308     bool hasErrors = false;
    309     ssize_t res = NO_ERROR;
    310     if (bundle->getUseCrunchCache() == false) {
    311         ResourceDirIterator it(set, String8(type));
    312         Vector<sp<AaptFile> > newNameFiles;
    313         Vector<String8> newNamePaths;
    314         while ((res=it.next()) == NO_ERROR) {
    315             res = preProcessImage(bundle, assets, it.getFile(), NULL);
    316             if (res < NO_ERROR) {
    317                 hasErrors = true;
    318             }
    319         }
    320     }
    321     return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
    322 }
    323 
    324 status_t postProcessImages(const sp<AaptAssets>& assets,
    325                            ResourceTable* table,
    326                            const sp<ResourceTypeSet>& set)
    327 {
    328     ResourceDirIterator it(set, String8("drawable"));
    329     bool hasErrors = false;
    330     ssize_t res;
    331     while ((res=it.next()) == NO_ERROR) {
    332         res = postProcessImage(assets, table, it.getFile());
    333         if (res < NO_ERROR) {
    334             hasErrors = true;
    335         }
    336     }
    337 
    338     return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
    339 }
    340 
    341 static void collect_files(const sp<AaptDir>& dir,
    342         KeyedVector<String8, sp<ResourceTypeSet> >* resources)
    343 {
    344     const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles();
    345     int N = groups.size();
    346     for (int i=0; i<N; i++) {
    347         String8 leafName = groups.keyAt(i);
    348         const sp<AaptGroup>& group = groups.valueAt(i);
    349 
    350         const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files
    351                 = group->getFiles();
    352 
    353         if (files.size() == 0) {
    354             continue;
    355         }
    356 
    357         String8 resType = files.valueAt(0)->getResourceType();
    358 
    359         ssize_t index = resources->indexOfKey(resType);
    360 
    361         if (index < 0) {
    362             sp<ResourceTypeSet> set = new ResourceTypeSet();
    363             NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n",
    364                     leafName.string(), group->getPath().string(), group.get()));
    365             set->add(leafName, group);
    366             resources->add(resType, set);
    367         } else {
    368             sp<ResourceTypeSet> set = resources->valueAt(index);
    369             index = set->indexOfKey(leafName);
    370             if (index < 0) {
    371                 NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n",
    372                         leafName.string(), group->getPath().string(), group.get()));
    373                 set->add(leafName, group);
    374             } else {
    375                 sp<AaptGroup> existingGroup = set->valueAt(index);
    376                 NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n",
    377                         leafName.string(), group->getPath().string(), group.get()));
    378                 for (size_t j=0; j<files.size(); j++) {
    379                     NOISY(printf("Adding file %s in group %s resType %s\n",
    380                         files.valueAt(j)->getSourceFile().string(),
    381                         files.keyAt(j).toDirName(String8()).string(),
    382                         resType.string()));
    383                     status_t err = existingGroup->addFile(files.valueAt(j));
    384                 }
    385             }
    386         }
    387     }
    388 }
    389 
    390 static void collect_files(const sp<AaptAssets>& ass,
    391         KeyedVector<String8, sp<ResourceTypeSet> >* resources)
    392 {
    393     const Vector<sp<AaptDir> >& dirs = ass->resDirs();
    394     int N = dirs.size();
    395 
    396     for (int i=0; i<N; i++) {
    397         sp<AaptDir> d = dirs.itemAt(i);
    398         NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
    399                 d->getLeaf().string()));
    400         collect_files(d, resources);
    401 
    402         // don't try to include the res dir
    403         NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string()));
    404         ass->removeDir(d->getLeaf());
    405     }
    406 }
    407 
    408 enum {
    409     ATTR_OKAY = -1,
    410     ATTR_NOT_FOUND = -2,
    411     ATTR_LEADING_SPACES = -3,
    412     ATTR_TRAILING_SPACES = -4
    413 };
    414 static int validateAttr(const String8& path, const ResTable& table,
    415         const ResXMLParser& parser,
    416         const char* ns, const char* attr, const char* validChars, bool required)
    417 {
    418     size_t len;
    419 
    420     ssize_t index = parser.indexOfAttribute(ns, attr);
    421     const uint16_t* str;
    422     Res_value value;
    423     if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) {
    424         const ResStringPool* pool = &parser.getStrings();
    425         if (value.dataType == Res_value::TYPE_REFERENCE) {
    426             uint32_t specFlags = 0;
    427             int strIdx;
    428             if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) {
    429                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n",
    430                         path.string(), parser.getLineNumber(),
    431                         String8(parser.getElementName(&len)).string(), attr,
    432                         value.data);
    433                 return ATTR_NOT_FOUND;
    434             }
    435 
    436             pool = table.getTableStringBlock(strIdx);
    437             #if 0
    438             if (pool != NULL) {
    439                 str = pool->stringAt(value.data, &len);
    440             }
    441             printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr,
    442                     specFlags, strIdx, str != NULL ? String8(str).string() : "???");
    443             #endif
    444             if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) {
    445                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n",
    446                         path.string(), parser.getLineNumber(),
    447                         String8(parser.getElementName(&len)).string(), attr,
    448                         specFlags);
    449                 return ATTR_NOT_FOUND;
    450             }
    451         }
    452         if (value.dataType == Res_value::TYPE_STRING) {
    453             if (pool == NULL) {
    454                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n",
    455                         path.string(), parser.getLineNumber(),
    456                         String8(parser.getElementName(&len)).string(), attr);
    457                 return ATTR_NOT_FOUND;
    458             }
    459             if ((str=pool->stringAt(value.data, &len)) == NULL) {
    460                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n",
    461                         path.string(), parser.getLineNumber(),
    462                         String8(parser.getElementName(&len)).string(), attr);
    463                 return ATTR_NOT_FOUND;
    464             }
    465         } else {
    466             fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n",
    467                     path.string(), parser.getLineNumber(),
    468                     String8(parser.getElementName(&len)).string(), attr,
    469                     value.dataType);
    470             return ATTR_NOT_FOUND;
    471         }
    472         if (validChars) {
    473             for (size_t i=0; i<len; i++) {
    474                 uint16_t c = str[i];
    475                 const char* p = validChars;
    476                 bool okay = false;
    477                 while (*p) {
    478                     if (c == *p) {
    479                         okay = true;
    480                         break;
    481                     }
    482                     p++;
    483                 }
    484                 if (!okay) {
    485                     fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n",
    486                             path.string(), parser.getLineNumber(),
    487                             String8(parser.getElementName(&len)).string(), attr, (char)str[i]);
    488                     return (int)i;
    489                 }
    490             }
    491         }
    492         if (*str == ' ') {
    493             fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n",
    494                     path.string(), parser.getLineNumber(),
    495                     String8(parser.getElementName(&len)).string(), attr);
    496             return ATTR_LEADING_SPACES;
    497         }
    498         if (str[len-1] == ' ') {
    499             fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n",
    500                     path.string(), parser.getLineNumber(),
    501                     String8(parser.getElementName(&len)).string(), attr);
    502             return ATTR_TRAILING_SPACES;
    503         }
    504         return ATTR_OKAY;
    505     }
    506     if (required) {
    507         fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n",
    508                 path.string(), parser.getLineNumber(),
    509                 String8(parser.getElementName(&len)).string(), attr);
    510         return ATTR_NOT_FOUND;
    511     }
    512     return ATTR_OKAY;
    513 }
    514 
    515 static void checkForIds(const String8& path, ResXMLParser& parser)
    516 {
    517     ResXMLTree::event_code_t code;
    518     while ((code=parser.next()) != ResXMLTree::END_DOCUMENT
    519            && code > ResXMLTree::BAD_DOCUMENT) {
    520         if (code == ResXMLTree::START_TAG) {
    521             ssize_t index = parser.indexOfAttribute(NULL, "id");
    522             if (index >= 0) {
    523                 fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
    524                         path.string(), parser.getLineNumber());
    525             }
    526         }
    527     }
    528 }
    529 
    530 static bool applyFileOverlay(Bundle *bundle,
    531                              const sp<AaptAssets>& assets,
    532                              sp<ResourceTypeSet> *baseSet,
    533                              const char *resType)
    534 {
    535     if (bundle->getVerbose()) {
    536         printf("applyFileOverlay for %s\n", resType);
    537     }
    538 
    539     // Replace any base level files in this category with any found from the overlay
    540     // Also add any found only in the overlay.
    541     sp<AaptAssets> overlay = assets->getOverlay();
    542     String8 resTypeString(resType);
    543 
    544     // work through the linked list of overlays
    545     while (overlay.get()) {
    546         KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources();
    547 
    548         // get the overlay resources of the requested type
    549         ssize_t index = overlayRes->indexOfKey(resTypeString);
    550         if (index >= 0) {
    551             sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index);
    552 
    553             // for each of the resources, check for a match in the previously built
    554             // non-overlay "baseset".
    555             size_t overlayCount = overlaySet->size();
    556             for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
    557                 if (bundle->getVerbose()) {
    558                     printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
    559                 }
    560                 size_t baseIndex = UNKNOWN_ERROR;
    561                 if (baseSet->get() != NULL) {
    562                     baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
    563                 }
    564                 if (baseIndex < UNKNOWN_ERROR) {
    565                     // look for same flavor.  For a given file (strings.xml, for example)
    566                     // there may be a locale specific or other flavors - we want to match
    567                     // the same flavor.
    568                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
    569                     sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex);
    570 
    571                     DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
    572                             overlayGroup->getFiles();
    573                     if (bundle->getVerbose()) {
    574                         DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
    575                                 baseGroup->getFiles();
    576                         for (size_t i=0; i < baseFiles.size(); i++) {
    577                             printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i,
    578                                     baseFiles.keyAt(i).toString().string());
    579                         }
    580                         for (size_t i=0; i < overlayFiles.size(); i++) {
    581                             printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i,
    582                                     overlayFiles.keyAt(i).toString().string());
    583                         }
    584                     }
    585 
    586                     size_t overlayGroupSize = overlayFiles.size();
    587                     for (size_t overlayGroupIndex = 0;
    588                             overlayGroupIndex<overlayGroupSize;
    589                             overlayGroupIndex++) {
    590                         size_t baseFileIndex =
    591                                 baseGroup->getFiles().indexOfKey(overlayFiles.
    592                                 keyAt(overlayGroupIndex));
    593                         if (baseFileIndex < UNKNOWN_ERROR) {
    594                             if (bundle->getVerbose()) {
    595                                 printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
    596                                         (ZD_TYPE) baseFileIndex,
    597                                         overlayGroup->getLeaf().string(),
    598                                         overlayFiles.keyAt(overlayGroupIndex).toString().string());
    599                             }
    600                             baseGroup->removeFile(baseFileIndex);
    601                         } else {
    602                             // didn't find a match fall through and add it..
    603                             if (true || bundle->getVerbose()) {
    604                                 printf("nothing matches overlay file %s, for flavor %s\n",
    605                                         overlayGroup->getLeaf().string(),
    606                                         overlayFiles.keyAt(overlayGroupIndex).toString().string());
    607                             }
    608                         }
    609                         baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
    610                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
    611                     }
    612                 } else {
    613                     if (baseSet->get() == NULL) {
    614                         *baseSet = new ResourceTypeSet();
    615                         assets->getResources()->add(String8(resType), *baseSet);
    616                     }
    617                     // this group doesn't exist (a file that's only in the overlay)
    618                     (*baseSet)->add(overlaySet->keyAt(overlayIndex),
    619                             overlaySet->valueAt(overlayIndex));
    620                     // make sure all flavors are defined in the resources.
    621                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
    622                     DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
    623                             overlayGroup->getFiles();
    624                     size_t overlayGroupSize = overlayFiles.size();
    625                     for (size_t overlayGroupIndex = 0;
    626                             overlayGroupIndex<overlayGroupSize;
    627                             overlayGroupIndex++) {
    628                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
    629                     }
    630                 }
    631             }
    632             // this overlay didn't have resources for this type
    633         }
    634         // try next overlay
    635         overlay = overlay->getOverlay();
    636     }
    637     return true;
    638 }
    639 
    640 void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
    641         const char* attr8, const char* value)
    642 {
    643     if (value == NULL) {
    644         return;
    645     }
    646 
    647     const String16 ns(ns8);
    648     const String16 attr(attr8);
    649 
    650     if (node->getAttribute(ns, attr) != NULL) {
    651         fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
    652                         " using existing value in manifest.\n",
    653                 String8(attr).string(), String8(ns).string());
    654         return;
    655     }
    656 
    657     node->addAttribute(ns, attr, String16(value));
    658 }
    659 
    660 static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
    661         const String16& attrName) {
    662     XMLNode::attribute_entry* attr = node->editAttribute(
    663             String16("http://schemas.android.com/apk/res/android"), attrName);
    664     if (attr != NULL) {
    665         String8 name(attr->string);
    666 
    667         // asdf     --> package.asdf
    668         // .asdf  .a.b  --> package.asdf package.a.b
    669         // asdf.adsf --> asdf.asdf
    670         String8 className;
    671         const char* p = name.string();
    672         const char* q = strchr(p, '.');
    673         if (p == q) {
    674             className += package;
    675             className += name;
    676         } else if (q == NULL) {
    677             className += package;
    678             className += ".";
    679             className += name;
    680         } else {
    681             className += name;
    682         }
    683         NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string()));
    684         attr->string.setTo(String16(className));
    685     }
    686 }
    687 
    688 status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
    689 {
    690     root = root->searchElement(String16(), String16("manifest"));
    691     if (root == NULL) {
    692         fprintf(stderr, "No <manifest> tag.\n");
    693         return UNKNOWN_ERROR;
    694     }
    695 
    696     addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
    697             bundle->getVersionCode());
    698     addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
    699             bundle->getVersionName());
    700 
    701     if (bundle->getMinSdkVersion() != NULL
    702             || bundle->getTargetSdkVersion() != NULL
    703             || bundle->getMaxSdkVersion() != NULL) {
    704         sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk"));
    705         if (vers == NULL) {
    706             vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk"));
    707             root->insertChildAt(vers, 0);
    708         }
    709 
    710         addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
    711                 bundle->getMinSdkVersion());
    712         addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
    713                 bundle->getTargetSdkVersion());
    714         addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
    715                 bundle->getMaxSdkVersion());
    716     }
    717 
    718     if (bundle->getDebugMode()) {
    719         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
    720         if (application != NULL) {
    721             addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true");
    722         }
    723     }
    724 
    725     // Deal with manifest package name overrides
    726     const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
    727     if (manifestPackageNameOverride != NULL) {
    728         // Update the actual package name
    729         XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package"));
    730         if (attr == NULL) {
    731             fprintf(stderr, "package name is required with --rename-manifest-package.\n");
    732             return UNKNOWN_ERROR;
    733         }
    734         String8 origPackage(attr->string);
    735         attr->string.setTo(String16(manifestPackageNameOverride));
    736         NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride));
    737 
    738         // Make class names fully qualified
    739         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
    740         if (application != NULL) {
    741             fullyQualifyClassName(origPackage, application, String16("name"));
    742             fullyQualifyClassName(origPackage, application, String16("backupAgent"));
    743 
    744             Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren());
    745             for (size_t i = 0; i < children.size(); i++) {
    746                 sp<XMLNode> child = children.editItemAt(i);
    747                 String8 tag(child->getElementName());
    748                 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
    749                     fullyQualifyClassName(origPackage, child, String16("name"));
    750                 } else if (tag == "activity-alias") {
    751                     fullyQualifyClassName(origPackage, child, String16("name"));
    752                     fullyQualifyClassName(origPackage, child, String16("targetActivity"));
    753                 }
    754             }
    755         }
    756     }
    757 
    758     // Deal with manifest package name overrides
    759     const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride();
    760     if (instrumentationPackageNameOverride != NULL) {
    761         // Fix up instrumentation targets.
    762         Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren());
    763         for (size_t i = 0; i < children.size(); i++) {
    764             sp<XMLNode> child = children.editItemAt(i);
    765             String8 tag(child->getElementName());
    766             if (tag == "instrumentation") {
    767                 XMLNode::attribute_entry* attr = child->editAttribute(
    768                         String16("http://schemas.android.com/apk/res/android"), String16("targetPackage"));
    769                 if (attr != NULL) {
    770                     attr->string.setTo(String16(instrumentationPackageNameOverride));
    771                 }
    772             }
    773         }
    774     }
    775 
    776     return NO_ERROR;
    777 }
    778 
    779 #define ASSIGN_IT(n) \
    780         do { \
    781             ssize_t index = resources->indexOfKey(String8(#n)); \
    782             if (index >= 0) { \
    783                 n ## s = resources->valueAt(index); \
    784             } \
    785         } while (0)
    786 
    787 status_t updatePreProcessedCache(Bundle* bundle)
    788 {
    789     #if BENCHMARK
    790     fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n");
    791     long startPNGTime = clock();
    792     #endif /* BENCHMARK */
    793 
    794     String8 source(bundle->getResourceSourceDirs()[0]);
    795     String8 dest(bundle->getCrunchedOutputDir());
    796 
    797     FileFinder* ff = new SystemFileFinder();
    798     CrunchCache cc(source,dest,ff);
    799 
    800     CacheUpdater* cu = new SystemCacheUpdater(bundle);
    801     size_t numFiles = cc.crunch(cu);
    802 
    803     if (bundle->getVerbose())
    804         fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles);
    805 
    806     delete ff;
    807     delete cu;
    808 
    809     #if BENCHMARK
    810     fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n"
    811             ,(clock() - startPNGTime)/1000.0);
    812     #endif /* BENCHMARK */
    813     return 0;
    814 }
    815 
    816 status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
    817 {
    818     // First, look for a package file to parse.  This is required to
    819     // be able to generate the resource information.
    820     sp<AaptGroup> androidManifestFile =
    821             assets->getFiles().valueFor(String8("AndroidManifest.xml"));
    822     if (androidManifestFile == NULL) {
    823         fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
    824         return UNKNOWN_ERROR;
    825     }
    826 
    827     status_t err = parsePackage(bundle, assets, androidManifestFile);
    828     if (err != NO_ERROR) {
    829         return err;
    830     }
    831 
    832     NOISY(printf("Creating resources for package %s\n",
    833                  assets->getPackage().string()));
    834 
    835     ResourceTable table(bundle, String16(assets->getPackage()));
    836     err = table.addIncludedResources(bundle, assets);
    837     if (err != NO_ERROR) {
    838         return err;
    839     }
    840 
    841     NOISY(printf("Found %d included resource packages\n", (int)table.size()));
    842 
    843     // Standard flags for compiled XML and optional UTF-8 encoding
    844     int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
    845 
    846     /* Only enable UTF-8 if the caller of aapt didn't specifically
    847      * request UTF-16 encoding and the parameters of this package
    848      * allow UTF-8 to be used.
    849      */
    850     if (!bundle->getWantUTF16()
    851             && bundle->isMinSdkAtLeast(SDK_FROYO)) {
    852         xmlFlags |= XML_COMPILE_UTF8;
    853     }
    854 
    855     // --------------------------------------------------------------
    856     // First, gather all resource information.
    857     // --------------------------------------------------------------
    858 
    859     // resType -> leafName -> group
    860     KeyedVector<String8, sp<ResourceTypeSet> > *resources =
    861             new KeyedVector<String8, sp<ResourceTypeSet> >;
    862     collect_files(assets, resources);
    863 
    864     sp<ResourceTypeSet> drawables;
    865     sp<ResourceTypeSet> layouts;
    866     sp<ResourceTypeSet> anims;
    867     sp<ResourceTypeSet> animators;
    868     sp<ResourceTypeSet> interpolators;
    869     sp<ResourceTypeSet> xmls;
    870     sp<ResourceTypeSet> raws;
    871     sp<ResourceTypeSet> colors;
    872     sp<ResourceTypeSet> menus;
    873     sp<ResourceTypeSet> mipmaps;
    874 
    875     ASSIGN_IT(drawable);
    876     ASSIGN_IT(layout);
    877     ASSIGN_IT(anim);
    878     ASSIGN_IT(animator);
    879     ASSIGN_IT(interpolator);
    880     ASSIGN_IT(xml);
    881     ASSIGN_IT(raw);
    882     ASSIGN_IT(color);
    883     ASSIGN_IT(menu);
    884     ASSIGN_IT(mipmap);
    885 
    886     assets->setResources(resources);
    887     // now go through any resource overlays and collect their files
    888     sp<AaptAssets> current = assets->getOverlay();
    889     while(current.get()) {
    890         KeyedVector<String8, sp<ResourceTypeSet> > *resources =
    891                 new KeyedVector<String8, sp<ResourceTypeSet> >;
    892         current->setResources(resources);
    893         collect_files(current, resources);
    894         current = current->getOverlay();
    895     }
    896     // apply the overlay files to the base set
    897     if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
    898             !applyFileOverlay(bundle, assets, &layouts, "layout") ||
    899             !applyFileOverlay(bundle, assets, &anims, "anim") ||
    900             !applyFileOverlay(bundle, assets, &animators, "animator") ||
    901             !applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
    902             !applyFileOverlay(bundle, assets, &xmls, "xml") ||
    903             !applyFileOverlay(bundle, assets, &raws, "raw") ||
    904             !applyFileOverlay(bundle, assets, &colors, "color") ||
    905             !applyFileOverlay(bundle, assets, &menus, "menu") ||
    906             !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
    907         return UNKNOWN_ERROR;
    908     }
    909 
    910     bool hasErrors = false;
    911 
    912     if (drawables != NULL) {
    913         if (bundle->getOutputAPKFile() != NULL) {
    914             err = preProcessImages(bundle, assets, drawables, "drawable");
    915         }
    916         if (err == NO_ERROR) {
    917             err = makeFileResources(bundle, assets, &table, drawables, "drawable");
    918             if (err != NO_ERROR) {
    919                 hasErrors = true;
    920             }
    921         } else {
    922             hasErrors = true;
    923         }
    924     }
    925 
    926     if (mipmaps != NULL) {
    927         if (bundle->getOutputAPKFile() != NULL) {
    928             err = preProcessImages(bundle, assets, mipmaps, "mipmap");
    929         }
    930         if (err == NO_ERROR) {
    931             err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
    932             if (err != NO_ERROR) {
    933                 hasErrors = true;
    934             }
    935         } else {
    936             hasErrors = true;
    937         }
    938     }
    939 
    940     if (layouts != NULL) {
    941         err = makeFileResources(bundle, assets, &table, layouts, "layout");
    942         if (err != NO_ERROR) {
    943             hasErrors = true;
    944         }
    945     }
    946 
    947     if (anims != NULL) {
    948         err = makeFileResources(bundle, assets, &table, anims, "anim");
    949         if (err != NO_ERROR) {
    950             hasErrors = true;
    951         }
    952     }
    953 
    954     if (animators != NULL) {
    955         err = makeFileResources(bundle, assets, &table, animators, "animator");
    956         if (err != NO_ERROR) {
    957             hasErrors = true;
    958         }
    959     }
    960 
    961     if (interpolators != NULL) {
    962         err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
    963         if (err != NO_ERROR) {
    964             hasErrors = true;
    965         }
    966     }
    967 
    968     if (xmls != NULL) {
    969         err = makeFileResources(bundle, assets, &table, xmls, "xml");
    970         if (err != NO_ERROR) {
    971             hasErrors = true;
    972         }
    973     }
    974 
    975     if (raws != NULL) {
    976         err = makeFileResources(bundle, assets, &table, raws, "raw");
    977         if (err != NO_ERROR) {
    978             hasErrors = true;
    979         }
    980     }
    981 
    982     // compile resources
    983     current = assets;
    984     while(current.get()) {
    985         KeyedVector<String8, sp<ResourceTypeSet> > *resources =
    986                 current->getResources();
    987 
    988         ssize_t index = resources->indexOfKey(String8("values"));
    989         if (index >= 0) {
    990             ResourceDirIterator it(resources->valueAt(index), String8("values"));
    991             ssize_t res;
    992             while ((res=it.next()) == NO_ERROR) {
    993                 sp<AaptFile> file = it.getFile();
    994                 res = compileResourceFile(bundle, assets, file, it.getParams(),
    995                                           (current!=assets), &table);
    996                 if (res != NO_ERROR) {
    997                     hasErrors = true;
    998                 }
    999             }
   1000         }
   1001         current = current->getOverlay();
   1002     }
   1003 
   1004     if (colors != NULL) {
   1005         err = makeFileResources(bundle, assets, &table, colors, "color");
   1006         if (err != NO_ERROR) {
   1007             hasErrors = true;
   1008         }
   1009     }
   1010 
   1011     if (menus != NULL) {
   1012         err = makeFileResources(bundle, assets, &table, menus, "menu");
   1013         if (err != NO_ERROR) {
   1014             hasErrors = true;
   1015         }
   1016     }
   1017 
   1018     // --------------------------------------------------------------------
   1019     // Assignment of resource IDs and initial generation of resource table.
   1020     // --------------------------------------------------------------------
   1021 
   1022     if (table.hasResources()) {
   1023         sp<AaptFile> resFile(getResourceFile(assets));
   1024         if (resFile == NULL) {
   1025             fprintf(stderr, "Error: unable to generate entry for resource data\n");
   1026             return UNKNOWN_ERROR;
   1027         }
   1028 
   1029         err = table.assignResourceIds();
   1030         if (err < NO_ERROR) {
   1031             return err;
   1032         }
   1033     }
   1034 
   1035     // --------------------------------------------------------------
   1036     // Finally, we can now we can compile XML files, which may reference
   1037     // resources.
   1038     // --------------------------------------------------------------
   1039 
   1040     if (layouts != NULL) {
   1041         ResourceDirIterator it(layouts, String8("layout"));
   1042         while ((err=it.next()) == NO_ERROR) {
   1043             String8 src = it.getFile()->getPrintableSource();
   1044             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1045             if (err == NO_ERROR) {
   1046                 ResXMLTree block;
   1047                 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
   1048                 checkForIds(src, block);
   1049             } else {
   1050                 hasErrors = true;
   1051             }
   1052         }
   1053 
   1054         if (err < NO_ERROR) {
   1055             hasErrors = true;
   1056         }
   1057         err = NO_ERROR;
   1058     }
   1059 
   1060     if (anims != NULL) {
   1061         ResourceDirIterator it(anims, String8("anim"));
   1062         while ((err=it.next()) == NO_ERROR) {
   1063             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1064             if (err != NO_ERROR) {
   1065                 hasErrors = true;
   1066             }
   1067         }
   1068 
   1069         if (err < NO_ERROR) {
   1070             hasErrors = true;
   1071         }
   1072         err = NO_ERROR;
   1073     }
   1074 
   1075     if (animators != NULL) {
   1076         ResourceDirIterator it(animators, String8("animator"));
   1077         while ((err=it.next()) == NO_ERROR) {
   1078             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1079             if (err != NO_ERROR) {
   1080                 hasErrors = true;
   1081             }
   1082         }
   1083 
   1084         if (err < NO_ERROR) {
   1085             hasErrors = true;
   1086         }
   1087         err = NO_ERROR;
   1088     }
   1089 
   1090     if (interpolators != NULL) {
   1091         ResourceDirIterator it(interpolators, String8("interpolator"));
   1092         while ((err=it.next()) == NO_ERROR) {
   1093             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1094             if (err != NO_ERROR) {
   1095                 hasErrors = true;
   1096             }
   1097         }
   1098 
   1099         if (err < NO_ERROR) {
   1100             hasErrors = true;
   1101         }
   1102         err = NO_ERROR;
   1103     }
   1104 
   1105     if (xmls != NULL) {
   1106         ResourceDirIterator it(xmls, String8("xml"));
   1107         while ((err=it.next()) == NO_ERROR) {
   1108             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1109             if (err != NO_ERROR) {
   1110                 hasErrors = true;
   1111             }
   1112         }
   1113 
   1114         if (err < NO_ERROR) {
   1115             hasErrors = true;
   1116         }
   1117         err = NO_ERROR;
   1118     }
   1119 
   1120     if (drawables != NULL) {
   1121         err = postProcessImages(assets, &table, drawables);
   1122         if (err != NO_ERROR) {
   1123             hasErrors = true;
   1124         }
   1125     }
   1126 
   1127     if (colors != NULL) {
   1128         ResourceDirIterator it(colors, String8("color"));
   1129         while ((err=it.next()) == NO_ERROR) {
   1130           err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1131             if (err != NO_ERROR) {
   1132                 hasErrors = true;
   1133             }
   1134         }
   1135 
   1136         if (err < NO_ERROR) {
   1137             hasErrors = true;
   1138         }
   1139         err = NO_ERROR;
   1140     }
   1141 
   1142     if (menus != NULL) {
   1143         ResourceDirIterator it(menus, String8("menu"));
   1144         while ((err=it.next()) == NO_ERROR) {
   1145             String8 src = it.getFile()->getPrintableSource();
   1146             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1147             if (err != NO_ERROR) {
   1148                 hasErrors = true;
   1149             }
   1150             ResXMLTree block;
   1151             block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
   1152             checkForIds(src, block);
   1153         }
   1154 
   1155         if (err < NO_ERROR) {
   1156             hasErrors = true;
   1157         }
   1158         err = NO_ERROR;
   1159     }
   1160 
   1161     if (table.validateLocalizations()) {
   1162         hasErrors = true;
   1163     }
   1164 
   1165     if (hasErrors) {
   1166         return UNKNOWN_ERROR;
   1167     }
   1168 
   1169     const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
   1170     String8 manifestPath(manifestFile->getPrintableSource());
   1171 
   1172     // Generate final compiled manifest file.
   1173     manifestFile->clearData();
   1174     sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
   1175     if (manifestTree == NULL) {
   1176         return UNKNOWN_ERROR;
   1177     }
   1178     err = massageManifest(bundle, manifestTree);
   1179     if (err < NO_ERROR) {
   1180         return err;
   1181     }
   1182     err = compileXmlFile(assets, manifestTree, manifestFile, &table);
   1183     if (err < NO_ERROR) {
   1184         return err;
   1185     }
   1186 
   1187     //block.restart();
   1188     //printXMLBlock(&block);
   1189 
   1190     // --------------------------------------------------------------
   1191     // Generate the final resource table.
   1192     // Re-flatten because we may have added new resource IDs
   1193     // --------------------------------------------------------------
   1194 
   1195     ResTable finalResTable;
   1196     sp<AaptFile> resFile;
   1197 
   1198     if (table.hasResources()) {
   1199         sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
   1200         err = table.addSymbols(symbols);
   1201         if (err < NO_ERROR) {
   1202             return err;
   1203         }
   1204 
   1205         resFile = getResourceFile(assets);
   1206         if (resFile == NULL) {
   1207             fprintf(stderr, "Error: unable to generate entry for resource data\n");
   1208             return UNKNOWN_ERROR;
   1209         }
   1210 
   1211         err = table.flatten(bundle, resFile);
   1212         if (err < NO_ERROR) {
   1213             return err;
   1214         }
   1215 
   1216         if (bundle->getPublicOutputFile()) {
   1217             FILE* fp = fopen(bundle->getPublicOutputFile(), "w+");
   1218             if (fp == NULL) {
   1219                 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n",
   1220                         (const char*)bundle->getPublicOutputFile(), strerror(errno));
   1221                 return UNKNOWN_ERROR;
   1222             }
   1223             if (bundle->getVerbose()) {
   1224                 printf("  Writing public definitions to %s.\n", bundle->getPublicOutputFile());
   1225             }
   1226             table.writePublicDefinitions(String16(assets->getPackage()), fp);
   1227             fclose(fp);
   1228         }
   1229 
   1230         // Read resources back in,
   1231         finalResTable.add(resFile->getData(), resFile->getSize(), NULL);
   1232 
   1233 #if 0
   1234         NOISY(
   1235               printf("Generated resources:\n");
   1236               finalResTable.print();
   1237         )
   1238 #endif
   1239     }
   1240 
   1241     // Perform a basic validation of the manifest file.  This time we
   1242     // parse it with the comments intact, so that we can use them to
   1243     // generate java docs...  so we are not going to write this one
   1244     // back out to the final manifest data.
   1245     sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(),
   1246             manifestFile->getGroupEntry(),
   1247             manifestFile->getResourceType());
   1248     err = compileXmlFile(assets, manifestFile,
   1249             outManifestFile, &table,
   1250             XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
   1251             | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
   1252     if (err < NO_ERROR) {
   1253         return err;
   1254     }
   1255     ResXMLTree block;
   1256     block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true);
   1257     String16 manifest16("manifest");
   1258     String16 permission16("permission");
   1259     String16 permission_group16("permission-group");
   1260     String16 uses_permission16("uses-permission");
   1261     String16 instrumentation16("instrumentation");
   1262     String16 application16("application");
   1263     String16 provider16("provider");
   1264     String16 service16("service");
   1265     String16 receiver16("receiver");
   1266     String16 activity16("activity");
   1267     String16 action16("action");
   1268     String16 category16("category");
   1269     String16 data16("scheme");
   1270     const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1271         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
   1272     const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
   1273         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
   1274     const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1275         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$";
   1276     const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1277         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:";
   1278     const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1279         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;";
   1280     const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1281         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+";
   1282     const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1283         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
   1284     ResXMLTree::event_code_t code;
   1285     sp<AaptSymbols> permissionSymbols;
   1286     sp<AaptSymbols> permissionGroupSymbols;
   1287     while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1288            && code > ResXMLTree::BAD_DOCUMENT) {
   1289         if (code == ResXMLTree::START_TAG) {
   1290             size_t len;
   1291             if (block.getElementNamespace(&len) != NULL) {
   1292                 continue;
   1293             }
   1294             if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) {
   1295                 if (validateAttr(manifestPath, finalResTable, block, NULL, "package",
   1296                                  packageIdentChars, true) != ATTR_OKAY) {
   1297                     hasErrors = true;
   1298                 }
   1299                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1300                                  "sharedUserId", packageIdentChars, false) != ATTR_OKAY) {
   1301                     hasErrors = true;
   1302                 }
   1303             } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0
   1304                     || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) {
   1305                 const bool isGroup = strcmp16(block.getElementName(&len),
   1306                         permission_group16.string()) == 0;
   1307                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1308                                  "name", isGroup ? packageIdentCharsWithTheStupid
   1309                                  : packageIdentChars, true) != ATTR_OKAY) {
   1310                     hasErrors = true;
   1311                 }
   1312                 SourcePos srcPos(manifestPath, block.getLineNumber());
   1313                 sp<AaptSymbols> syms;
   1314                 if (!isGroup) {
   1315                     syms = permissionSymbols;
   1316                     if (syms == NULL) {
   1317                         sp<AaptSymbols> symbols =
   1318                                 assets->getSymbolsFor(String8("Manifest"));
   1319                         syms = permissionSymbols = symbols->addNestedSymbol(
   1320                                 String8("permission"), srcPos);
   1321                     }
   1322                 } else {
   1323                     syms = permissionGroupSymbols;
   1324                     if (syms == NULL) {
   1325                         sp<AaptSymbols> symbols =
   1326                                 assets->getSymbolsFor(String8("Manifest"));
   1327                         syms = permissionGroupSymbols = symbols->addNestedSymbol(
   1328                                 String8("permission_group"), srcPos);
   1329                     }
   1330                 }
   1331                 size_t len;
   1332                 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name");
   1333                 const uint16_t* id = block.getAttributeStringValue(index, &len);
   1334                 if (id == NULL) {
   1335                     fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n",
   1336                             manifestPath.string(), block.getLineNumber(),
   1337                             String8(block.getElementName(&len)).string());
   1338                     hasErrors = true;
   1339                     break;
   1340                 }
   1341                 String8 idStr(id);
   1342                 char* p = idStr.lockBuffer(idStr.size());
   1343                 char* e = p + idStr.size();
   1344                 bool begins_with_digit = true;  // init to true so an empty string fails
   1345                 while (e > p) {
   1346                     e--;
   1347                     if (*e >= '0' && *e <= '9') {
   1348                       begins_with_digit = true;
   1349                       continue;
   1350                     }
   1351                     if ((*e >= 'a' && *e <= 'z') ||
   1352                         (*e >= 'A' && *e <= 'Z') ||
   1353                         (*e == '_')) {
   1354                       begins_with_digit = false;
   1355                       continue;
   1356                     }
   1357                     if (isGroup && (*e == '-')) {
   1358                         *e = '_';
   1359                         begins_with_digit = false;
   1360                         continue;
   1361                     }
   1362                     e++;
   1363                     break;
   1364                 }
   1365                 idStr.unlockBuffer();
   1366                 // verify that we stopped because we hit a period or
   1367                 // the beginning of the string, and that the
   1368                 // identifier didn't begin with a digit.
   1369                 if (begins_with_digit || (e != p && *(e-1) != '.')) {
   1370                   fprintf(stderr,
   1371                           "%s:%d: Permission name <%s> is not a valid Java symbol\n",
   1372                           manifestPath.string(), block.getLineNumber(), idStr.string());
   1373                   hasErrors = true;
   1374                 }
   1375                 syms->addStringSymbol(String8(e), idStr, srcPos);
   1376                 const uint16_t* cmt = block.getComment(&len);
   1377                 if (cmt != NULL && *cmt != 0) {
   1378                     //printf("Comment of %s: %s\n", String8(e).string(),
   1379                     //        String8(cmt).string());
   1380                     syms->appendComment(String8(e), String16(cmt), srcPos);
   1381                 } else {
   1382                     //printf("No comment for %s\n", String8(e).string());
   1383                 }
   1384                 syms->makeSymbolPublic(String8(e), srcPos);
   1385             } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
   1386                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1387                                  "name", packageIdentChars, true) != ATTR_OKAY) {
   1388                     hasErrors = true;
   1389                 }
   1390             } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) {
   1391                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1392                                  "name", classIdentChars, true) != ATTR_OKAY) {
   1393                     hasErrors = true;
   1394                 }
   1395                 if (validateAttr(manifestPath, finalResTable, block,
   1396                                  RESOURCES_ANDROID_NAMESPACE, "targetPackage",
   1397                                  packageIdentChars, true) != ATTR_OKAY) {
   1398                     hasErrors = true;
   1399                 }
   1400             } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) {
   1401                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1402                                  "name", classIdentChars, false) != ATTR_OKAY) {
   1403                     hasErrors = true;
   1404                 }
   1405                 if (validateAttr(manifestPath, finalResTable, block,
   1406                                  RESOURCES_ANDROID_NAMESPACE, "permission",
   1407                                  packageIdentChars, false) != ATTR_OKAY) {
   1408                     hasErrors = true;
   1409                 }
   1410                 if (validateAttr(manifestPath, finalResTable, block,
   1411                                  RESOURCES_ANDROID_NAMESPACE, "process",
   1412                                  processIdentChars, false) != ATTR_OKAY) {
   1413                     hasErrors = true;
   1414                 }
   1415                 if (validateAttr(manifestPath, finalResTable, block,
   1416                                  RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
   1417                                  processIdentChars, false) != ATTR_OKAY) {
   1418                     hasErrors = true;
   1419                 }
   1420             } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) {
   1421                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1422                                  "name", classIdentChars, true) != ATTR_OKAY) {
   1423                     hasErrors = true;
   1424                 }
   1425                 if (validateAttr(manifestPath, finalResTable, block,
   1426                                  RESOURCES_ANDROID_NAMESPACE, "authorities",
   1427                                  authoritiesIdentChars, true) != ATTR_OKAY) {
   1428                     hasErrors = true;
   1429                 }
   1430                 if (validateAttr(manifestPath, finalResTable, block,
   1431                                  RESOURCES_ANDROID_NAMESPACE, "permission",
   1432                                  packageIdentChars, false) != ATTR_OKAY) {
   1433                     hasErrors = true;
   1434                 }
   1435                 if (validateAttr(manifestPath, finalResTable, block,
   1436                                  RESOURCES_ANDROID_NAMESPACE, "process",
   1437                                  processIdentChars, false) != ATTR_OKAY) {
   1438                     hasErrors = true;
   1439                 }
   1440             } else if (strcmp16(block.getElementName(&len), service16.string()) == 0
   1441                        || strcmp16(block.getElementName(&len), receiver16.string()) == 0
   1442                        || strcmp16(block.getElementName(&len), activity16.string()) == 0) {
   1443                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1444                                  "name", classIdentChars, true) != ATTR_OKAY) {
   1445                     hasErrors = true;
   1446                 }
   1447                 if (validateAttr(manifestPath, finalResTable, block,
   1448                                  RESOURCES_ANDROID_NAMESPACE, "permission",
   1449                                  packageIdentChars, false) != ATTR_OKAY) {
   1450                     hasErrors = true;
   1451                 }
   1452                 if (validateAttr(manifestPath, finalResTable, block,
   1453                                  RESOURCES_ANDROID_NAMESPACE, "process",
   1454                                  processIdentChars, false) != ATTR_OKAY) {
   1455                     hasErrors = true;
   1456                 }
   1457                 if (validateAttr(manifestPath, finalResTable, block,
   1458                                  RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
   1459                                  processIdentChars, false) != ATTR_OKAY) {
   1460                     hasErrors = true;
   1461                 }
   1462             } else if (strcmp16(block.getElementName(&len), action16.string()) == 0
   1463                        || strcmp16(block.getElementName(&len), category16.string()) == 0) {
   1464                 if (validateAttr(manifestPath, finalResTable, block,
   1465                                  RESOURCES_ANDROID_NAMESPACE, "name",
   1466                                  packageIdentChars, true) != ATTR_OKAY) {
   1467                     hasErrors = true;
   1468                 }
   1469             } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) {
   1470                 if (validateAttr(manifestPath, finalResTable, block,
   1471                                  RESOURCES_ANDROID_NAMESPACE, "mimeType",
   1472                                  typeIdentChars, true) != ATTR_OKAY) {
   1473                     hasErrors = true;
   1474                 }
   1475                 if (validateAttr(manifestPath, finalResTable, block,
   1476                                  RESOURCES_ANDROID_NAMESPACE, "scheme",
   1477                                  schemeIdentChars, true) != ATTR_OKAY) {
   1478                     hasErrors = true;
   1479                 }
   1480             }
   1481         }
   1482     }
   1483 
   1484     if (resFile != NULL) {
   1485         // These resources are now considered to be a part of the included
   1486         // resources, for others to reference.
   1487         err = assets->addIncludedResources(resFile);
   1488         if (err < NO_ERROR) {
   1489             fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n");
   1490             return err;
   1491         }
   1492     }
   1493 
   1494     return err;
   1495 }
   1496 
   1497 static const char* getIndentSpace(int indent)
   1498 {
   1499 static const char whitespace[] =
   1500 "                                                                                       ";
   1501 
   1502     return whitespace + sizeof(whitespace) - 1 - indent*4;
   1503 }
   1504 
   1505 static status_t fixupSymbol(String16* inoutSymbol)
   1506 {
   1507     inoutSymbol->replaceAll('.', '_');
   1508     inoutSymbol->replaceAll(':', '_');
   1509     return NO_ERROR;
   1510 }
   1511 
   1512 static String16 getAttributeComment(const sp<AaptAssets>& assets,
   1513                                     const String8& name,
   1514                                     String16* outTypeComment = NULL)
   1515 {
   1516     sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R"));
   1517     if (asym != NULL) {
   1518         //printf("Got R symbols!\n");
   1519         asym = asym->getNestedSymbols().valueFor(String8("attr"));
   1520         if (asym != NULL) {
   1521             //printf("Got attrs symbols! comment %s=%s\n",
   1522             //     name.string(), String8(asym->getComment(name)).string());
   1523             if (outTypeComment != NULL) {
   1524                 *outTypeComment = asym->getTypeComment(name);
   1525             }
   1526             return asym->getComment(name);
   1527         }
   1528     }
   1529     return String16();
   1530 }
   1531 
   1532 static status_t writeLayoutClasses(
   1533     FILE* fp, const sp<AaptAssets>& assets,
   1534     const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
   1535 {
   1536     const char* indentStr = getIndentSpace(indent);
   1537     if (!includePrivate) {
   1538         fprintf(fp, "%s/** @doconly */\n", indentStr);
   1539     }
   1540     fprintf(fp, "%spublic static final class styleable {\n", indentStr);
   1541     indent++;
   1542 
   1543     String16 attr16("attr");
   1544     String16 package16(assets->getPackage());
   1545 
   1546     indentStr = getIndentSpace(indent);
   1547     bool hasErrors = false;
   1548 
   1549     size_t i;
   1550     size_t N = symbols->getNestedSymbols().size();
   1551     for (i=0; i<N; i++) {
   1552         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
   1553         String16 nclassName16(symbols->getNestedSymbols().keyAt(i));
   1554         String8 realClassName(nclassName16);
   1555         if (fixupSymbol(&nclassName16) != NO_ERROR) {
   1556             hasErrors = true;
   1557         }
   1558         String8 nclassName(nclassName16);
   1559 
   1560         SortedVector<uint32_t> idents;
   1561         Vector<uint32_t> origOrder;
   1562         Vector<bool> publicFlags;
   1563 
   1564         size_t a;
   1565         size_t NA = nsymbols->getSymbols().size();
   1566         for (a=0; a<NA; a++) {
   1567             const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
   1568             int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
   1569                     ? sym.int32Val : 0;
   1570             bool isPublic = true;
   1571             if (code == 0) {
   1572                 String16 name16(sym.name);
   1573                 uint32_t typeSpecFlags;
   1574                 code = assets->getIncludedResources().identifierForName(
   1575                     name16.string(), name16.size(),
   1576                     attr16.string(), attr16.size(),
   1577                     package16.string(), package16.size(), &typeSpecFlags);
   1578                 if (code == 0) {
   1579                     fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
   1580                             nclassName.string(), sym.name.string());
   1581                     hasErrors = true;
   1582                 }
   1583                 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
   1584             }
   1585             idents.add(code);
   1586             origOrder.add(code);
   1587             publicFlags.add(isPublic);
   1588         }
   1589 
   1590         NA = idents.size();
   1591 
   1592         bool deprecated = false;
   1593 
   1594         String16 comment = symbols->getComment(realClassName);
   1595         fprintf(fp, "%s/** ", indentStr);
   1596         if (comment.size() > 0) {
   1597             String8 cmt(comment);
   1598             fprintf(fp, "%s\n", cmt.string());
   1599             if (strstr(cmt.string(), "@deprecated") != NULL) {
   1600                 deprecated = true;
   1601             }
   1602         } else {
   1603             fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
   1604         }
   1605         bool hasTable = false;
   1606         for (a=0; a<NA; a++) {
   1607             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
   1608             if (pos >= 0) {
   1609                 if (!hasTable) {
   1610                     hasTable = true;
   1611                     fprintf(fp,
   1612                             "%s   <p>Includes the following attributes:</p>\n"
   1613                             "%s   <table>\n"
   1614                             "%s   <colgroup align=\"left\" />\n"
   1615                             "%s   <colgroup align=\"left\" />\n"
   1616                             "%s   <tr><th>Attribute</th><th>Description</th></tr>\n",
   1617                             indentStr,
   1618                             indentStr,
   1619                             indentStr,
   1620                             indentStr,
   1621                             indentStr);
   1622                 }
   1623                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
   1624                 if (!publicFlags.itemAt(a) && !includePrivate) {
   1625                     continue;
   1626                 }
   1627                 String8 name8(sym.name);
   1628                 String16 comment(sym.comment);
   1629                 if (comment.size() <= 0) {
   1630                     comment = getAttributeComment(assets, name8);
   1631                 }
   1632                 if (comment.size() > 0) {
   1633                     const char16_t* p = comment.string();
   1634                     while (*p != 0 && *p != '.') {
   1635                         if (*p == '{') {
   1636                             while (*p != 0 && *p != '}') {
   1637                                 p++;
   1638                             }
   1639                         } else {
   1640                             p++;
   1641                         }
   1642                     }
   1643                     if (*p == '.') {
   1644                         p++;
   1645                     }
   1646                     comment = String16(comment.string(), p-comment.string());
   1647                 }
   1648                 String16 name(name8);
   1649                 fixupSymbol(&name);
   1650                 fprintf(fp, "%s   <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n",
   1651                         indentStr, nclassName.string(),
   1652                         String8(name).string(),
   1653                         assets->getPackage().string(),
   1654                         String8(name).string(),
   1655                         String8(comment).string());
   1656             }
   1657         }
   1658         if (hasTable) {
   1659             fprintf(fp, "%s   </table>\n", indentStr);
   1660         }
   1661         for (a=0; a<NA; a++) {
   1662             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
   1663             if (pos >= 0) {
   1664                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
   1665                 if (!publicFlags.itemAt(a) && !includePrivate) {
   1666                     continue;
   1667                 }
   1668                 String16 name(sym.name);
   1669                 fixupSymbol(&name);
   1670                 fprintf(fp, "%s   @see #%s_%s\n",
   1671                         indentStr, nclassName.string(),
   1672                         String8(name).string());
   1673             }
   1674         }
   1675         fprintf(fp, "%s */\n", getIndentSpace(indent));
   1676 
   1677         if (deprecated) {
   1678             fprintf(fp, "%s@Deprecated\n", indentStr);
   1679         }
   1680 
   1681         fprintf(fp,
   1682                 "%spublic static final int[] %s = {\n"
   1683                 "%s",
   1684                 indentStr, nclassName.string(),
   1685                 getIndentSpace(indent+1));
   1686 
   1687         for (a=0; a<NA; a++) {
   1688             if (a != 0) {
   1689                 if ((a&3) == 0) {
   1690                     fprintf(fp, ",\n%s", getIndentSpace(indent+1));
   1691                 } else {
   1692                     fprintf(fp, ", ");
   1693                 }
   1694             }
   1695             fprintf(fp, "0x%08x", idents[a]);
   1696         }
   1697 
   1698         fprintf(fp, "\n%s};\n", indentStr);
   1699 
   1700         for (a=0; a<NA; a++) {
   1701             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
   1702             if (pos >= 0) {
   1703                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
   1704                 if (!publicFlags.itemAt(a) && !includePrivate) {
   1705                     continue;
   1706                 }
   1707                 String8 name8(sym.name);
   1708                 String16 comment(sym.comment);
   1709                 String16 typeComment;
   1710                 if (comment.size() <= 0) {
   1711                     comment = getAttributeComment(assets, name8, &typeComment);
   1712                 } else {
   1713                     getAttributeComment(assets, name8, &typeComment);
   1714                 }
   1715                 String16 name(name8);
   1716                 if (fixupSymbol(&name) != NO_ERROR) {
   1717                     hasErrors = true;
   1718                 }
   1719 
   1720                 uint32_t typeSpecFlags = 0;
   1721                 String16 name16(sym.name);
   1722                 assets->getIncludedResources().identifierForName(
   1723                     name16.string(), name16.size(),
   1724                     attr16.string(), attr16.size(),
   1725                     package16.string(), package16.size(), &typeSpecFlags);
   1726                 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
   1727                 //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
   1728                 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
   1729 
   1730                 bool deprecated = false;
   1731 
   1732                 fprintf(fp, "%s/**\n", indentStr);
   1733                 if (comment.size() > 0) {
   1734                     String8 cmt(comment);
   1735                     fprintf(fp, "%s  <p>\n%s  @attr description\n", indentStr, indentStr);
   1736                     fprintf(fp, "%s  %s\n", indentStr, cmt.string());
   1737                     if (strstr(cmt.string(), "@deprecated") != NULL) {
   1738                         deprecated = true;
   1739                     }
   1740                 } else {
   1741                     fprintf(fp,
   1742                             "%s  <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
   1743                             "%s  attribute's value can be found in the {@link #%s} array.\n",
   1744                             indentStr,
   1745                             pub ? assets->getPackage().string()
   1746                                 : assets->getSymbolsPrivatePackage().string(),
   1747                             String8(name).string(),
   1748                             indentStr, nclassName.string());
   1749                 }
   1750                 if (typeComment.size() > 0) {
   1751                     String8 cmt(typeComment);
   1752                     fprintf(fp, "\n\n%s  %s\n", indentStr, cmt.string());
   1753                     if (strstr(cmt.string(), "@deprecated") != NULL) {
   1754                         deprecated = true;
   1755                     }
   1756                 }
   1757                 if (comment.size() > 0) {
   1758                     if (pub) {
   1759                         fprintf(fp,
   1760                                 "%s  <p>This corresponds to the global attribute"
   1761                                 "%s  resource symbol {@link %s.R.attr#%s}.\n",
   1762                                 indentStr, indentStr,
   1763                                 assets->getPackage().string(),
   1764                                 String8(name).string());
   1765                     } else {
   1766                         fprintf(fp,
   1767                                 "%s  <p>This is a private symbol.\n", indentStr);
   1768                     }
   1769                 }
   1770                 fprintf(fp, "%s  @attr name %s:%s\n", indentStr,
   1771                         "android", String8(name).string());
   1772                 fprintf(fp, "%s*/\n", indentStr);
   1773                 if (deprecated) {
   1774                     fprintf(fp, "%s@Deprecated\n", indentStr);
   1775                 }
   1776                 fprintf(fp,
   1777                         "%spublic static final int %s_%s = %d;\n",
   1778                         indentStr, nclassName.string(),
   1779                         String8(name).string(), (int)pos);
   1780             }
   1781         }
   1782     }
   1783 
   1784     indent--;
   1785     fprintf(fp, "%s};\n", getIndentSpace(indent));
   1786     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
   1787 }
   1788 
   1789 static status_t writeSymbolClass(
   1790     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
   1791     const sp<AaptSymbols>& symbols, const String8& className, int indent,
   1792     bool nonConstantId)
   1793 {
   1794     fprintf(fp, "%spublic %sfinal class %s {\n",
   1795             getIndentSpace(indent),
   1796             indent != 0 ? "static " : "", className.string());
   1797     indent++;
   1798 
   1799     size_t i;
   1800     status_t err = NO_ERROR;
   1801 
   1802     const char * id_format = nonConstantId ?
   1803             "%spublic static int %s=0x%08x;\n" :
   1804             "%spublic static final int %s=0x%08x;\n";
   1805 
   1806     size_t N = symbols->getSymbols().size();
   1807     for (i=0; i<N; i++) {
   1808         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
   1809         if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
   1810             continue;
   1811         }
   1812         if (!includePrivate && !sym.isPublic) {
   1813             continue;
   1814         }
   1815         String16 name(sym.name);
   1816         String8 realName(name);
   1817         if (fixupSymbol(&name) != NO_ERROR) {
   1818             return UNKNOWN_ERROR;
   1819         }
   1820         String16 comment(sym.comment);
   1821         bool haveComment = false;
   1822         bool deprecated = false;
   1823         if (comment.size() > 0) {
   1824             haveComment = true;
   1825             String8 cmt(comment);
   1826             fprintf(fp,
   1827                     "%s/** %s\n",
   1828                     getIndentSpace(indent), cmt.string());
   1829             if (strstr(cmt.string(), "@deprecated") != NULL) {
   1830                 deprecated = true;
   1831             }
   1832         } else if (sym.isPublic && !includePrivate) {
   1833             sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
   1834                 assets->getPackage().string(), className.string(),
   1835                 String8(sym.name).string());
   1836         }
   1837         String16 typeComment(sym.typeComment);
   1838         if (typeComment.size() > 0) {
   1839             String8 cmt(typeComment);
   1840             if (!haveComment) {
   1841                 haveComment = true;
   1842                 fprintf(fp,
   1843                         "%s/** %s\n", getIndentSpace(indent), cmt.string());
   1844             } else {
   1845                 fprintf(fp,
   1846                         "%s %s\n", getIndentSpace(indent), cmt.string());
   1847             }
   1848             if (strstr(cmt.string(), "@deprecated") != NULL) {
   1849                 deprecated = true;
   1850             }
   1851         }
   1852         if (haveComment) {
   1853             fprintf(fp,"%s */\n", getIndentSpace(indent));
   1854         }
   1855         if (deprecated) {
   1856             fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
   1857         }
   1858         fprintf(fp, id_format,
   1859                 getIndentSpace(indent),
   1860                 String8(name).string(), (int)sym.int32Val);
   1861     }
   1862 
   1863     for (i=0; i<N; i++) {
   1864         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
   1865         if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) {
   1866             continue;
   1867         }
   1868         if (!includePrivate && !sym.isPublic) {
   1869             continue;
   1870         }
   1871         String16 name(sym.name);
   1872         if (fixupSymbol(&name) != NO_ERROR) {
   1873             return UNKNOWN_ERROR;
   1874         }
   1875         String16 comment(sym.comment);
   1876         bool deprecated = false;
   1877         if (comment.size() > 0) {
   1878             String8 cmt(comment);
   1879             fprintf(fp,
   1880                     "%s/** %s\n"
   1881                      "%s */\n",
   1882                     getIndentSpace(indent), cmt.string(),
   1883                     getIndentSpace(indent));
   1884             if (strstr(cmt.string(), "@deprecated") != NULL) {
   1885                 deprecated = true;
   1886             }
   1887         } else if (sym.isPublic && !includePrivate) {
   1888             sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
   1889                 assets->getPackage().string(), className.string(),
   1890                 String8(sym.name).string());
   1891         }
   1892         if (deprecated) {
   1893             fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
   1894         }
   1895         fprintf(fp, "%spublic static final String %s=\"%s\";\n",
   1896                 getIndentSpace(indent),
   1897                 String8(name).string(), sym.stringVal.string());
   1898     }
   1899 
   1900     sp<AaptSymbols> styleableSymbols;
   1901 
   1902     N = symbols->getNestedSymbols().size();
   1903     for (i=0; i<N; i++) {
   1904         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
   1905         String8 nclassName(symbols->getNestedSymbols().keyAt(i));
   1906         if (nclassName == "styleable") {
   1907             styleableSymbols = nsymbols;
   1908         } else {
   1909             err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
   1910         }
   1911         if (err != NO_ERROR) {
   1912             return err;
   1913         }
   1914     }
   1915 
   1916     if (styleableSymbols != NULL) {
   1917         err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate);
   1918         if (err != NO_ERROR) {
   1919             return err;
   1920         }
   1921     }
   1922 
   1923     indent--;
   1924     fprintf(fp, "%s}\n", getIndentSpace(indent));
   1925     return NO_ERROR;
   1926 }
   1927 
   1928 status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
   1929     const String8& package, bool includePrivate)
   1930 {
   1931     if (!bundle->getRClassDir()) {
   1932         return NO_ERROR;
   1933     }
   1934 
   1935     const size_t N = assets->getSymbols().size();
   1936     for (size_t i=0; i<N; i++) {
   1937         sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
   1938         String8 className(assets->getSymbols().keyAt(i));
   1939         String8 dest(bundle->getRClassDir());
   1940         if (bundle->getMakePackageDirs()) {
   1941             String8 pkg(package);
   1942             const char* last = pkg.string();
   1943             const char* s = last-1;
   1944             do {
   1945                 s++;
   1946                 if (s > last && (*s == '.' || *s == 0)) {
   1947                     String8 part(last, s-last);
   1948                     dest.appendPath(part);
   1949 #ifdef HAVE_MS_C_RUNTIME
   1950                     _mkdir(dest.string());
   1951 #else
   1952                     mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
   1953 #endif
   1954                     last = s+1;
   1955                 }
   1956             } while (*s);
   1957         }
   1958         dest.appendPath(className);
   1959         dest.append(".java");
   1960         FILE* fp = fopen(dest.string(), "w+");
   1961         if (fp == NULL) {
   1962             fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
   1963                     dest.string(), strerror(errno));
   1964             return UNKNOWN_ERROR;
   1965         }
   1966         if (bundle->getVerbose()) {
   1967             printf("  Writing symbols for class %s.\n", className.string());
   1968         }
   1969 
   1970         fprintf(fp,
   1971         "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
   1972         " *\n"
   1973         " * This class was automatically generated by the\n"
   1974         " * aapt tool from the resource data it found.  It\n"
   1975         " * should not be modified by hand.\n"
   1976         " */\n"
   1977         "\n"
   1978         "package %s;\n\n", package.string());
   1979 
   1980         status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0, bundle->getNonConstantId());
   1981         if (err != NO_ERROR) {
   1982             return err;
   1983         }
   1984         fclose(fp);
   1985 
   1986         // If we were asked to generate a dependency file, we'll go ahead and add this R.java
   1987         // as a target in the dependency file right next to it.
   1988         if (bundle->getGenDependencies()) {
   1989             // Add this R.java to the dependency file
   1990             String8 dependencyFile(bundle->getRClassDir());
   1991             dependencyFile.appendPath("R.java.d");
   1992 
   1993             fp = fopen(dependencyFile.string(), "a");
   1994             fprintf(fp,"%s \\\n", dest.string());
   1995             fclose(fp);
   1996         }
   1997     }
   1998 
   1999     return NO_ERROR;
   2000 }
   2001 
   2002 
   2003 
   2004 class ProguardKeepSet
   2005 {
   2006 public:
   2007     // { rule --> { file locations } }
   2008     KeyedVector<String8, SortedVector<String8> > rules;
   2009 
   2010     void add(const String8& rule, const String8& where);
   2011 };
   2012 
   2013 void ProguardKeepSet::add(const String8& rule, const String8& where)
   2014 {
   2015     ssize_t index = rules.indexOfKey(rule);
   2016     if (index < 0) {
   2017         index = rules.add(rule, SortedVector<String8>());
   2018     }
   2019     rules.editValueAt(index).add(where);
   2020 }
   2021 
   2022 void
   2023 addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
   2024         const char* pkg, const String8& srcName, int line)
   2025 {
   2026     String8 className(inClassName);
   2027     if (pkg != NULL) {
   2028         // asdf     --> package.asdf
   2029         // .asdf  .a.b  --> package.asdf package.a.b
   2030         // asdf.adsf --> asdf.asdf
   2031         const char* p = className.string();
   2032         const char* q = strchr(p, '.');
   2033         if (p == q) {
   2034             className = pkg;
   2035             className.append(inClassName);
   2036         } else if (q == NULL) {
   2037             className = pkg;
   2038             className.append(".");
   2039             className.append(inClassName);
   2040         }
   2041     }
   2042 
   2043     String8 rule("-keep class ");
   2044     rule += className;
   2045     rule += " { <init>(...); }";
   2046 
   2047     String8 location("view ");
   2048     location += srcName;
   2049     char lineno[20];
   2050     sprintf(lineno, ":%d", line);
   2051     location += lineno;
   2052 
   2053     keep->add(rule, location);
   2054 }
   2055 
   2056 status_t
   2057 writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
   2058 {
   2059     status_t err;
   2060     ResXMLTree tree;
   2061     size_t len;
   2062     ResXMLTree::event_code_t code;
   2063     int depth = 0;
   2064     bool inApplication = false;
   2065     String8 error;
   2066     sp<AaptGroup> assGroup;
   2067     sp<AaptFile> assFile;
   2068     String8 pkg;
   2069 
   2070     // First, look for a package file to parse.  This is required to
   2071     // be able to generate the resource information.
   2072     assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml"));
   2073     if (assGroup == NULL) {
   2074         fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
   2075         return -1;
   2076     }
   2077 
   2078     if (assGroup->getFiles().size() != 1) {
   2079         fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
   2080                 assGroup->getFiles().valueAt(0)->getPrintableSource().string());
   2081     }
   2082 
   2083     assFile = assGroup->getFiles().valueAt(0);
   2084 
   2085     err = parseXMLResource(assFile, &tree);
   2086     if (err != NO_ERROR) {
   2087         return err;
   2088     }
   2089 
   2090     tree.restart();
   2091 
   2092     while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   2093         if (code == ResXMLTree::END_TAG) {
   2094             if (/* name == "Application" && */ depth == 2) {
   2095                 inApplication = false;
   2096             }
   2097             depth--;
   2098             continue;
   2099         }
   2100         if (code != ResXMLTree::START_TAG) {
   2101             continue;
   2102         }
   2103         depth++;
   2104         String8 tag(tree.getElementName(&len));
   2105         // printf("Depth %d tag %s\n", depth, tag.string());
   2106         bool keepTag = false;
   2107         if (depth == 1) {
   2108             if (tag != "manifest") {
   2109                 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
   2110                 return -1;
   2111             }
   2112             pkg = getAttribute(tree, NULL, "package", NULL);
   2113         } else if (depth == 2) {
   2114             if (tag == "application") {
   2115                 inApplication = true;
   2116                 keepTag = true;
   2117 
   2118                 String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
   2119                         "backupAgent", &error);
   2120                 if (agent.length() > 0) {
   2121                     addProguardKeepRule(keep, agent, pkg.string(),
   2122                             assFile->getPrintableSource(), tree.getLineNumber());
   2123                 }
   2124             } else if (tag == "instrumentation") {
   2125                 keepTag = true;
   2126             }
   2127         }
   2128         if (!keepTag && inApplication && depth == 3) {
   2129             if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
   2130                 keepTag = true;
   2131             }
   2132         }
   2133         if (keepTag) {
   2134             String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
   2135                     "name", &error);
   2136             if (error != "") {
   2137                 fprintf(stderr, "ERROR: %s\n", error.string());
   2138                 return -1;
   2139             }
   2140             if (name.length() > 0) {
   2141                 addProguardKeepRule(keep, name, pkg.string(),
   2142                         assFile->getPrintableSource(), tree.getLineNumber());
   2143             }
   2144         }
   2145     }
   2146 
   2147     return NO_ERROR;
   2148 }
   2149 
   2150 struct NamespaceAttributePair {
   2151     const char* ns;
   2152     const char* attr;
   2153 
   2154     NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {}
   2155     NamespaceAttributePair() : ns(NULL), attr(NULL) {}
   2156 };
   2157 
   2158 status_t
   2159 writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
   2160         const char* startTag, const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs)
   2161 {
   2162     status_t err;
   2163     ResXMLTree tree;
   2164     size_t len;
   2165     ResXMLTree::event_code_t code;
   2166 
   2167     err = parseXMLResource(layoutFile, &tree);
   2168     if (err != NO_ERROR) {
   2169         return err;
   2170     }
   2171 
   2172     tree.restart();
   2173 
   2174     if (startTag != NULL) {
   2175         bool haveStart = false;
   2176         while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   2177             if (code != ResXMLTree::START_TAG) {
   2178                 continue;
   2179             }
   2180             String8 tag(tree.getElementName(&len));
   2181             if (tag == startTag) {
   2182                 haveStart = true;
   2183             }
   2184             break;
   2185         }
   2186         if (!haveStart) {
   2187             return NO_ERROR;
   2188         }
   2189     }
   2190 
   2191     while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   2192         if (code != ResXMLTree::START_TAG) {
   2193             continue;
   2194         }
   2195         String8 tag(tree.getElementName(&len));
   2196 
   2197         // If there is no '.', we'll assume that it's one of the built in names.
   2198         if (strchr(tag.string(), '.')) {
   2199             addProguardKeepRule(keep, tag, NULL,
   2200                     layoutFile->getPrintableSource(), tree.getLineNumber());
   2201         } else if (tagAttrPairs != NULL) {
   2202             ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
   2203             if (tagIndex >= 0) {
   2204                 const NamespaceAttributePair& nsAttr = tagAttrPairs->valueAt(tagIndex);
   2205                 ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
   2206                 if (attrIndex < 0) {
   2207                     // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
   2208                     //        layoutFile->getPrintableSource().string(), tree.getLineNumber(),
   2209                     //        tag.string(), nsAttr.ns, nsAttr.attr);
   2210                 } else {
   2211                     size_t len;
   2212                     addProguardKeepRule(keep,
   2213                                         String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
   2214                                         layoutFile->getPrintableSource(), tree.getLineNumber());
   2215                 }
   2216             }
   2217         }
   2218     }
   2219 
   2220     return NO_ERROR;
   2221 }
   2222 
   2223 static void addTagAttrPair(KeyedVector<String8, NamespaceAttributePair>* dest,
   2224         const char* tag, const char* ns, const char* attr) {
   2225     dest->add(String8(tag), NamespaceAttributePair(ns, attr));
   2226 }
   2227 
   2228 status_t
   2229 writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
   2230 {
   2231     status_t err;
   2232 
   2233     // tag:attribute pairs that should be checked in layout files.
   2234     KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
   2235     addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
   2236     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
   2237     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
   2238 
   2239     // tag:attribute pairs that should be checked in xml files.
   2240     KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
   2241     addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
   2242     addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
   2243 
   2244     const Vector<sp<AaptDir> >& dirs = assets->resDirs();
   2245     const size_t K = dirs.size();
   2246     for (size_t k=0; k<K; k++) {
   2247         const sp<AaptDir>& d = dirs.itemAt(k);
   2248         const String8& dirName = d->getLeaf();
   2249         const char* startTag = NULL;
   2250         const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs = NULL;
   2251         if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
   2252             tagAttrPairs = &kLayoutTagAttrPairs;
   2253         } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
   2254             startTag = "PreferenceScreen";
   2255             tagAttrPairs = &kXmlTagAttrPairs;
   2256         } else {
   2257             continue;
   2258         }
   2259 
   2260         const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
   2261         const size_t N = groups.size();
   2262         for (size_t i=0; i<N; i++) {
   2263             const sp<AaptGroup>& group = groups.valueAt(i);
   2264             const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
   2265             const size_t M = files.size();
   2266             for (size_t j=0; j<M; j++) {
   2267                 err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
   2268                 if (err < 0) {
   2269                     return err;
   2270                 }
   2271             }
   2272         }
   2273     }
   2274     // Handle the overlays
   2275     sp<AaptAssets> overlay = assets->getOverlay();
   2276     if (overlay.get()) {
   2277         return writeProguardForLayouts(keep, overlay);
   2278     }
   2279     return NO_ERROR;
   2280 }
   2281 
   2282 status_t
   2283 writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
   2284 {
   2285     status_t err = -1;
   2286 
   2287     if (!bundle->getProguardFile()) {
   2288         return NO_ERROR;
   2289     }
   2290 
   2291     ProguardKeepSet keep;
   2292 
   2293     err = writeProguardForAndroidManifest(&keep, assets);
   2294     if (err < 0) {
   2295         return err;
   2296     }
   2297 
   2298     err = writeProguardForLayouts(&keep, assets);
   2299     if (err < 0) {
   2300         return err;
   2301     }
   2302 
   2303     FILE* fp = fopen(bundle->getProguardFile(), "w+");
   2304     if (fp == NULL) {
   2305         fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
   2306                 bundle->getProguardFile(), strerror(errno));
   2307         return UNKNOWN_ERROR;
   2308     }
   2309 
   2310     const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules;
   2311     const size_t N = rules.size();
   2312     for (size_t i=0; i<N; i++) {
   2313         const SortedVector<String8>& locations = rules.valueAt(i);
   2314         const size_t M = locations.size();
   2315         for (size_t j=0; j<M; j++) {
   2316             fprintf(fp, "# %s\n", locations.itemAt(j).string());
   2317         }
   2318         fprintf(fp, "%s\n\n", rules.keyAt(i).string());
   2319     }
   2320     fclose(fp);
   2321 
   2322     return err;
   2323 }
   2324 
   2325 // Loops through the string paths and writes them to the file pointer
   2326 // Each file path is written on its own line with a terminating backslash.
   2327 status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
   2328 {
   2329     status_t deps = -1;
   2330     for (size_t file_i = 0; file_i < files->size(); ++file_i) {
   2331         // Add the full file path to the dependency file
   2332         fprintf(fp, "%s \\\n", files->itemAt(file_i).string());
   2333         deps++;
   2334     }
   2335     return deps;
   2336 }
   2337 
   2338 status_t
   2339 writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw)
   2340 {
   2341     status_t deps = -1;
   2342     deps += writePathsToFile(assets->getFullResPaths(), fp);
   2343     if (includeRaw) {
   2344         deps += writePathsToFile(assets->getFullAssetPaths(), fp);
   2345     }
   2346     return deps;
   2347 }
   2348