]> git.saurik.com Git - android/aapt.git/blobdiff - Resource.cpp
Fix writing text version of styleable IDs. do not merge.
[android/aapt.git] / Resource.cpp
index b9ec30ca4915c69f3736bfe8ef5408f1264f469c..77168f948640fa57abbc70094ebf313e4c5be0af 100644 (file)
@@ -673,24 +673,40 @@ static bool applyFileOverlay(Bundle *bundle,
     return true;
 }
 
-void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
-        const char* attr8, const char* value)
+/*
+ * 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)
 {
     if (value == NULL) {
-        return;
+        return true;
     }
-    
+
     const String16 ns(ns8);
     const String16 attr(attr8);
-    
+
     if (node->getAttribute(ns, attr) != NULL) {
+        if (errorOnFailedInsert) {
+            fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);"
+                            " cannot insert new value %s.\n",
+                    String8(attr).string(), String8(ns).string(), value);
+            return false;
+        }
+
         fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
                         " using existing value in manifest.\n",
                 String8(attr).string(), String8(ns).string());
-        return;
+
+        // don't stop the build.
+        return true;
     }
     
     node->addAttribute(ns, attr, String16(value));
+    return true;
 }
 
 static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
@@ -728,11 +744,17 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
         fprintf(stderr, "No <manifest> tag.\n");
         return UNKNOWN_ERROR;
     }
-    
-    addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
-            bundle->getVersionCode());
-    addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
-            bundle->getVersionName());
+
+    bool errorOnFailedInsert = bundle->getErrorOnFailedInsert();
+
+    if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
+            bundle->getVersionCode(), errorOnFailedInsert)) {
+        return UNKNOWN_ERROR;
+    }
+    if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
+            bundle->getVersionName(), errorOnFailedInsert)) {
+        return UNKNOWN_ERROR;
+    }
     
     if (bundle->getMinSdkVersion() != NULL
             || bundle->getTargetSdkVersion() != NULL
@@ -743,18 +765,27 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
             root->insertChildAt(vers, 0);
         }
         
-        addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
-                bundle->getMinSdkVersion());
-        addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
-                bundle->getTargetSdkVersion());
-        addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
-                bundle->getMaxSdkVersion());
+        if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
+                bundle->getMinSdkVersion(), errorOnFailedInsert)) {
+            return UNKNOWN_ERROR;
+        }
+        if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
+                bundle->getTargetSdkVersion(), errorOnFailedInsert)) {
+            return UNKNOWN_ERROR;
+        }
+        if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
+                bundle->getMaxSdkVersion(), errorOnFailedInsert)) {
+            return UNKNOWN_ERROR;
+        }
     }
 
     if (bundle->getDebugMode()) {
         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
         if (application != NULL) {
-            addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true");
+            if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true",
+                    errorOnFailedInsert)) {
+                return UNKNOWN_ERROR;
+            }
         }
     }
 
@@ -1821,6 +1852,110 @@ static status_t writeLayoutClasses(
     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
 }
 
+static status_t writeTextLayoutClasses(
+    FILE* fp, const sp<AaptAssets>& assets,
+    const sp<AaptSymbols>& symbols, bool includePrivate)
+{
+    String16 attr16("attr");
+    String16 package16(assets->getPackage());
+
+    bool hasErrors = false;
+
+    size_t i;
+    size_t N = symbols->getNestedSymbols().size();
+    for (i=0; i<N; i++) {
+        sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
+        String16 nclassName16(symbols->getNestedSymbols().keyAt(i));
+        String8 realClassName(nclassName16);
+        if (fixupSymbol(&nclassName16) != NO_ERROR) {
+            hasErrors = true;
+        }
+        String8 nclassName(nclassName16);
+
+        SortedVector<uint32_t> idents;
+        Vector<uint32_t> origOrder;
+        Vector<bool> publicFlags;
+
+        size_t a;
+        size_t NA = nsymbols->getSymbols().size();
+        for (a=0; a<NA; a++) {
+            const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
+            int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
+                    ? sym.int32Val : 0;
+            bool isPublic = true;
+            if (code == 0) {
+                String16 name16(sym.name);
+                uint32_t typeSpecFlags;
+                code = assets->getIncludedResources().identifierForName(
+                    name16.string(), name16.size(),
+                    attr16.string(), attr16.size(),
+                    package16.string(), package16.size(), &typeSpecFlags);
+                if (code == 0) {
+                    fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
+                            nclassName.string(), sym.name.string());
+                    hasErrors = true;
+                }
+                isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
+            }
+            idents.add(code);
+            origOrder.add(code);
+            publicFlags.add(isPublic);
+        }
+
+        NA = idents.size();
+
+        fprintf(fp, "int[] styleable %s {", nclassName.string());
+
+        for (a=0; a<NA; a++) {
+            if (a != 0) {
+                fprintf(fp, ",");
+            }
+            fprintf(fp, " 0x%08x", idents[a]);
+        }
+
+        fprintf(fp, " }\n");
+
+        for (a=0; a<NA; a++) {
+            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
+            if (pos >= 0) {
+                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
+                if (!publicFlags.itemAt(a) && !includePrivate) {
+                    continue;
+                }
+                String8 name8(sym.name);
+                String16 comment(sym.comment);
+                String16 typeComment;
+                if (comment.size() <= 0) {
+                    comment = getAttributeComment(assets, name8, &typeComment);
+                } else {
+                    getAttributeComment(assets, name8, &typeComment);
+                }
+                String16 name(name8);
+                if (fixupSymbol(&name) != NO_ERROR) {
+                    hasErrors = true;
+                }
+
+                uint32_t typeSpecFlags = 0;
+                String16 name16(sym.name);
+                assets->getIncludedResources().identifierForName(
+                    name16.string(), name16.size(),
+                    attr16.string(), attr16.size(),
+                    package16.string(), package16.size(), &typeSpecFlags);
+                //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
+                //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
+                const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
+
+                fprintf(fp,
+                        "int styleable %s_%s %d\n",
+                        nclassName.string(),
+                        String8(name).string(), (int)pos);
+            }
+        }
+    }
+
+    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+}
+
 static status_t writeSymbolClass(
     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
     const sp<AaptSymbols>& symbols, const String8& className, int indent,
@@ -1848,7 +1983,6 @@ static status_t writeSymbolClass(
             continue;
         }
         String16 name(sym.name);
-        String8 realName(name);
         if (fixupSymbol(&name) != NO_ERROR) {
             return UNKNOWN_ERROR;
         }
@@ -1960,6 +2094,51 @@ static status_t writeSymbolClass(
     return NO_ERROR;
 }
 
+static status_t writeTextSymbolClass(
+    FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
+    const sp<AaptSymbols>& symbols, const String8& className)
+{
+    size_t i;
+    status_t err = NO_ERROR;
+
+    size_t N = symbols->getSymbols().size();
+    for (i=0; i<N; i++) {
+        const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
+        if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
+            continue;
+        }
+
+        if (!assets->isJavaSymbol(sym, includePrivate)) {
+            continue;
+        }
+
+        String16 name(sym.name);
+        if (fixupSymbol(&name) != NO_ERROR) {
+            return UNKNOWN_ERROR;
+        }
+
+        fprintf(fp, "int %s %s 0x%08x\n",
+                className.string(),
+                String8(name).string(), (int)sym.int32Val);
+    }
+
+    N = symbols->getNestedSymbols().size();
+    for (i=0; i<N; i++) {
+        sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
+        String8 nclassName(symbols->getNestedSymbols().keyAt(i));
+        if (nclassName == "styleable") {
+            err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate);
+        } else {
+            err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName);
+        }
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
 status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
     const String8& package, bool includePrivate)
 {
@@ -1967,11 +2146,15 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
         return NO_ERROR;
     }
 
+    const char* textSymbolsDest = bundle->getOutputTextSymbols();
+
+    String8 R("R");
     const size_t N = assets->getSymbols().size();
     for (size_t i=0; i<N; i++) {
         sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
         String8 className(assets->getSymbols().keyAt(i));
         String8 dest(bundle->getRClassDir());
+
         if (bundle->getMakePackageDirs()) {
             String8 pkg(package);
             const char* last = pkg.string();
@@ -2003,14 +2186,14 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
         }
 
         fprintf(fp,
-        "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
-        " *\n"
-        " * This class was automatically generated by the\n"
-        " * aapt tool from the resource data it found.  It\n"
-        " * should not be modified by hand.\n"
-        " */\n"
-        "\n"
-        "package %s;\n\n", package.string());
+            "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
+            " *\n"
+            " * This class was automatically generated by the\n"
+            " * aapt tool from the resource data it found.  It\n"
+            " * should not be modified by hand.\n"
+            " */\n"
+            "\n"
+            "package %s;\n\n", package.string());
 
         status_t err = writeSymbolClass(fp, assets, includePrivate, symbols,
                 className, 0, bundle->getNonConstantId());
