X-Git-Url: https://git.saurik.com/android/aapt.git/blobdiff_plain/4bb85b9ff958cc4bcd44f1a0c15cfda652109b80..d4ee3b303621db0c85465c6bee031fe11be66313:/Resource.cpp diff --git a/Resource.cpp b/Resource.cpp index ae4bdc8..e4f8d81 100644 --- a/Resource.cpp +++ b/Resource.cpp @@ -10,8 +10,25 @@ #include "ResourceTable.h" #include "Images.h" +#include "CrunchCache.h" +#include "FileFinder.h" +#include "CacheUpdater.h" + +#include + +#if HAVE_PRINTF_ZD +# define ZD "%zd" +# define ZD_TYPE ssize_t +#else +# define ZD "%ld" +# define ZD_TYPE long +#endif + #define NOISY(x) // x +// Number of threads to use for preprocessing images. +static const size_t MAX_THREADS = 4; + // ========================================================================== // ========================================================================== // ========================================================================== @@ -290,21 +307,53 @@ static status_t makeFileResources(Bundle* bundle, const sp& assets, return hasErrors ? UNKNOWN_ERROR : NO_ERROR; } -static status_t preProcessImages(Bundle* bundle, const sp& assets, +class PreProcessImageWorkUnit : public WorkQueue::WorkUnit { +public: + PreProcessImageWorkUnit(const Bundle* bundle, const sp& assets, + const sp& file, volatile bool* hasErrors) : + mBundle(bundle), mAssets(assets), mFile(file), mHasErrors(hasErrors) { + } + + virtual bool run() { + status_t status = preProcessImage(mBundle, mAssets, mFile, NULL); + if (status) { + *mHasErrors = true; + } + return true; // continue even if there are errors + } + +private: + const Bundle* mBundle; + sp mAssets; + sp mFile; + volatile bool* mHasErrors; +}; + +static status_t preProcessImages(const Bundle* bundle, const sp& assets, const sp& set, const char* type) { - ResourceDirIterator it(set, String8(type)); - Vector > newNameFiles; - Vector newNamePaths; - bool hasErrors = false; - ssize_t res; - while ((res=it.next()) == NO_ERROR) { - res = preProcessImage(bundle, assets, it.getFile(), NULL); - if (res < NO_ERROR) { + volatile bool hasErrors = false; + ssize_t res = NO_ERROR; + if (bundle->getUseCrunchCache() == false) { + WorkQueue wq(MAX_THREADS, false); + ResourceDirIterator it(set, String8(type)); + while ((res=it.next()) == NO_ERROR) { + PreProcessImageWorkUnit* w = new PreProcessImageWorkUnit( + bundle, assets, it.getFile(), &hasErrors); + status_t status = wq.schedule(w); + if (status) { + fprintf(stderr, "preProcessImages failed: schedule() returned %d\n", status); + hasErrors = true; + delete w; + break; + } + } + status_t status = wq.finish(); + if (status) { + fprintf(stderr, "preProcessImages failed: finish() returned %d\n", status); hasErrors = true; } } - return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; } @@ -347,18 +396,27 @@ static void collect_files(const sp& dir, if (index < 0) { sp set = new ResourceTypeSet(); + NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n", + leafName.string(), group->getPath().string(), group.get())); set->add(leafName, group); resources->add(resType, set); } else { sp set = resources->valueAt(index); index = set->indexOfKey(leafName); if (index < 0) { + NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n", + leafName.string(), group->getPath().string(), group.get())); set->add(leafName, group); } else { sp existingGroup = set->valueAt(index); - int M = files.size(); - for (int j=0; jaddFile(files.valueAt(j)); + NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n", + leafName.string(), group->getPath().string(), group.get())); + for (size_t j=0; jgetSourceFile().string(), + files.keyAt(j).toDirName(String8()).string(), + resType.string())); + status_t err = existingGroup->addFile(files.valueAt(j)); } } } @@ -373,9 +431,12 @@ static void collect_files(const sp& ass, for (int i=0; i d = dirs.itemAt(i); + NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(), + d->getLeaf().string())); collect_files(d, resources); // don't try to include the res dir + NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string())); ass->removeDir(d->getLeaf()); } } @@ -549,11 +610,11 @@ static bool applyFileOverlay(Bundle *bundle, DefaultKeyedVector > baseFiles = baseGroup->getFiles(); for (size_t i=0; i < baseFiles.size(); i++) { - printf("baseFile %zd has flavor %s\n", i, + printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i, baseFiles.keyAt(i).toString().string()); } for (size_t i=0; i < overlayFiles.size(); i++) { - printf("overlayFile %zd has flavor %s\n", i, + printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i, overlayFiles.keyAt(i).toString().string()); } } @@ -565,16 +626,21 @@ static bool applyFileOverlay(Bundle *bundle, size_t baseFileIndex = baseGroup->getFiles().indexOfKey(overlayFiles. keyAt(overlayGroupIndex)); - if(baseFileIndex < UNKNOWN_ERROR) { + if (baseFileIndex < UNKNOWN_ERROR) { if (bundle->getVerbose()) { - printf("found a match (%zd) for overlay file %s, for flavor %s\n", - baseFileIndex, + printf("found a match (" ZD ") for overlay file %s, for flavor %s\n", + (ZD_TYPE) baseFileIndex, overlayGroup->getLeaf().string(), overlayFiles.keyAt(overlayGroupIndex).toString().string()); } baseGroup->removeFile(baseFileIndex); } else { // didn't find a match fall through and add it.. + if (true || bundle->getVerbose()) { + printf("nothing matches overlay file %s, for flavor %s\n", + overlayGroup->getLeaf().string(), + overlayFiles.keyAt(overlayGroupIndex).toString().string()); + } } baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex)); assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex)); @@ -754,6 +820,35 @@ status_t massageManifest(Bundle* bundle, sp root) } \ } while (0) +status_t updatePreProcessedCache(Bundle* bundle) +{ + #if BENCHMARK + fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n"); + long startPNGTime = clock(); + #endif /* BENCHMARK */ + + String8 source(bundle->getResourceSourceDirs()[0]); + String8 dest(bundle->getCrunchedOutputDir()); + + FileFinder* ff = new SystemFileFinder(); + CrunchCache cc(source,dest,ff); + + CacheUpdater* cu = new SystemCacheUpdater(bundle); + size_t numFiles = cc.crunch(cu); + + if (bundle->getVerbose()) + fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles); + + delete ff; + delete cu; + + #if BENCHMARK + fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n" + ,(clock() - startPNGTime)/1000.0); + #endif /* BENCHMARK */ + return 0; +} + status_t buildResources(Bundle* bundle, const sp& assets) { // First, look for a package file to parse. This is required to @@ -788,8 +883,7 @@ status_t buildResources(Bundle* bundle, const sp& assets) * request UTF-16 encoding and the parameters of this package * allow UTF-8 to be used. */ - if (!bundle->getWantUTF16() - && bundle->isMinSdkAtLeast(SDK_FROYO)) { + if (!bundle->getUTF16StringsOption()) { xmlFlags |= XML_COMPILE_UTF8; } @@ -1727,6 +1821,110 @@ static status_t writeLayoutClasses( return hasErrors ? UNKNOWN_ERROR : NO_ERROR; } +static status_t writeTextLayoutClasses( + FILE* fp, const sp& assets, + const sp& 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 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 idents; + Vector origOrder; + Vector publicFlags; + + size_t a; + size_t NA = nsymbols->getSymbols().size(); + for (a=0; agetSymbols().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 %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= 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& assets, bool includePrivate, const sp& symbols, const String8& className, int indent, @@ -1750,11 +1948,10 @@ static status_t writeSymbolClass( if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { continue; } - if (!includePrivate && !sym.isPublic) { + if (!assets->isJavaSymbol(sym, includePrivate)) { continue; } String16 name(sym.name); - String8 realName(name); if (fixupSymbol(&name) != NO_ERROR) { return UNKNOWN_ERROR; } @@ -1806,7 +2003,7 @@ static status_t writeSymbolClass( if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) { continue; } - if (!includePrivate && !sym.isPublic) { + if (!assets->isJavaSymbol(sym, includePrivate)) { continue; } String16 name(sym.name); @@ -1866,6 +2063,51 @@ static status_t writeSymbolClass( return NO_ERROR; } +static status_t writeTextSymbolClass( + FILE* fp, const sp& assets, bool includePrivate, + const sp& symbols, const String8& className) +{ + size_t i; + status_t err = NO_ERROR; + + size_t N = symbols->getSymbols().size(); + for (i=0; igetSymbols().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 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& assets, const String8& package, bool includePrivate) { @@ -1873,11 +2115,15 @@ status_t writeResourceSymbols(Bundle* bundle, const sp& 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 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(); @@ -1909,27 +2155,53 @@ status_t writeResourceSymbols(Bundle* bundle, const sp& 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()); - - status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0, bundle->getNonConstantId()); + "/* 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()); if (err != NO_ERROR) { return err; } fclose(fp); - if (bundle->getGenDependencies()) { + 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() && R == className) { // Add this R.java to the dependency file String8 dependencyFile(bundle->getRClassDir()); - dependencyFile.appendPath("R.d"); + dependencyFile.appendPath("R.java.d"); - fp = fopen(dependencyFile.string(), "a"); + FILE *fp = fopen(dependencyFile.string(), "a"); fprintf(fp,"%s \\\n", dest.string()); fclose(fp); } @@ -1939,7 +2211,6 @@ status_t writeResourceSymbols(Bundle* bundle, const sp& assets, } - class ProguardKeepSet { public: @@ -1992,6 +2263,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& assets) { @@ -2096,7 +2384,7 @@ struct NamespaceAttributePair { status_t writeProguardForXml(ProguardKeepSet* keep, const sp& layoutFile, - const char* startTag, const KeyedVector* tagAttrPairs) + const char* startTag, const KeyedVector >* tagAttrPairs) { status_t err; ResXMLTree tree; @@ -2140,28 +2428,48 @@ writeProguardForXml(ProguardKeepSet* keep, const sp& 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& 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* dest, +static void addTagAttrPair(KeyedVector >* 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 vector; + vector.add(NamespaceAttributePair(ns, attr)); + dest->add(tagStr, vector); + } else { + dest->editValueAt(index).add(NamespaceAttributePair(ns, attr)); + } } status_t @@ -2170,13 +2478,13 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp& assets) status_t err; // tag:attribute pairs that should be checked in layout files. - KeyedVector kLayoutTagAttrPairs; + KeyedVector > 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 kXmlTagAttrPairs; + KeyedVector > kXmlTagAttrPairs; addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment"); addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment"); @@ -2186,12 +2494,15 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp& assets) const sp& d = dirs.itemAt(k); const String8& dirName = d->getLeaf(); const char* startTag = NULL; - const KeyedVector* tagAttrPairs = NULL; + const KeyedVector >* 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; } @@ -2210,6 +2521,12 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp& assets) } } } + // Handle the overlays + sp overlay = assets->getOverlay(); + if (overlay.get()) { + return writeProguardForLayouts(keep, overlay); + } + return NO_ERROR; } @@ -2256,11 +2573,11 @@ writeProguardFile(Bundle* bundle, const sp& assets) return err; } -status_t -writeDependencyPreReqs(Bundle* bundle, const sp& assets, FILE* fp) +// Loops through the string paths and writes them to the file pointer +// Each file path is written on its own line with a terminating backslash. +status_t writePathsToFile(const sp& files, FILE* fp) { status_t deps = -1; - sp files = assets->getFullResPaths(); for (size_t file_i = 0; file_i < files->size(); ++file_i) { // Add the full file path to the dependency file fprintf(fp, "%s \\\n", files->itemAt(file_i).string()); @@ -2268,3 +2585,14 @@ writeDependencyPreReqs(Bundle* bundle, const sp& assets, FILE* fp) } return deps; } + +status_t +writeDependencyPreReqs(Bundle* bundle, const sp& assets, FILE* fp, bool includeRaw) +{ + status_t deps = -1; + deps += writePathsToFile(assets->getFullResPaths(), fp); + if (includeRaw) { + deps += writePathsToFile(assets->getFullAssetPaths(), fp); + } + return deps; +}