From 9d0f7d44d5cc5322415f52f7ce03cc37a478b350 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Wed, 28 Oct 2015 15:44:27 -0700 Subject: Implement AAPT Bundle format AAPT will scan XML files looking for the XML tag. The SINGLE child element of the tag is extracted into its own top level resource. It is given a generated name. The parent element of is then given the resource attribute that was assigned to the `name' attribute. The value is set to a reference to the generated resource. Bug:22627686 Change-Id: I31bc96aae30d38bfd0b16508d0f585de5fd88a07 --- tools/aapt/ResourceTable.cpp | 233 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 230 insertions(+), 3 deletions(-) (limited to 'tools/aapt/ResourceTable.cpp') diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index d5a09d817b1e..7d198282fc46 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -88,8 +88,11 @@ status_t compileXmlFile(const Bundle* bundle, root->setUTF8(true); } - bool hasErrors = false; + if (table->processBundleFormat(bundle, resourceName, target, root) != NO_ERROR) { + return UNKNOWN_ERROR; + } + bool hasErrors = false; if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) { status_t err = root->assignResourceIds(assets, table); if (err != NO_ERROR) { @@ -4755,9 +4758,9 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, newConfig.sdkVersion = sdkVersionToGenerate; sp newFile = new AaptFile(target->getSourceFile(), AaptGroupEntry(newConfig), target->getResourceType()); - String8 resPath = String8::format("res/%s/%s", + String8 resPath = String8::format("res/%s/%s.xml", newFile->getGroupEntry().toDirName(target->getResourceType()).string(), - target->getSourceFile().getPathLeaf().string()); + String8(resourceName).string()); resPath.convertToResPath(); // Add a resource table entry. @@ -4784,6 +4787,7 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, item.resourceName = resourceName; item.resPath = resPath; item.file = newFile; + item.xmlRoot = newRoot; mWorkQueue.push(item); } @@ -4825,3 +4829,226 @@ void ResourceTable::getDensityVaryingResources( } } } + +static String16 buildNamespace(const String16& package) { + return String16("http://schemas.android.com/apk/res/") + package; +} + +static sp findOnlyChildElement(const sp& parent) { + const Vector >& children = parent->getChildren(); + sp onlyChild; + for (size_t i = 0; i < children.size(); i++) { + if (children[i]->getType() != XMLNode::TYPE_CDATA) { + if (onlyChild != NULL) { + return NULL; + } + onlyChild = children[i]; + } + } + return onlyChild; +} + +/** + * Detects use of the `bundle' format and extracts nested resources into their own top level + * resources. The bundle format looks like this: + * + * + * + * + * + * + * + * + * + * + * When AAPT sees the tag, it will extract its single element and its children + * into a new high-level resource, assigning it a name and ID. Then value of the `name` + * attribute must be a resource attribute. That resource attribute is inserted into the parent + * with the reference to the extracted resource as the value. + * + * + * + * + * + * + * + * + * + */ +status_t ResourceTable::processBundleFormat(const Bundle* bundle, + const String16& resourceName, + const sp& target, + const sp& root) { + Vector > namespaces; + if (root->getType() == XMLNode::TYPE_NAMESPACE) { + namespaces.push(root); + } + return processBundleFormatImpl(bundle, resourceName, target, root, &namespaces); +} + +status_t ResourceTable::processBundleFormatImpl(const Bundle* bundle, + const String16& resourceName, + const sp& target, + const sp& parent, + Vector >* namespaces) { + const String16 kAaptNamespaceUri16("http://schemas.android.com/aapt"); + const String16 kName16("name"); + const String16 kAttr16("attr"); + const String16 kAssetPackage16(mAssets->getPackage()); + + Vector >& children = parent->getChildren(); + for (size_t i = 0; i < children.size(); i++) { + const sp& child = children[i]; + + if (child->getType() == XMLNode::TYPE_CDATA) { + continue; + } else if (child->getType() == XMLNode::TYPE_NAMESPACE) { + namespaces->push(child); + } + + if (child->getElementNamespace() != kAaptNamespaceUri16 || + child->getElementName() != kAttr16) { + status_t result = processBundleFormatImpl(bundle, resourceName, target, child, + namespaces); + if (result != NO_ERROR) { + return result; + } + + if (child->getType() == XMLNode::TYPE_NAMESPACE) { + namespaces->pop(); + } + continue; + } + + // This is the tag. Look for the 'name' attribute. + SourcePos source(child->getFilename(), child->getStartLineNumber()); + + sp nestedRoot = findOnlyChildElement(child); + if (nestedRoot == NULL) { + source.error("<%s:%s> must have exactly one child element", + String8(child->getElementNamespace()).string(), + String8(child->getElementName()).string()); + return UNKNOWN_ERROR; + } + + // Find the special attribute 'parent-attr'. This attribute's value contains + // the resource attribute for which this element should be assigned in the parent. + const XMLNode::attribute_entry* attr = child->getAttribute(String16(), kName16); + if (attr == NULL) { + source.error("inline resource definition must specify an attribute via 'name'"); + return UNKNOWN_ERROR; + } + + // Parse the attribute name. + const char* errorMsg = NULL; + String16 attrPackage, attrType, attrName; + bool result = ResTable::expandResourceRef(attr->string.string(), + attr->string.size(), + &attrPackage, &attrType, &attrName, + &kAttr16, &kAssetPackage16, + &errorMsg, NULL); + if (!result) { + source.error("invalid attribute name for 'name': %s", errorMsg); + return UNKNOWN_ERROR; + } + + if (attrType != kAttr16) { + // The value of the 'name' attribute must be an attribute reference. + source.error("value of 'name' must be an attribute reference."); + return UNKNOWN_ERROR; + } + + // Generate a name for this nested resource and try to add it to the table. + // We do this in a loop because the name may be taken, in which case we will + // increment a suffix until we succeed. + String8 nestedResourceName; + String8 nestedResourcePath; + int suffix = 1; + while (true) { + // This child element will be extracted into its own resource file. + // Generate a name and path for it from its parent. + nestedResourceName = String8::format("%s_%d", + String8(resourceName).string(), suffix++); + nestedResourcePath = String8::format("res/%s/%s.xml", + target->getGroupEntry().toDirName(target->getResourceType()) + .string(), + nestedResourceName.string()); + + // Lookup or create the entry for this name. + sp entry = getEntry(kAssetPackage16, + String16(target->getResourceType()), + String16(nestedResourceName), + source, + false, + &target->getGroupEntry().toParams(), + true); + if (entry == NULL) { + return UNKNOWN_ERROR; + } + + if (entry->getType() == Entry::TYPE_UNKNOWN) { + // The value for this resource has never been set, + // meaning we're good! + entry->setItem(source, String16(nestedResourcePath)); + break; + } + + // We failed (name already exists), so try with a different name + // (increment the suffix). + } + + if (bundle->getVerbose()) { + source.printf("generating nested resource %s:%s/%s", + mAssets->getPackage().string(), target->getResourceType().string(), + nestedResourceName.string()); + } + + // Build the attribute reference and assign it to the parent. + String16 nestedResourceRef = String16(String8::format("@%s:%s/%s", + mAssets->getPackage().string(), target->getResourceType().string(), + nestedResourceName.string())); + + String16 attrNs = buildNamespace(attrPackage); + if (parent->getAttribute(attrNs, attrName) != NULL) { + SourcePos(parent->getFilename(), parent->getStartLineNumber()) + .error("parent of nested resource already defines attribute '%s:%s'", + String8(attrPackage).string(), String8(attrName).string()); + return UNKNOWN_ERROR; + } + + // Add the reference to the inline resource. + parent->addAttribute(attrNs, attrName, nestedResourceRef); + + // Remove the child element from here. + children.removeAt(i); + i--; + + // Append all namespace declarations that we've seen on this branch in the XML tree + // to this resource. + // We do this because the order of namespace declarations and prefix usage is determined + // by the developer and we do not want to override any decisions. Be conservative. + for (size_t nsIndex = namespaces->size(); nsIndex > 0; nsIndex--) { + const sp& ns = namespaces->itemAt(nsIndex - 1); + sp newNs = XMLNode::newNamespace(ns->getFilename(), ns->getNamespacePrefix(), + ns->getNamespaceUri()); + newNs->addChild(nestedRoot); + nestedRoot = newNs; + } + + // Schedule compilation of the nested resource. + CompileResourceWorkItem workItem; + workItem.resPath = nestedResourcePath; + workItem.resourceName = String16(nestedResourceName); + workItem.xmlRoot = nestedRoot; + workItem.file = new AaptFile(target->getSourceFile(), target->getGroupEntry(), + target->getResourceType()); + mWorkQueue.push(workItem); + } + return NO_ERROR; +} -- cgit v1.2.3-59-g8ed1b From 5b9847ca3fbcd0bb2528dd2b4c0b789e2d501684 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 30 Nov 2015 21:07:44 +0000 Subject: Revert "Implement AAPT Bundle format" This reverts commit 9d0f7d44d5cc5322415f52f7ce03cc37a478b350. Caused a build breakage when parsing some attributes. Change-Id: I89ca958b2f2e820d353648df8aadd90bbe8d8339 --- tools/aapt/Resource.cpp | 9 +- tools/aapt/ResourceTable.cpp | 233 +------------------------------------------ tools/aapt/ResourceTable.h | 12 --- tools/aapt/XMLNode.cpp | 18 ---- tools/aapt/XMLNode.h | 4 +- 5 files changed, 5 insertions(+), 271 deletions(-) (limited to 'tools/aapt/ResourceTable.cpp') diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index f1a8f1c5f170..e22e76de66c2 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -1537,14 +1537,7 @@ status_t buildResources(Bundle* bundle, const sp& assets, sp& workQueue = table.getWorkQueue(); while (!workQueue.empty()) { CompileResourceWorkItem& workItem = workQueue.front(); - if (workItem.xmlRoot != NULL) { - err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.xmlRoot, - workItem.file, &table, xmlFlags); - } else { - err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.file, - &table, xmlFlags); - } - + err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.file, &table, xmlFlags); if (err == NO_ERROR) { assets->addResource(workItem.resPath.getPathLeaf(), workItem.resPath, diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 7d198282fc46..d5a09d817b1e 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -88,11 +88,8 @@ status_t compileXmlFile(const Bundle* bundle, root->setUTF8(true); } - if (table->processBundleFormat(bundle, resourceName, target, root) != NO_ERROR) { - return UNKNOWN_ERROR; - } - bool hasErrors = false; + if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) { status_t err = root->assignResourceIds(assets, table); if (err != NO_ERROR) { @@ -4758,9 +4755,9 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, newConfig.sdkVersion = sdkVersionToGenerate; sp newFile = new AaptFile(target->getSourceFile(), AaptGroupEntry(newConfig), target->getResourceType()); - String8 resPath = String8::format("res/%s/%s.xml", + String8 resPath = String8::format("res/%s/%s", newFile->getGroupEntry().toDirName(target->getResourceType()).string(), - String8(resourceName).string()); + target->getSourceFile().getPathLeaf().string()); resPath.convertToResPath(); // Add a resource table entry. @@ -4787,7 +4784,6 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, item.resourceName = resourceName; item.resPath = resPath; item.file = newFile; - item.xmlRoot = newRoot; mWorkQueue.push(item); } @@ -4829,226 +4825,3 @@ void ResourceTable::getDensityVaryingResources( } } } - -static String16 buildNamespace(const String16& package) { - return String16("http://schemas.android.com/apk/res/") + package; -} - -static sp findOnlyChildElement(const sp& parent) { - const Vector >& children = parent->getChildren(); - sp onlyChild; - for (size_t i = 0; i < children.size(); i++) { - if (children[i]->getType() != XMLNode::TYPE_CDATA) { - if (onlyChild != NULL) { - return NULL; - } - onlyChild = children[i]; - } - } - return onlyChild; -} - -/** - * Detects use of the `bundle' format and extracts nested resources into their own top level - * resources. The bundle format looks like this: - * - * - * - * - * - * - * - * - * - * - * When AAPT sees the tag, it will extract its single element and its children - * into a new high-level resource, assigning it a name and ID. Then value of the `name` - * attribute must be a resource attribute. That resource attribute is inserted into the parent - * with the reference to the extracted resource as the value. - * - * - * - * - * - * - * - * - * - */ -status_t ResourceTable::processBundleFormat(const Bundle* bundle, - const String16& resourceName, - const sp& target, - const sp& root) { - Vector > namespaces; - if (root->getType() == XMLNode::TYPE_NAMESPACE) { - namespaces.push(root); - } - return processBundleFormatImpl(bundle, resourceName, target, root, &namespaces); -} - -status_t ResourceTable::processBundleFormatImpl(const Bundle* bundle, - const String16& resourceName, - const sp& target, - const sp& parent, - Vector >* namespaces) { - const String16 kAaptNamespaceUri16("http://schemas.android.com/aapt"); - const String16 kName16("name"); - const String16 kAttr16("attr"); - const String16 kAssetPackage16(mAssets->getPackage()); - - Vector >& children = parent->getChildren(); - for (size_t i = 0; i < children.size(); i++) { - const sp& child = children[i]; - - if (child->getType() == XMLNode::TYPE_CDATA) { - continue; - } else if (child->getType() == XMLNode::TYPE_NAMESPACE) { - namespaces->push(child); - } - - if (child->getElementNamespace() != kAaptNamespaceUri16 || - child->getElementName() != kAttr16) { - status_t result = processBundleFormatImpl(bundle, resourceName, target, child, - namespaces); - if (result != NO_ERROR) { - return result; - } - - if (child->getType() == XMLNode::TYPE_NAMESPACE) { - namespaces->pop(); - } - continue; - } - - // This is the tag. Look for the 'name' attribute. - SourcePos source(child->getFilename(), child->getStartLineNumber()); - - sp nestedRoot = findOnlyChildElement(child); - if (nestedRoot == NULL) { - source.error("<%s:%s> must have exactly one child element", - String8(child->getElementNamespace()).string(), - String8(child->getElementName()).string()); - return UNKNOWN_ERROR; - } - - // Find the special attribute 'parent-attr'. This attribute's value contains - // the resource attribute for which this element should be assigned in the parent. - const XMLNode::attribute_entry* attr = child->getAttribute(String16(), kName16); - if (attr == NULL) { - source.error("inline resource definition must specify an attribute via 'name'"); - return UNKNOWN_ERROR; - } - - // Parse the attribute name. - const char* errorMsg = NULL; - String16 attrPackage, attrType, attrName; - bool result = ResTable::expandResourceRef(attr->string.string(), - attr->string.size(), - &attrPackage, &attrType, &attrName, - &kAttr16, &kAssetPackage16, - &errorMsg, NULL); - if (!result) { - source.error("invalid attribute name for 'name': %s", errorMsg); - return UNKNOWN_ERROR; - } - - if (attrType != kAttr16) { - // The value of the 'name' attribute must be an attribute reference. - source.error("value of 'name' must be an attribute reference."); - return UNKNOWN_ERROR; - } - - // Generate a name for this nested resource and try to add it to the table. - // We do this in a loop because the name may be taken, in which case we will - // increment a suffix until we succeed. - String8 nestedResourceName; - String8 nestedResourcePath; - int suffix = 1; - while (true) { - // This child element will be extracted into its own resource file. - // Generate a name and path for it from its parent. - nestedResourceName = String8::format("%s_%d", - String8(resourceName).string(), suffix++); - nestedResourcePath = String8::format("res/%s/%s.xml", - target->getGroupEntry().toDirName(target->getResourceType()) - .string(), - nestedResourceName.string()); - - // Lookup or create the entry for this name. - sp entry = getEntry(kAssetPackage16, - String16(target->getResourceType()), - String16(nestedResourceName), - source, - false, - &target->getGroupEntry().toParams(), - true); - if (entry == NULL) { - return UNKNOWN_ERROR; - } - - if (entry->getType() == Entry::TYPE_UNKNOWN) { - // The value for this resource has never been set, - // meaning we're good! - entry->setItem(source, String16(nestedResourcePath)); - break; - } - - // We failed (name already exists), so try with a different name - // (increment the suffix). - } - - if (bundle->getVerbose()) { - source.printf("generating nested resource %s:%s/%s", - mAssets->getPackage().string(), target->getResourceType().string(), - nestedResourceName.string()); - } - - // Build the attribute reference and assign it to the parent. - String16 nestedResourceRef = String16(String8::format("@%s:%s/%s", - mAssets->getPackage().string(), target->getResourceType().string(), - nestedResourceName.string())); - - String16 attrNs = buildNamespace(attrPackage); - if (parent->getAttribute(attrNs, attrName) != NULL) { - SourcePos(parent->getFilename(), parent->getStartLineNumber()) - .error("parent of nested resource already defines attribute '%s:%s'", - String8(attrPackage).string(), String8(attrName).string()); - return UNKNOWN_ERROR; - } - - // Add the reference to the inline resource. - parent->addAttribute(attrNs, attrName, nestedResourceRef); - - // Remove the child element from here. - children.removeAt(i); - i--; - - // Append all namespace declarations that we've seen on this branch in the XML tree - // to this resource. - // We do this because the order of namespace declarations and prefix usage is determined - // by the developer and we do not want to override any decisions. Be conservative. - for (size_t nsIndex = namespaces->size(); nsIndex > 0; nsIndex--) { - const sp& ns = namespaces->itemAt(nsIndex - 1); - sp newNs = XMLNode::newNamespace(ns->getFilename(), ns->getNamespacePrefix(), - ns->getNamespaceUri()); - newNs->addChild(nestedRoot); - nestedRoot = newNs; - } - - // Schedule compilation of the nested resource. - CompileResourceWorkItem workItem; - workItem.resPath = nestedResourcePath; - workItem.resourceName = String16(nestedResourceName); - workItem.xmlRoot = nestedRoot; - workItem.file = new AaptFile(target->getSourceFile(), target->getGroupEntry(), - target->getResourceType()); - mWorkQueue.push(workItem); - } - return NO_ERROR; -} diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 8ef84f773f18..c4bdf09d8b19 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -83,7 +83,6 @@ struct CompileResourceWorkItem { String16 resourceName; String8 resPath; sp file; - sp xmlRoot; }; class ResourceTable : public ResTable::Accessor @@ -207,12 +206,6 @@ public: const sp& file, const sp& root); - status_t processBundleFormat(const Bundle* bundle, - const String16& resourceName, - const sp& file, - const sp& parent); - - sp flatten(Bundle* bundle, const sp& filter, const bool isBase); @@ -593,11 +586,6 @@ private: Res_value* outValue); int getPublicAttributeSdkLevel(uint32_t attrId) const; - status_t processBundleFormatImpl(const Bundle* bundle, - const String16& resourceName, - const sp& file, - const sp& parent, - Vector >* namespaces); String16 mAssetsPackage; PackageType mPackageType; diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp index 5b215daeb494..dc08eb806356 100644 --- a/tools/aapt/XMLNode.cpp +++ b/tools/aapt/XMLNode.cpp @@ -693,12 +693,6 @@ const Vector >& XMLNode::getChildren() const return mChildren; } - -Vector >& XMLNode::getChildren() -{ - return mChildren; -} - const String8& XMLNode::getFilename() const { return mFilename; @@ -723,18 +717,6 @@ const XMLNode::attribute_entry* XMLNode::getAttribute(const String16& ns, return NULL; } -bool XMLNode::removeAttribute(const String16& ns, const String16& name) -{ - for (size_t i = 0; i < mAttributes.size(); i++) { - const attribute_entry& ae(mAttributes.itemAt(i)); - if (ae.ns == ns && ae.name == name) { - removeAttribute(i); - return true; - } - } - return false; -} - XMLNode::attribute_entry* XMLNode::editAttribute(const String16& ns, const String16& name) { diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h index 749bf9f59bf7..b9e5cd574cdc 100644 --- a/tools/aapt/XMLNode.h +++ b/tools/aapt/XMLNode.h @@ -55,7 +55,7 @@ public: sp newCData(const String8& filename) { return new XMLNode(filename); } - + enum type { TYPE_NAMESPACE, TYPE_ELEMENT, @@ -70,7 +70,6 @@ public: const String16& getElementNamespace() const; const String16& getElementName() const; const Vector >& getChildren() const; - Vector >& getChildren(); const String8& getFilename() const; @@ -98,7 +97,6 @@ public: const Vector& getAttributes() const; const attribute_entry* getAttribute(const String16& ns, const String16& name) const; - bool removeAttribute(const String16& ns, const String16& name); attribute_entry* editAttribute(const String16& ns, const String16& name); -- cgit v1.2.3-59-g8ed1b From 07dfd2d8642f8a3630ca6429f740865a0c0bfdf7 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Wed, 28 Oct 2015 15:44:27 -0700 Subject: Implement AAPT Bundle format AAPT will scan XML files looking for the XML tag. The SINGLE child element of the tag is extracted into its own top level resource. It is given a generated name. The parent element of is then given the resource attribute that was assigned to the `name' attribute. The value is set to a reference to the generated resource. Bug:22627686 Change-Id: I8575fc4f739011402662fbf6b3db96df0012f598 --- tools/aapt/Resource.cpp | 20 ++-- tools/aapt/ResourceTable.cpp | 246 +++++++++++++++++++++++++++++++++++++++++-- tools/aapt/ResourceTable.h | 24 ++++- tools/aapt/XMLNode.cpp | 18 ++++ tools/aapt/XMLNode.h | 4 +- 5 files changed, 291 insertions(+), 21 deletions(-) (limited to 'tools/aapt/ResourceTable.cpp') diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index e22e76de66c2..fb0fe38da1ff 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -1537,12 +1537,20 @@ status_t buildResources(Bundle* bundle, const sp& assets, sp& workQueue = table.getWorkQueue(); while (!workQueue.empty()) { CompileResourceWorkItem& workItem = workQueue.front(); - err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.file, &table, xmlFlags); + int xmlCompilationFlags = xmlFlags | XML_COMPILE_PARSE_VALUES + | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS; + if (!workItem.needsCompiling) { + xmlCompilationFlags &= ~XML_COMPILE_ASSIGN_ATTRIBUTE_IDS; + xmlCompilationFlags &= ~XML_COMPILE_PARSE_VALUES; + } + err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.xmlRoot, + workItem.file, &table, xmlCompilationFlags); + if (err == NO_ERROR) { assets->addResource(workItem.resPath.getPathLeaf(), - workItem.resPath, - workItem.file, - workItem.file->getResourceType()); + workItem.resPath, + workItem.file, + workItem.file->getResourceType()); } else { hasErrors = true; } @@ -1737,9 +1745,7 @@ status_t buildResources(Bundle* bundle, const sp& assets, spgetGroupEntry(), manifestFile->getResourceType()); err = compileXmlFile(bundle, assets, String16(), manifestFile, - outManifestFile, &table, - XML_COMPILE_ASSIGN_ATTRIBUTE_IDS - | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES); + outManifestFile, &table, XML_COMPILE_STANDARD_RESOURCE & ~XML_COMPILE_STRIP_COMMENTS); if (err < NO_ERROR) { return err; } diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index d5a09d817b1e..0e470d924f5a 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -88,8 +88,11 @@ status_t compileXmlFile(const Bundle* bundle, root->setUTF8(true); } - bool hasErrors = false; + if (table->processBundleFormat(bundle, resourceName, target, root) != NO_ERROR) { + return UNKNOWN_ERROR; + } + bool hasErrors = false; if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) { status_t err = root->assignResourceIds(assets, table); if (err != NO_ERROR) { @@ -97,9 +100,11 @@ status_t compileXmlFile(const Bundle* bundle, } } - status_t err = root->parseValues(assets, table); - if (err != NO_ERROR) { - hasErrors = true; + if ((options&XML_COMPILE_PARSE_VALUES) != 0) { + status_t err = root->parseValues(assets, table); + if (err != NO_ERROR) { + hasErrors = true; + } } if (hasErrors) { @@ -114,7 +119,7 @@ status_t compileXmlFile(const Bundle* bundle, printf("Input XML Resource:\n"); root->print(); } - err = root->flatten(target, + status_t err = root->flatten(target, (options&XML_COMPILE_STRIP_COMMENTS) != 0, (options&XML_COMPILE_STRIP_RAW_VALUES) != 0); if (err != NO_ERROR) { @@ -4755,9 +4760,9 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, newConfig.sdkVersion = sdkVersionToGenerate; sp newFile = new AaptFile(target->getSourceFile(), AaptGroupEntry(newConfig), target->getResourceType()); - String8 resPath = String8::format("res/%s/%s", + String8 resPath = String8::format("res/%s/%s.xml", newFile->getGroupEntry().toDirName(target->getResourceType()).string(), - target->getSourceFile().getPathLeaf().string()); + String8(resourceName).string()); resPath.convertToResPath(); // Add a resource table entry. @@ -4784,9 +4789,11 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, item.resourceName = resourceName; item.resPath = resPath; item.file = newFile; + item.xmlRoot = newRoot; + item.needsCompiling = false; // This step occurs after we parse/assign, so we don't need + // to do it again. mWorkQueue.push(item); } - return NO_ERROR; } @@ -4825,3 +4832,226 @@ void ResourceTable::getDensityVaryingResources( } } } + +static String16 buildNamespace(const String16& package) { + return String16("http://schemas.android.com/apk/res/") + package; +} + +static sp findOnlyChildElement(const sp& parent) { + const Vector >& children = parent->getChildren(); + sp onlyChild; + for (size_t i = 0; i < children.size(); i++) { + if (children[i]->getType() != XMLNode::TYPE_CDATA) { + if (onlyChild != NULL) { + return NULL; + } + onlyChild = children[i]; + } + } + return onlyChild; +} + +/** + * Detects use of the `bundle' format and extracts nested resources into their own top level + * resources. The bundle format looks like this: + * + * + * + * + * + * + * + * + * + * + * When AAPT sees the tag, it will extract its single element and its children + * into a new high-level resource, assigning it a name and ID. Then value of the `name` + * attribute must be a resource attribute. That resource attribute is inserted into the parent + * with the reference to the extracted resource as the value. + * + * + * + * + * + * + * + * + * + */ +status_t ResourceTable::processBundleFormat(const Bundle* bundle, + const String16& resourceName, + const sp& target, + const sp& root) { + Vector > namespaces; + if (root->getType() == XMLNode::TYPE_NAMESPACE) { + namespaces.push(root); + } + return processBundleFormatImpl(bundle, resourceName, target, root, &namespaces); +} + +status_t ResourceTable::processBundleFormatImpl(const Bundle* bundle, + const String16& resourceName, + const sp& target, + const sp& parent, + Vector >* namespaces) { + const String16 kAaptNamespaceUri16("http://schemas.android.com/aapt"); + const String16 kName16("name"); + const String16 kAttr16("attr"); + const String16 kAssetPackage16(mAssets->getPackage()); + + Vector >& children = parent->getChildren(); + for (size_t i = 0; i < children.size(); i++) { + const sp& child = children[i]; + + if (child->getType() == XMLNode::TYPE_CDATA) { + continue; + } else if (child->getType() == XMLNode::TYPE_NAMESPACE) { + namespaces->push(child); + } + + if (child->getElementNamespace() != kAaptNamespaceUri16 || + child->getElementName() != kAttr16) { + status_t result = processBundleFormatImpl(bundle, resourceName, target, child, + namespaces); + if (result != NO_ERROR) { + return result; + } + + if (child->getType() == XMLNode::TYPE_NAMESPACE) { + namespaces->pop(); + } + continue; + } + + // This is the tag. Look for the 'name' attribute. + SourcePos source(child->getFilename(), child->getStartLineNumber()); + + sp nestedRoot = findOnlyChildElement(child); + if (nestedRoot == NULL) { + source.error("<%s:%s> must have exactly one child element", + String8(child->getElementNamespace()).string(), + String8(child->getElementName()).string()); + return UNKNOWN_ERROR; + } + + // Find the special attribute 'parent-attr'. This attribute's value contains + // the resource attribute for which this element should be assigned in the parent. + const XMLNode::attribute_entry* attr = child->getAttribute(String16(), kName16); + if (attr == NULL) { + source.error("inline resource definition must specify an attribute via 'name'"); + return UNKNOWN_ERROR; + } + + // Parse the attribute name. + const char* errorMsg = NULL; + String16 attrPackage, attrType, attrName; + bool result = ResTable::expandResourceRef(attr->string.string(), + attr->string.size(), + &attrPackage, &attrType, &attrName, + &kAttr16, &kAssetPackage16, + &errorMsg, NULL); + if (!result) { + source.error("invalid attribute name for 'name': %s", errorMsg); + return UNKNOWN_ERROR; + } + + if (attrType != kAttr16) { + // The value of the 'name' attribute must be an attribute reference. + source.error("value of 'name' must be an attribute reference."); + return UNKNOWN_ERROR; + } + + // Generate a name for this nested resource and try to add it to the table. + // We do this in a loop because the name may be taken, in which case we will + // increment a suffix until we succeed. + String8 nestedResourceName; + String8 nestedResourcePath; + int suffix = 1; + while (true) { + // This child element will be extracted into its own resource file. + // Generate a name and path for it from its parent. + nestedResourceName = String8::format("%s_%d", + String8(resourceName).string(), suffix++); + nestedResourcePath = String8::format("res/%s/%s.xml", + target->getGroupEntry().toDirName(target->getResourceType()) + .string(), + nestedResourceName.string()); + + // Lookup or create the entry for this name. + sp entry = getEntry(kAssetPackage16, + String16(target->getResourceType()), + String16(nestedResourceName), + source, + false, + &target->getGroupEntry().toParams(), + true); + if (entry == NULL) { + return UNKNOWN_ERROR; + } + + if (entry->getType() == Entry::TYPE_UNKNOWN) { + // The value for this resource has never been set, + // meaning we're good! + entry->setItem(source, String16(nestedResourcePath)); + break; + } + + // We failed (name already exists), so try with a different name + // (increment the suffix). + } + + if (bundle->getVerbose()) { + source.printf("generating nested resource %s:%s/%s", + mAssets->getPackage().string(), target->getResourceType().string(), + nestedResourceName.string()); + } + + // Build the attribute reference and assign it to the parent. + String16 nestedResourceRef = String16(String8::format("@%s:%s/%s", + mAssets->getPackage().string(), target->getResourceType().string(), + nestedResourceName.string())); + + String16 attrNs = buildNamespace(attrPackage); + if (parent->getAttribute(attrNs, attrName) != NULL) { + SourcePos(parent->getFilename(), parent->getStartLineNumber()) + .error("parent of nested resource already defines attribute '%s:%s'", + String8(attrPackage).string(), String8(attrName).string()); + return UNKNOWN_ERROR; + } + + // Add the reference to the inline resource. + parent->addAttribute(attrNs, attrName, nestedResourceRef); + + // Remove the child element from here. + children.removeAt(i); + i--; + + // Append all namespace declarations that we've seen on this branch in the XML tree + // to this resource. + // We do this because the order of namespace declarations and prefix usage is determined + // by the developer and we do not want to override any decisions. Be conservative. + for (size_t nsIndex = namespaces->size(); nsIndex > 0; nsIndex--) { + const sp& ns = namespaces->itemAt(nsIndex - 1); + sp newNs = XMLNode::newNamespace(ns->getFilename(), ns->getNamespacePrefix(), + ns->getNamespaceUri()); + newNs->addChild(nestedRoot); + nestedRoot = newNs; + } + + // Schedule compilation of the nested resource. + CompileResourceWorkItem workItem; + workItem.resPath = nestedResourcePath; + workItem.resourceName = String16(nestedResourceName); + workItem.xmlRoot = nestedRoot; + workItem.file = new AaptFile(target->getSourceFile(), target->getGroupEntry(), + target->getResourceType()); + mWorkQueue.push(workItem); + } + return NO_ERROR; +} diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index c4bdf09d8b19..4b7b3cdcef2b 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -23,13 +23,14 @@ class ResourceTable; enum { XML_COMPILE_STRIP_COMMENTS = 1<<0, XML_COMPILE_ASSIGN_ATTRIBUTE_IDS = 1<<1, - XML_COMPILE_COMPACT_WHITESPACE = 1<<2, - XML_COMPILE_STRIP_WHITESPACE = 1<<3, - XML_COMPILE_STRIP_RAW_VALUES = 1<<4, - XML_COMPILE_UTF8 = 1<<5, + XML_COMPILE_PARSE_VALUES = 1 << 2, + XML_COMPILE_COMPACT_WHITESPACE = 1<<3, + XML_COMPILE_STRIP_WHITESPACE = 1<<4, + XML_COMPILE_STRIP_RAW_VALUES = 1<<5, + XML_COMPILE_UTF8 = 1<<6, XML_COMPILE_STANDARD_RESOURCE = - XML_COMPILE_STRIP_COMMENTS | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS + XML_COMPILE_STRIP_COMMENTS | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS | XML_COMPILE_PARSE_VALUES | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES }; @@ -83,6 +84,8 @@ struct CompileResourceWorkItem { String16 resourceName; String8 resPath; sp file; + sp xmlRoot; + bool needsCompiling = true; }; class ResourceTable : public ResTable::Accessor @@ -206,6 +209,12 @@ public: const sp& file, const sp& root); + status_t processBundleFormat(const Bundle* bundle, + const String16& resourceName, + const sp& file, + const sp& parent); + + sp flatten(Bundle* bundle, const sp& filter, const bool isBase); @@ -586,6 +595,11 @@ private: Res_value* outValue); int getPublicAttributeSdkLevel(uint32_t attrId) const; + status_t processBundleFormatImpl(const Bundle* bundle, + const String16& resourceName, + const sp& file, + const sp& parent, + Vector >* namespaces); String16 mAssetsPackage; PackageType mPackageType; diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp index dc08eb806356..5b215daeb494 100644 --- a/tools/aapt/XMLNode.cpp +++ b/tools/aapt/XMLNode.cpp @@ -693,6 +693,12 @@ const Vector >& XMLNode::getChildren() const return mChildren; } + +Vector >& XMLNode::getChildren() +{ + return mChildren; +} + const String8& XMLNode::getFilename() const { return mFilename; @@ -717,6 +723,18 @@ const XMLNode::attribute_entry* XMLNode::getAttribute(const String16& ns, return NULL; } +bool XMLNode::removeAttribute(const String16& ns, const String16& name) +{ + for (size_t i = 0; i < mAttributes.size(); i++) { + const attribute_entry& ae(mAttributes.itemAt(i)); + if (ae.ns == ns && ae.name == name) { + removeAttribute(i); + return true; + } + } + return false; +} + XMLNode::attribute_entry* XMLNode::editAttribute(const String16& ns, const String16& name) { diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h index b9e5cd574cdc..749bf9f59bf7 100644 --- a/tools/aapt/XMLNode.h +++ b/tools/aapt/XMLNode.h @@ -55,7 +55,7 @@ public: sp newCData(const String8& filename) { return new XMLNode(filename); } - + enum type { TYPE_NAMESPACE, TYPE_ELEMENT, @@ -70,6 +70,7 @@ public: const String16& getElementNamespace() const; const String16& getElementName() const; const Vector >& getChildren() const; + Vector >& getChildren(); const String8& getFilename() const; @@ -97,6 +98,7 @@ public: const Vector& getAttributes() const; const attribute_entry* getAttribute(const String16& ns, const String16& name) const; + bool removeAttribute(const String16& ns, const String16& name); attribute_entry* editAttribute(const String16& ns, const String16& name); -- cgit v1.2.3-59-g8ed1b From 78713998f94a2cc2562aa0ae7761bbc563d8eafa Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 7 Dec 2015 14:02:15 -0800 Subject: Specify private resource package in Android.mk Private resource package shouldn't be buried in some resource file. It can now be specified on the command line via the Android.mk file. Change-Id: I9e3cb0bf54830d6b021077af271913306c024701 --- tools/aapt/Bundle.h | 7 +++++++ tools/aapt/Main.cpp | 14 +++++++++++++- tools/aapt/Resource.cpp | 6 ++++++ tools/aapt/ResourceTable.cpp | 10 +++++++++- 4 files changed, 35 insertions(+), 2 deletions(-) (limited to 'tools/aapt/ResourceTable.cpp') diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index c29bb482c159..c4495509614e 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -127,6 +127,12 @@ public: const android::String8& getPlatformBuildVersionName() { return mPlatformVersionName; } void setPlatformBuildVersionName(const android::String8& name) { mPlatformVersionName = name; } + const android::String8& getPrivateSymbolsPackage() const { return mPrivateSymbolsPackage; } + + void setPrivateSymbolsPackage(const android::String8& package) { + mPrivateSymbolsPackage = package; + } + bool getUTF16StringsOption() { return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO); } @@ -333,6 +339,7 @@ private: bool mBuildAppAsSharedLibrary; android::String8 mPlatformVersionCode; android::String8 mPlatformVersionName; + android::String8 mPrivateSymbolsPackage; /* file specification */ int mArgc; diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 64112867a4b4..c424cc516b56 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -220,7 +220,9 @@ void usage(void) " Prevents symbols from being generated for strings that do not have a default\n" " localization\n" " --no-version-vectors\n" - " Do not automatically generate versioned copies of vector XML resources.\n", + " Do not automatically generate versioned copies of vector XML resources.\n" + " --private-symbols\n" + " Java package name to use when generating R.java for private resources.\n", gDefaultIgnoreAssets); } @@ -689,6 +691,16 @@ int main(int argc, char* const argv[]) bundle.setPseudolocalize(PSEUDO_ACCENTED | PSEUDO_BIDI); } else if (strcmp(cp, "-no-version-vectors") == 0) { bundle.setNoVersionVectors(true); + } else if (strcmp(cp, "-private-symbols") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for " + "'--private-symbols' option\n"); + wantUsage = true; + goto bail; + } + bundle.setPrivateSymbolsPackage(String8(argv[0])); } else { fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp); wantUsage = true; diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index fb0fe38da1ff..576f076f4a3f 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -1161,6 +1161,12 @@ status_t buildResources(Bundle* bundle, const sp& assets, spgetPackage().string()); } + // Set the private symbols package if it was declared. + // This can also be declared in XML as + if (bundle->getPrivateSymbolsPackage().size() != 0) { + assets->setSymbolsPrivatePackage(bundle->getPrivateSymbolsPackage()); + } + ResourceTable::PackageType packageType = ResourceTable::App; if (bundle->getBuildSharedLibrary()) { packageType = ResourceTable::SharedLibrary; diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 0e470d924f5a..e87c7d40f1d4 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -1141,7 +1141,15 @@ status_t compileResourceFile(Bundle* bundle, } pkg = String16(block.getAttributeStringValue(pkgIdx, &len)); if (!localHasErrors) { - assets->setSymbolsPrivatePackage(String8(pkg)); + SourcePos(in->getPrintableSource(), block.getLineNumber()).warning( + " is deprecated. Use the command line flag " + "--private-symbols instead.\n"); + if (assets->havePrivateSymbols()) { + SourcePos(in->getPrintableSource(), block.getLineNumber()).warning( + "private symbol package already specified. Ignoring...\n"); + } else { + assets->setSymbolsPrivatePackage(String8(pkg)); + } } while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { -- cgit v1.2.3-59-g8ed1b From c25283b5e206ceae2aa6eb01877b79c3c1f0adfb Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 22 Feb 2016 09:16:33 -0800 Subject: AAPT: Allow duplicate attribute definitions if identical This allows libraries to coexist a bit easier, since a lot of them declare the same attribute. When used together they cause errors. Bug:27101533 Change-Id: I10327251636392e810637a79e7c9aa514f3f07a6 --- tools/aapt/ResourceTable.cpp | 64 +++++++++++++++++++++++++++++++------------- tools/aapt/ResourceTable.h | 12 +++++++++ 2 files changed, 58 insertions(+), 18 deletions(-) (limited to 'tools/aapt/ResourceTable.cpp') diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index e87c7d40f1d4..f979c8423500 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -308,29 +308,13 @@ struct PendingAttribute } added = true; - String16 attr16("attr"); - - if (outTable->hasBagOrEntry(myPackage, attr16, ident)) { + if (!outTable->makeAttribute(myPackage, ident, sourcePos, type, comment, appendComment)) { sourcePos.error("Attribute \"%s\" has already been defined\n", String8(ident).string()); hasErrors = true; return UNKNOWN_ERROR; } - - char numberStr[16]; - sprintf(numberStr, "%d", type); - status_t err = outTable->addBag(sourcePos, myPackage, - attr16, ident, String16(""), - String16("^type"), - String16(numberStr), NULL, NULL); - if (err != NO_ERROR) { - hasErrors = true; - return err; - } - outTable->appendComment(myPackage, attr16, ident, comment, appendComment); - //printf("Attribute %s comment: %s\n", String8(ident).string(), - // String8(comment).string()); - return err; + return UNKNOWN_ERROR; } }; @@ -2115,6 +2099,50 @@ bool ResourceTable::appendTypeComment(const String16& package, return false; } +bool ResourceTable::makeAttribute(const String16& package, + const String16& name, + const SourcePos& source, + int32_t format, + const String16& comment, + bool shouldAppendComment) { + const String16 attr16("attr"); + + // First look for this in the included resources... + uint32_t rid = mAssets->getIncludedResources() + .identifierForName(name.string(), name.size(), + attr16.string(), attr16.size(), + package.string(), package.size()); + if (rid != 0) { + return false; + } + + sp entry = getEntry(package, attr16, name, source, false); + if (entry == NULL) { + return false; + } + + if (entry->makeItABag(source) != NO_ERROR) { + return false; + } + + const String16 formatKey16("^type"); + const String16 formatValue16(String8::format("%d", format)); + + ssize_t idx = entry->getBag().indexOfKey(formatKey16); + if (idx >= 0) { + // We have already set a format for this attribute, check if they are different. + // We allow duplicate attribute definitions so long as they are identical. + // This is to ensure interoperation with libraries that define the same generic attribute. + if (entry->getBag().valueAt(idx).value != formatValue16) { + return false; + } + } else { + entry->addToBag(source, formatKey16, formatValue16); + } + appendComment(package, attr16, name, comment, shouldAppendComment); + return true; +} + void ResourceTable::canAddEntry(const SourcePos& pos, const String16& package, const String16& type, const String16& name) { diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 4b7b3cdcef2b..cf1e992ec330 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -571,6 +571,18 @@ public: void getDensityVaryingResources(KeyedVector >& resources); + /** + * Make an attribute with the specified format. If another attribute with the same name but + * different format exists, this method returns false. If the name is not taken, or if the + * format is identical, this returns true. + */ + bool makeAttribute(const String16& package, + const String16& name, + const SourcePos& source, + int32_t format, + const String16& comment, + bool appendComment); + private: void writePublicDefinitions(const String16& package, FILE* fp, bool pub); sp getPackage(const String16& package); -- cgit v1.2.3-59-g8ed1b From 525db2489d8bb1204225e697171d3f18b6fc9695 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Thu, 25 Feb 2016 23:13:08 +0000 Subject: Revert "AAPT: Allow duplicate attribute definitions if identical" This reverts commit c25283b5e206ceae2aa6eb01877b79c3c1f0adfb. Change-Id: I2f42a4360fa53c4333494afe0178dcec56d62d8e --- tools/aapt/ResourceTable.cpp | 64 +++++++++++++------------------------------- tools/aapt/ResourceTable.h | 12 --------- 2 files changed, 18 insertions(+), 58 deletions(-) (limited to 'tools/aapt/ResourceTable.cpp') diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index f979c8423500..e87c7d40f1d4 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -308,13 +308,29 @@ struct PendingAttribute } added = true; - if (!outTable->makeAttribute(myPackage, ident, sourcePos, type, comment, appendComment)) { + String16 attr16("attr"); + + if (outTable->hasBagOrEntry(myPackage, attr16, ident)) { sourcePos.error("Attribute \"%s\" has already been defined\n", String8(ident).string()); hasErrors = true; return UNKNOWN_ERROR; } - return UNKNOWN_ERROR; + + char numberStr[16]; + sprintf(numberStr, "%d", type); + status_t err = outTable->addBag(sourcePos, myPackage, + attr16, ident, String16(""), + String16("^type"), + String16(numberStr), NULL, NULL); + if (err != NO_ERROR) { + hasErrors = true; + return err; + } + outTable->appendComment(myPackage, attr16, ident, comment, appendComment); + //printf("Attribute %s comment: %s\n", String8(ident).string(), + // String8(comment).string()); + return err; } }; @@ -2099,50 +2115,6 @@ bool ResourceTable::appendTypeComment(const String16& package, return false; } -bool ResourceTable::makeAttribute(const String16& package, - const String16& name, - const SourcePos& source, - int32_t format, - const String16& comment, - bool shouldAppendComment) { - const String16 attr16("attr"); - - // First look for this in the included resources... - uint32_t rid = mAssets->getIncludedResources() - .identifierForName(name.string(), name.size(), - attr16.string(), attr16.size(), - package.string(), package.size()); - if (rid != 0) { - return false; - } - - sp entry = getEntry(package, attr16, name, source, false); - if (entry == NULL) { - return false; - } - - if (entry->makeItABag(source) != NO_ERROR) { - return false; - } - - const String16 formatKey16("^type"); - const String16 formatValue16(String8::format("%d", format)); - - ssize_t idx = entry->getBag().indexOfKey(formatKey16); - if (idx >= 0) { - // We have already set a format for this attribute, check if they are different. - // We allow duplicate attribute definitions so long as they are identical. - // This is to ensure interoperation with libraries that define the same generic attribute. - if (entry->getBag().valueAt(idx).value != formatValue16) { - return false; - } - } else { - entry->addToBag(source, formatKey16, formatValue16); - } - appendComment(package, attr16, name, comment, shouldAppendComment); - return true; -} - void ResourceTable::canAddEntry(const SourcePos& pos, const String16& package, const String16& type, const String16& name) { diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index cf1e992ec330..4b7b3cdcef2b 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -571,18 +571,6 @@ public: void getDensityVaryingResources(KeyedVector >& resources); - /** - * Make an attribute with the specified format. If another attribute with the same name but - * different format exists, this method returns false. If the name is not taken, or if the - * format is identical, this returns true. - */ - bool makeAttribute(const String16& package, - const String16& name, - const SourcePos& source, - int32_t format, - const String16& comment, - bool appendComment); - private: void writePublicDefinitions(const String16& package, FILE* fp, bool pub); sp getPackage(const String16& package); -- cgit v1.2.3-59-g8ed1b From afc79be228596c56ea68c0d2fa22d0a8d733be47 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 22 Feb 2016 09:16:33 -0800 Subject: AAPT: Allow duplicate attribute definitions if identical This allows libraries to coexist a bit easier, since a lot of them declare the same attribute. When used together they cause errors. Bug:27101533 Change-Id: I649cf17bc587c4814f849d5ac522daf74495adcf --- tools/aapt/ResourceTable.cpp | 77 ++++++++++++++++++++++++++++++++------------ tools/aapt/ResourceTable.h | 12 +++++++ 2 files changed, 69 insertions(+), 20 deletions(-) (limited to 'tools/aapt/ResourceTable.cpp') diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index e87c7d40f1d4..6a4b63789815 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -308,29 +308,11 @@ struct PendingAttribute } added = true; - String16 attr16("attr"); - - if (outTable->hasBagOrEntry(myPackage, attr16, ident)) { - sourcePos.error("Attribute \"%s\" has already been defined\n", - String8(ident).string()); + if (!outTable->makeAttribute(myPackage, ident, sourcePos, type, comment, appendComment)) { hasErrors = true; return UNKNOWN_ERROR; } - - char numberStr[16]; - sprintf(numberStr, "%d", type); - status_t err = outTable->addBag(sourcePos, myPackage, - attr16, ident, String16(""), - String16("^type"), - String16(numberStr), NULL, NULL); - if (err != NO_ERROR) { - hasErrors = true; - return err; - } - outTable->appendComment(myPackage, attr16, ident, comment, appendComment); - //printf("Attribute %s comment: %s\n", String8(ident).string(), - // String8(comment).string()); - return err; + return NO_ERROR; } }; @@ -2115,6 +2097,61 @@ bool ResourceTable::appendTypeComment(const String16& package, return false; } +bool ResourceTable::makeAttribute(const String16& package, + const String16& name, + const SourcePos& source, + int32_t format, + const String16& comment, + bool shouldAppendComment) { + const String16 attr16("attr"); + + // First look for this in the included resources... + uint32_t rid = mAssets->getIncludedResources() + .identifierForName(name.string(), name.size(), + attr16.string(), attr16.size(), + package.string(), package.size()); + if (rid != 0) { + source.error("Attribute \"%s\" has already been defined", String8(name).string()); + return false; + } + + sp entry = getEntry(package, attr16, name, source, false); + if (entry == NULL) { + source.error("Failed to create entry attr/%s", String8(name).string()); + return false; + } + + if (entry->makeItABag(source) != NO_ERROR) { + return false; + } + + const String16 formatKey16("^type"); + const String16 formatValue16(String8::format("%d", format)); + + ssize_t idx = entry->getBag().indexOfKey(formatKey16); + if (idx >= 0) { + // We have already set a format for this attribute, check if they are different. + // We allow duplicate attribute definitions so long as they are identical. + // This is to ensure inter-operation with libraries that define the same generic attribute. + const Item& formatItem = entry->getBag().valueAt(idx); + if ((format & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) || + formatItem.value != formatValue16) { + source.error("Attribute \"%s\" already defined with incompatible format.\n" + "%s:%d: Original attribute defined here.", + String8(name).string(), formatItem.sourcePos.file.string(), + formatItem.sourcePos.line); + return false; + } + } else { + entry->addToBag(source, formatKey16, formatValue16); + // Increment the number of resources we have. This is used to determine if we should + // even generate a resource table. + mNumLocal++; + } + appendComment(package, attr16, name, comment, shouldAppendComment); + return true; +} + void ResourceTable::canAddEntry(const SourcePos& pos, const String16& package, const String16& type, const String16& name) { diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 4b7b3cdcef2b..cf1e992ec330 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -571,6 +571,18 @@ public: void getDensityVaryingResources(KeyedVector >& resources); + /** + * Make an attribute with the specified format. If another attribute with the same name but + * different format exists, this method returns false. If the name is not taken, or if the + * format is identical, this returns true. + */ + bool makeAttribute(const String16& package, + const String16& name, + const SourcePos& source, + int32_t format, + const String16& comment, + bool appendComment); + private: void writePublicDefinitions(const String16& package, FILE* fp, bool pub); sp getPackage(const String16& package); -- cgit v1.2.3-59-g8ed1b