@@ -2019,14 +2202,37 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
         }
         fclose(fp);
 
+        if (textSymbolsDest != NULL && R == className) {
+            String8 textDest(textSymbolsDest);
+            textDest.appendPath(className);
+            textDest.append(".txt");
+
+            FILE* fp = fopen(textDest.string(), "w+");
+            if (fp == NULL) {
+                fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n",
+                        textDest.string(), strerror(errno));
+                return UNKNOWN_ERROR;
+            }
+            if (bundle->getVerbose()) {
+                printf("  Writing text symbols for class %s.\n", className.string());
+            }
+
+            status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols,
+                    className);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            fclose(fp);
+        }
+
         // If we were asked to generate a dependency file, we'll go ahead and add this R.java
         // as a target in the dependency file right next to it.
-        if (bundle->getGenDependencies()) {
+        if (bundle->getGenDependencies() && R == className) {
             // Add this R.java to the dependency file
             String8 dependencyFile(bundle->getRClassDir());
             dependencyFile.appendPath("R.java.d");
 
-            fp = fopen(dependencyFile.string(), "a");
+            FILE *fp = fopen(dependencyFile.string(), "a");
             fprintf(fp,"%s \\\n", dest.string());
             fclose(fp);
         }
@@ -2036,7 +2242,6 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
 }
 
 
