diff options
Diffstat (limited to 'tools/aapt/Resource.cpp')
-rw-r--r-- | tools/aapt/Resource.cpp | 312 |
1 files changed, 183 insertions, 129 deletions
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 42b190572cbd..3d93bbe62f67 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -166,6 +166,35 @@ private: ResTable_config mParams; }; +class AnnotationProcessor { +public: + AnnotationProcessor() : mDeprecated(false), mSystemApi(false) { } + + void preprocessComment(String8& comment) { + if (comment.size() > 0) { + if (comment.contains("@deprecated")) { + mDeprecated = true; + } + if (comment.removeAll("@SystemApi")) { + mSystemApi = true; + } + } + } + + void printAnnotations(FILE* fp, const char* indentStr) { + if (mDeprecated) { + fprintf(fp, "%s@Deprecated\n", indentStr); + } + if (mSystemApi) { + fprintf(fp, "%s@android.annotation.SystemApi\n", indentStr); + } + } + +private: + bool mDeprecated; + bool mSystemApi; +}; + // ========================================================================== // ========================================================================== // ========================================================================== @@ -179,24 +208,6 @@ bool isValidResourceType(const String8& type) || type == "color" || type == "menu" || type == "mipmap"; } -static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true) -{ - sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc")); - sp<AaptFile> file; - if (group != NULL) { - file = group->getFiles().valueFor(AaptGroupEntry()); - if (file != NULL) { - return file; - } - } - - if (!makeIfNecessary) { - return NULL; - } - return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(), - NULL, String8()); -} - static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets, const sp<AaptGroup>& grp) { @@ -359,23 +370,6 @@ static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& ass return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; } -status_t postProcessImages(const sp<AaptAssets>& assets, - ResourceTable* table, - const sp<ResourceTypeSet>& set) -{ - ResourceDirIterator it(set, String8("drawable")); - bool hasErrors = false; - ssize_t res; - while ((res=it.next()) == NO_ERROR) { - res = postProcessImage(assets, table, it.getFile()); - if (res < NO_ERROR) { - hasErrors = true; - } - } - - return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; -} - static void collect_files(const sp<AaptDir>& dir, KeyedVector<String8, sp<ResourceTypeSet> >* resources) { @@ -470,7 +464,7 @@ static int validateAttr(const String8& path, const ResTable& table, value.data); return ATTR_NOT_FOUND; } - + pool = table.getTableStringBlock(strIdx); #if 0 if (pool != NULL) { @@ -595,11 +589,11 @@ static bool applyFileOverlay(Bundle *bundle, if (bundle->getVerbose()) { printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string()); } - size_t baseIndex = UNKNOWN_ERROR; + ssize_t baseIndex = -1; if (baseSet->get() != NULL) { baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex)); } - if (baseIndex < UNKNOWN_ERROR) { + if (baseIndex >= 0) { // look for same flavor. For a given file (strings.xml, for example) // there may be a locale specific or other flavors - we want to match // the same flavor. @@ -625,10 +619,10 @@ static bool applyFileOverlay(Bundle *bundle, for (size_t overlayGroupIndex = 0; overlayGroupIndex<overlayGroupSize; overlayGroupIndex++) { - size_t baseFileIndex = + ssize_t baseFileIndex = baseGroup->getFiles().indexOfKey(overlayFiles. keyAt(overlayGroupIndex)); - if (baseFileIndex < UNKNOWN_ERROR) { + if (baseFileIndex >= 0) { if (bundle->getVerbose()) { printf("found a match (" ZD ") for overlay file %s, for flavor %s\n", (ZD_TYPE) baseFileIndex, @@ -676,13 +670,15 @@ static bool applyFileOverlay(Bundle *bundle, } /* - * Inserts an attribute in a given node, only if the attribute does not - * exist. + * Inserts an attribute in a given node. * If errorOnFailedInsert is true, and the attribute already exists, returns false. - * Returns true otherwise, even if the attribute already exists. + * If replaceExisting is true, the attribute will be updated if it already exists. + * Returns true otherwise, even if the attribute already exists, and does not modify + * the existing attribute's value. */ bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, - const char* attr8, const char* value, bool errorOnFailedInsert) + const char* attr8, const char* value, bool errorOnFailedInsert, + bool replaceExisting) { if (value == NULL) { return true; @@ -691,7 +687,16 @@ bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, const String16 ns(ns8); const String16 attr(attr8); - if (node->getAttribute(ns, attr) != NULL) { + XMLNode::attribute_entry* existingEntry = node->editAttribute(ns, attr); + if (existingEntry != NULL) { + if (replaceExisting) { + NOISY(printf("Info: AndroidManifest.xml already defines %s (in %s);" + " overwriting existing value from manifest.\n", + String8(attr).string(), String8(ns).string())); + existingEntry->string = String16(value); + return true; + } + if (errorOnFailedInsert) { fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);" " cannot insert new value %s.\n", @@ -706,11 +711,23 @@ bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, // don't stop the build. return true; } - + node->addAttribute(ns, attr, String16(value)); return true; } +/* + * Inserts an attribute in a given node, only if the attribute does not + * exist. + * If errorOnFailedInsert is true, and the attribute already exists, returns false. + * Returns true otherwise, even if the attribute already exists. + */ +bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, + const char* attr8, const char* value, bool errorOnFailedInsert) +{ + return addTagAttribute(node, ns8, attr8, value, errorOnFailedInsert, false); +} + static void fullyQualifyClassName(const String8& package, sp<XMLNode> node, const String16& attrName) { XMLNode::attribute_entry* attr = node->editAttribute( @@ -748,16 +765,17 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) } bool errorOnFailedInsert = bundle->getErrorOnFailedInsert(); + bool replaceVersion = bundle->getReplaceVersion(); if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode", - bundle->getVersionCode(), errorOnFailedInsert)) { + bundle->getVersionCode(), errorOnFailedInsert, replaceVersion)) { return UNKNOWN_ERROR; } if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", - bundle->getVersionName(), errorOnFailedInsert)) { + bundle->getVersionName(), errorOnFailedInsert, replaceVersion)) { return UNKNOWN_ERROR; } - + if (bundle->getMinSdkVersion() != NULL || bundle->getTargetSdkVersion() != NULL || bundle->getMaxSdkVersion() != NULL) { @@ -766,7 +784,7 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); root->insertChildAt(vers, 0); } - + if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion", bundle->getMinSdkVersion(), errorOnFailedInsert)) { return UNKNOWN_ERROR; @@ -841,7 +859,7 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) } } } - + return NO_ERROR; } @@ -882,7 +900,38 @@ status_t updatePreProcessedCache(Bundle* bundle) return 0; } -status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) +status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split, + sp<AaptFile>& outFile) { + const String8 filename("AndroidManifest.xml"); + const String16 androidPrefix("android"); + const String16 androidNSUri("http://schemas.android.com/apk/res/android"); + sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri); + + // Build the <manifest> tag + sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest")); + + // Add the 'package' attribute which is set to the original package name. + manifest->addAttribute(String16(), String16("package"), package); + + // Add the 'split' attribute which describes the configurations included. + String8 splitName("config_"); + splitName.append(split->getDirectorySafeName()); + manifest->addAttribute(String16(), String16("split"), String16(splitName)); + + // Build an empty <application> tag (required). + sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application")); + manifest->addChild(app); + root->addChild(manifest); + + status_t err = root->flatten(outFile, true, true); + if (err != NO_ERROR) { + return err; + } + outFile->setCompressionMethod(ZipEntry::kCompressDeflated); + return NO_ERROR; +} + +status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) { // First, look for a package file to parse. This is required to // be able to generate the resource information. @@ -925,7 +974,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // -------------------------------------------------------------- // resType -> leafName -> group - KeyedVector<String8, sp<ResourceTypeSet> > *resources = + KeyedVector<String8, sp<ResourceTypeSet> > *resources = new KeyedVector<String8, sp<ResourceTypeSet> >; collect_files(assets, resources); @@ -957,7 +1006,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // now go through any resource overlays and collect their files sp<AaptAssets> current = assets->getOverlay(); while(current.get()) { - KeyedVector<String8, sp<ResourceTypeSet> > *resources = + KeyedVector<String8, sp<ResourceTypeSet> > *resources = new KeyedVector<String8, sp<ResourceTypeSet> >; current->setResources(resources); collect_files(current, resources); @@ -1060,7 +1109,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // compile resources current = assets; while(current.get()) { - KeyedVector<String8, sp<ResourceTypeSet> > *resources = + KeyedVector<String8, sp<ResourceTypeSet> > *resources = current->getResources(); ssize_t index = resources->indexOfKey(String8("values")); @@ -1069,7 +1118,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) ssize_t res; while ((res=it.next()) == NO_ERROR) { sp<AaptFile> file = it.getFile(); - res = compileResourceFile(bundle, assets, file, it.getParams(), + res = compileResourceFile(bundle, assets, file, it.getParams(), (current!=assets), &table); if (res != NO_ERROR) { hasErrors = true; @@ -1098,12 +1147,6 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // -------------------------------------------------------------------- if (table.hasResources()) { - sp<AaptFile> resFile(getResourceFile(assets)); - if (resFile == NULL) { - fprintf(stderr, "Error: unable to generate entry for resource data\n"); - return UNKNOWN_ERROR; - } - err = table.assignResourceIds(); if (err < NO_ERROR) { return err; @@ -1211,16 +1254,24 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } if (drawables != NULL) { - err = postProcessImages(assets, &table, drawables); - if (err != NO_ERROR) { + ResourceDirIterator it(drawables, String8("drawable")); + while ((err=it.next()) == NO_ERROR) { + err = postProcessImage(assets, &table, it.getFile()); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (err < NO_ERROR) { hasErrors = true; } + err = NO_ERROR; } if (colors != NULL) { ResourceDirIterator it(colors, String8("color")); while ((err=it.next()) == NO_ERROR) { - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } @@ -1255,7 +1306,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) if (table.validateLocalizations()) { hasErrors = true; } - + if (hasErrors) { return UNKNOWN_ERROR; } @@ -1288,7 +1339,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) ResTable finalResTable; sp<AaptFile> resFile; - + if (table.hasResources()) { sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); err = table.addSymbols(symbols); @@ -1296,15 +1347,39 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) return err; } - resFile = getResourceFile(assets); - if (resFile == NULL) { - fprintf(stderr, "Error: unable to generate entry for resource data\n"); - return UNKNOWN_ERROR; - } + Vector<sp<ApkSplit> >& splits = builder->getSplits(); + const size_t numSplits = splits.size(); + for (size_t i = 0; i < numSplits; i++) { + sp<ApkSplit>& split = splits.editItemAt(i); + sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"), + AaptGroupEntry(), String8()); + err = table.flatten(bundle, split->getResourceFilter(), flattenedTable); + if (err != NO_ERROR) { + fprintf(stderr, "Failed to generate resource table for split '%s'\n", + split->getPrintableName().string()); + return err; + } + split->addEntry(String8("resources.arsc"), flattenedTable); - err = table.flatten(bundle, resFile); - if (err < NO_ERROR) { - return err; + if (split->isBase()) { + resFile = flattenedTable; + err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); + if (err != NO_ERROR) { + fprintf(stderr, "Generated resource table is corrupt.\n"); + return err; + } + } else { + sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), + AaptGroupEntry(), String8()); + err = generateAndroidManifestForSplit(String16(assets->getPackage()), split, + generatedManifest); + if (err != NO_ERROR) { + fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n", + split->getPrintableName().string()); + return err; + } + split->addEntry(String8("AndroidManifest.xml"), generatedManifest); + } } if (bundle->getPublicOutputFile()) { @@ -1321,14 +1396,10 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) fclose(fp); } - // Read resources back in, - finalResTable.add(resFile->getData(), resFile->getSize()); -#if 0 - NOISY( - printf("Generated resources:\n"); - finalResTable.print(); - ) -#endif + if (finalResTable.getTableCount() == 0 || resFile == NULL) { + fprintf(stderr, "No resource table was generated.\n"); + return UNKNOWN_ERROR; + } } // Perform a basic validation of the manifest file. This time we @@ -1425,7 +1496,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name"); const uint16_t* id = block.getAttributeStringValue(index, &len); if (id == NULL) { - fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", + fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", manifestPath.string(), block.getLineNumber(), String8(block.getElementName(&len)).string()); hasErrors = true; @@ -1583,7 +1654,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) return err; } } - + return err; } @@ -1704,16 +1775,13 @@ static status_t writeLayoutClasses( NA = idents.size(); - bool deprecated = false; - String16 comment = symbols->getComment(realClassName); + AnnotationProcessor ann; fprintf(fp, "%s/** ", indentStr); if (comment.size() > 0) { String8 cmt(comment); + ann.preprocessComment(cmt); fprintf(fp, "%s\n", cmt.string()); - if (strstr(cmt.string(), "@deprecated") != NULL) { - deprecated = true; - } } else { fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string()); } @@ -1785,10 +1853,8 @@ static status_t writeLayoutClasses( } fprintf(fp, "%s */\n", getIndentSpace(indent)); - if (deprecated) { - fprintf(fp, "%s@Deprecated\n", indentStr); - } - + ann.printAnnotations(fp, indentStr); + fprintf(fp, "%spublic static final int[] %s = {\n" "%s", @@ -1834,16 +1900,13 @@ static status_t writeLayoutClasses( // String8(attr16).string(), String8(name16).string(), typeSpecFlags); const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; - bool deprecated = false; - + AnnotationProcessor ann; fprintf(fp, "%s/**\n", indentStr); if (comment.size() > 0) { String8 cmt(comment); + ann.preprocessComment(cmt); fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr); fprintf(fp, "%s %s\n", indentStr, cmt.string()); - if (strstr(cmt.string(), "@deprecated") != NULL) { - deprecated = true; - } } else { fprintf(fp, "%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n" @@ -1855,10 +1918,8 @@ static status_t writeLayoutClasses( } if (typeComment.size() > 0) { String8 cmt(typeComment); + ann.preprocessComment(cmt); fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string()); - if (strstr(cmt.string(), "@deprecated") != NULL) { - deprecated = true; - } } if (comment.size() > 0) { if (pub) { @@ -1877,9 +1938,7 @@ static status_t writeLayoutClasses( getSymbolPackage(name8, assets, pub).string(), getSymbolName(name8).string()); fprintf(fp, "%s*/\n", indentStr); - if (deprecated) { - fprintf(fp, "%s@Deprecated\n", indentStr); - } + ann.printAnnotations(fp, indentStr); fprintf(fp, "%spublic static final int %s_%s = %d;\n", indentStr, nclassName.string(), @@ -2018,16 +2077,14 @@ static status_t writeSymbolClass( String8 name8(sym.name); String16 comment(sym.comment); bool haveComment = false; - bool deprecated = false; + AnnotationProcessor ann; if (comment.size() > 0) { haveComment = true; String8 cmt(comment); + ann.preprocessComment(cmt); fprintf(fp, "%s/** %s\n", getIndentSpace(indent), cmt.string()); - if (strstr(cmt.string(), "@deprecated") != NULL) { - deprecated = true; - } } else if (sym.isPublic && !includePrivate) { sym.sourcePos.warning("No comment for public symbol %s:%s/%s", assets->getPackage().string(), className.string(), @@ -2036,6 +2093,7 @@ static status_t writeSymbolClass( String16 typeComment(sym.typeComment); if (typeComment.size() > 0) { String8 cmt(typeComment); + ann.preprocessComment(cmt); if (!haveComment) { haveComment = true; fprintf(fp, @@ -2044,16 +2102,11 @@ static status_t writeSymbolClass( fprintf(fp, "%s %s\n", getIndentSpace(indent), cmt.string()); } - if (strstr(cmt.string(), "@deprecated") != NULL) { - deprecated = true; - } } if (haveComment) { fprintf(fp,"%s */\n", getIndentSpace(indent)); } - if (deprecated) { - fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent)); - } + ann.printAnnotations(fp, getIndentSpace(indent)); fprintf(fp, id_format, getIndentSpace(indent), flattenSymbol(name8).string(), (int)sym.int32Val); @@ -2069,25 +2122,21 @@ static status_t writeSymbolClass( } String8 name8(sym.name); String16 comment(sym.comment); - bool deprecated = false; + AnnotationProcessor ann; if (comment.size() > 0) { String8 cmt(comment); + ann.preprocessComment(cmt); fprintf(fp, "%s/** %s\n" "%s */\n", getIndentSpace(indent), cmt.string(), getIndentSpace(indent)); - if (strstr(cmt.string(), "@deprecated") != NULL) { - deprecated = true; - } } else if (sym.isPublic && !includePrivate) { sym.sourcePos.warning("No comment for public symbol %s:%s/%s", assets->getPackage().string(), className.string(), String8(sym.name).string()); } - if (deprecated) { - fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent)); - } + ann.printAnnotations(fp, getIndentSpace(indent)); fprintf(fp, "%spublic static final String %s=\"%s\";\n", getIndentSpace(indent), flattenSymbol(name8).string(), sym.stringVal.string()); @@ -2438,7 +2487,7 @@ struct NamespaceAttributePair { status_t writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, - const char* startTag, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs) + const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs) { status_t err; ResXMLTree tree; @@ -2452,15 +2501,18 @@ writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, tree.restart(); - if (startTag != NULL) { + if (!startTags.isEmpty()) { bool haveStart = false; while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { if (code != ResXMLTree::START_TAG) { continue; } String8 tag(tree.getElementName(&len)); - if (tag == startTag) { - haveStart = true; + const size_t numStartTags = startTags.size(); + for (size_t i = 0; i < numStartTags; i++) { + if (tag == startTags[i]) { + haveStart = true; + } } break; } @@ -2547,15 +2599,17 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) for (size_t k=0; k<K; k++) { const sp<AaptDir>& d = dirs.itemAt(k); const String8& dirName = d->getLeaf(); + Vector<String8> startTags; const char* startTag = NULL; const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL; if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { tagAttrPairs = &kLayoutTagAttrPairs; } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) { - startTag = "PreferenceScreen"; + startTags.add(String8("PreferenceScreen")); + startTags.add(String8("preference-headers")); tagAttrPairs = &kXmlTagAttrPairs; } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) { - startTag = "menu"; + startTags.add(String8("menu")); tagAttrPairs = NULL; } else { continue; @@ -2568,7 +2622,7 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles(); const size_t M = files.size(); for (size_t j=0; j<M; j++) { - err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs); + err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs); if (err < 0) { return err; } |