-
 class ProguardKeepSet
 {
 public:
@@ -2089,6 +2294,23 @@ addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
     keep->add(rule, location);
 }
 
+void
+addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName,
+        const char* pkg, const String8& srcName, int line)
+{
+    String8 rule("-keepclassmembers class * { *** ");
+    rule += memberName;
+    rule += "(...); }";
+
+    String8 location("onClick ");
+    location += srcName;
+    char lineno[20];
+    sprintf(lineno, ":%d", line);
+    location += lineno;
+
+    keep->add(rule, location);
+}
+
 status_t
 writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
 {
@@ -2193,7 +2415,7 @@ struct NamespaceAttributePair {
 
 status_t
 writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
-        const char* startTag, const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs)
+        const char* startTag, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs)
 {
     status_t err;
     ResXMLTree tree;
@@ -2237,28 +2459,48 @@ writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
         } else if (tagAttrPairs != NULL) {
             ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
             if (tagIndex >= 0) {
-                const NamespaceAttributePair& nsAttr = tagAttrPairs->valueAt(tagIndex);
-                ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
-                if (attrIndex < 0) {
-                    // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
-                    //        layoutFile->getPrintableSource().string(), tree.getLineNumber(),
-                    //        tag.string(), nsAttr.ns, nsAttr.attr);
-                } else {
-                    size_t len;
-                    addProguardKeepRule(keep,
-                                        String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
-                                        layoutFile->getPrintableSource(), tree.getLineNumber());
+                const Vector<NamespaceAttributePair>& nsAttrVector = tagAttrPairs->valueAt(tagIndex);
+                for (size_t i = 0; i < nsAttrVector.size(); i++) {
+                    const NamespaceAttributePair& nsAttr = nsAttrVector[i];
+
+                    ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
+                    if (attrIndex < 0) {
+                        // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
+                        //        layoutFile->getPrintableSource().string(), tree.getLineNumber(),
+                        //        tag.string(), nsAttr.ns, nsAttr.attr);
+                    } else {
+                        size_t len;
+                        addProguardKeepRule(keep,
+                                            String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
+                                            layoutFile->getPrintableSource(), tree.getLineNumber());
+                    }
                 }
             }
         }
+        ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick");
+        if (attrIndex >= 0) {
+            size_t len;
+            addProguardKeepMethodRule(keep,
+                                String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
+                                layoutFile->getPrintableSource(), tree.getLineNumber());
+        }
     }
 
     return NO_ERROR;
 }
 
-static void addTagAttrPair(KeyedVector<String8, NamespaceAttributePair>* dest,
+static void addTagAttrPair(KeyedVector<String8, Vector<NamespaceAttributePair> >* dest,
         const char* tag, const char* ns, const char* attr) {
-    dest->add(String8(tag), NamespaceAttributePair(ns, attr));
+    String8 tagStr(tag);
+    ssize_t index = dest->indexOfKey(tagStr);
+
+    if (index < 0) {
+        Vector<NamespaceAttributePair> vector;
+        vector.add(NamespaceAttributePair(ns, attr));
+        dest->add(tagStr, vector);
+    } else {
+        dest->editValueAt(index).add(NamespaceAttributePair(ns, attr));
+    }
 }
 
 status_t
@@ -2267,13 +2509,13 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
     status_t err;
 
     // tag:attribute pairs that should be checked in layout files.
-    KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
+    KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs;
     addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
 
     // tag:attribute pairs that should be checked in xml files.
-    KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
+    KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs;
     addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
     addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
 
@@ -2283,12 +2525,15 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
         const sp<AaptDir>& d = dirs.itemAt(k);
         const String8& dirName = d->getLeaf();
         const char* startTag = NULL;
-        const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs = 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";
             tagAttrPairs = &kXmlTagAttrPairs;
+        } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
+            startTag = "menu";
+            tagAttrPairs = NULL;
         } else {
             continue;
         }
@@ -2312,6 +2557,7 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
     if (overlay.get()) {
         return writeProguardForLayouts(keep, overlay);
     }
+
     return NO_ERROR;
 }