X-Git-Url: https://git.saurik.com/android/aapt.git/blobdiff_plain/26bee7e6258deed92e270ab95d7de54cc55edb85..d35357226f49361bd6125654e5cff0bd5cab7556:/Resource.cpp diff --git a/Resource.cpp b/Resource.cpp index 5855b56..b9ec30c 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; + // ========================================================================== // ========================================================================== // ========================================================================== @@ -51,6 +68,12 @@ ResourceTypeSet::ResourceTypeSet() { } +FilePathStore::FilePathStore() + :RefBase(), + Vector() +{ +} + class ResourceDirIterator { public: @@ -148,9 +171,10 @@ private: bool isValidResourceType(const String8& type) { - return type == "anim" || type == "drawable" || type == "layout" + return type == "anim" || type == "animator" || type == "interpolator" + || type == "drawable" || type == "layout" || type == "values" || type == "xml" || type == "raw" - || type == "color" || type == "menu"; + || type == "color" || type == "menu" || type == "mipmap"; } static sp getResourceFile(const sp& assets, bool makeIfNecessary=true) @@ -283,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, - const sp& set) +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("drawable")); - 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; } @@ -340,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)); } } } @@ -366,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()); } } @@ -542,11 +610,11 @@ static bool applyFileOverlay(Bundle *bundle, DefaultKeyedVector > baseFiles = baseGroup->getFiles(); for (size_t i=0; i < baseFiles.size(); i++) { - printf("baseFile %d 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 %d has flavor %s\n", i, + printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i, overlayFiles.keyAt(i).toString().string()); } } @@ -558,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 (%d) 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)); @@ -678,6 +751,13 @@ status_t massageManifest(Bundle* bundle, sp root) bundle->getMaxSdkVersion()); } + if (bundle->getDebugMode()) { + sp application = root->getChildElement(String16(), String16("application")); + if (application != NULL) { + addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true"); + } + } + // Deal with manifest package name overrides const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); if (manifestPackageNameOverride != NULL) { @@ -740,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 @@ -774,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; } @@ -791,18 +899,24 @@ status_t buildResources(Bundle* bundle, const sp& assets) sp drawables; sp layouts; sp anims; + sp animators; + sp interpolators; sp xmls; sp raws; sp colors; sp menus; + sp mipmaps; ASSIGN_IT(drawable); ASSIGN_IT(layout); ASSIGN_IT(anim); + ASSIGN_IT(animator); + ASSIGN_IT(interpolator); ASSIGN_IT(xml); ASSIGN_IT(raw); ASSIGN_IT(color); ASSIGN_IT(menu); + ASSIGN_IT(mipmap); assets->setResources(resources); // now go through any resource overlays and collect their files @@ -818,17 +932,22 @@ status_t buildResources(Bundle* bundle, const sp& assets) if (!applyFileOverlay(bundle, assets, &drawables, "drawable") || !applyFileOverlay(bundle, assets, &layouts, "layout") || !applyFileOverlay(bundle, assets, &anims, "anim") || + !applyFileOverlay(bundle, assets, &animators, "animator") || + !applyFileOverlay(bundle, assets, &interpolators, "interpolator") || !applyFileOverlay(bundle, assets, &xmls, "xml") || !applyFileOverlay(bundle, assets, &raws, "raw") || !applyFileOverlay(bundle, assets, &colors, "color") || - !applyFileOverlay(bundle, assets, &menus, "menu")) { + !applyFileOverlay(bundle, assets, &menus, "menu") || + !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) { return UNKNOWN_ERROR; } bool hasErrors = false; if (drawables != NULL) { - err = preProcessImages(bundle, assets, drawables); + if (bundle->getOutputAPKFile() != NULL) { + err = preProcessImages(bundle, assets, drawables, "drawable"); + } if (err == NO_ERROR) { err = makeFileResources(bundle, assets, &table, drawables, "drawable"); if (err != NO_ERROR) { @@ -839,6 +958,20 @@ status_t buildResources(Bundle* bundle, const sp& assets) } } + if (mipmaps != NULL) { + if (bundle->getOutputAPKFile() != NULL) { + err = preProcessImages(bundle, assets, mipmaps, "mipmap"); + } + if (err == NO_ERROR) { + err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap"); + if (err != NO_ERROR) { + hasErrors = true; + } + } else { + hasErrors = true; + } + } + if (layouts != NULL) { err = makeFileResources(bundle, assets, &table, layouts, "layout"); if (err != NO_ERROR) { @@ -853,6 +986,20 @@ status_t buildResources(Bundle* bundle, const sp& assets) } } + if (animators != NULL) { + err = makeFileResources(bundle, assets, &table, animators, "animator"); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (interpolators != NULL) { + err = makeFileResources(bundle, assets, &table, interpolators, "interpolator"); + if (err != NO_ERROR) { + hasErrors = true; + } + } + if (xmls != NULL) { err = makeFileResources(bundle, assets, &table, xmls, "xml"); if (err != NO_ERROR) { @@ -960,6 +1107,36 @@ status_t buildResources(Bundle* bundle, const sp& assets) err = NO_ERROR; } + if (animators != NULL) { + ResourceDirIterator it(animators, String8("animator")); + while ((err=it.next()) == NO_ERROR) { + err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (err < NO_ERROR) { + hasErrors = true; + } + err = NO_ERROR; + } + + if (interpolators != NULL) { + ResourceDirIterator it(interpolators, String8("interpolator")); + while ((err=it.next()) == NO_ERROR) { + err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (err < NO_ERROR) { + hasErrors = true; + } + err = NO_ERROR; + } + if (xmls != NULL) { ResourceDirIterator it(xmls, String8("xml")); while ((err=it.next()) == NO_ERROR) { @@ -1646,7 +1823,8 @@ static status_t writeLayoutClasses( static status_t writeSymbolClass( FILE* fp, const sp& assets, bool includePrivate, - const sp& symbols, const String8& className, int indent) + const sp& symbols, const String8& className, int indent, + bool nonConstantId) { fprintf(fp, "%spublic %sfinal class %s {\n", getIndentSpace(indent), @@ -1656,13 +1834,17 @@ static status_t writeSymbolClass( size_t i; status_t err = NO_ERROR; + const char * id_format = nonConstantId ? + "%spublic static int %s=0x%08x;\n" : + "%spublic static final int %s=0x%08x;\n"; + size_t N = symbols->getSymbols().size(); for (i=0; igetSymbols().valueAt(i); if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { continue; } - if (!includePrivate && !sym.isPublic) { + if (!assets->isJavaSymbol(sym, includePrivate)) { continue; } String16 name(sym.name); @@ -1708,7 +1890,7 @@ static status_t writeSymbolClass( if (deprecated) { fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent)); } - fprintf(fp, "%spublic static final int %s=0x%08x;\n", + fprintf(fp, id_format, getIndentSpace(indent), String8(name).string(), (int)sym.int32Val); } @@ -1718,7 +1900,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); @@ -1759,7 +1941,7 @@ static status_t writeSymbolClass( if (nclassName == "styleable") { styleableSymbols = nsymbols; } else { - err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent); + err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId); } if (err != NO_ERROR) { return err; @@ -1830,11 +2012,24 @@ status_t writeResourceSymbols(Bundle* bundle, const sp& assets, "\n" "package %s;\n\n", package.string()); - status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0); + status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, + className, 0, bundle->getNonConstantId()); 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()) { + // Add this R.java to the dependency file + String8 dependencyFile(bundle->getRClassDir()); + dependencyFile.appendPath("R.java.d"); + + fp = fopen(dependencyFile.string(), "a"); + fprintf(fp,"%s \\\n", dest.string()); + fclose(fp); + } } return NO_ERROR; @@ -1880,7 +2075,7 @@ addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName, className.append(inClassName); } } - + String8 rule("-keep class "); rule += className; rule += " { (...); }"; @@ -1955,7 +2150,7 @@ writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp& ass if (tag == "application") { inApplication = true; keepTag = true; - + String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android", "backupAgent", &error); if (agent.length() > 0) { @@ -1988,9 +2183,17 @@ writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp& ass return NO_ERROR; } +struct NamespaceAttributePair { + const char* ns; + const char* attr; + + NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {} + NamespaceAttributePair() : ns(NULL), attr(NULL) {} +}; + status_t writeProguardForXml(ProguardKeepSet* keep, const sp& layoutFile, - const char* startTag, const char* altTag) + const char* startTag, const KeyedVector* tagAttrPairs) { status_t err; ResXMLTree tree; @@ -2020,7 +2223,7 @@ writeProguardForXml(ProguardKeepSet* keep, const sp& layoutFile, return NO_ERROR; } } - + while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { if (code != ResXMLTree::START_TAG) { continue; @@ -2031,16 +2234,21 @@ writeProguardForXml(ProguardKeepSet* keep, const sp& layoutFile, if (strchr(tag.string(), '.')) { addProguardKeepRule(keep, tag, NULL, layoutFile->getPrintableSource(), tree.getLineNumber()); - } else if (altTag != NULL && tag == altTag) { - ssize_t classIndex = tree.indexOfAttribute(NULL, "class"); - if (classIndex < 0) { - fprintf(stderr, "%s:%d: does not have class attribute.\n", - layoutFile->getPrintableSource().string(), tree.getLineNumber()); - } else { - size_t len; - addProguardKeepRule(keep, - String8(tree.getAttributeStringValue(classIndex, &len)), NULL, - layoutFile->getPrintableSource(), tree.getLineNumber()); + } 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()); + } } } } @@ -2048,25 +2256,43 @@ writeProguardForXml(ProguardKeepSet* keep, const sp& layoutFile, return NO_ERROR; } +static void addTagAttrPair(KeyedVector* dest, + const char* tag, const char* ns, const char* attr) { + dest->add(String8(tag), NamespaceAttributePair(ns, attr)); +} + status_t writeProguardForLayouts(ProguardKeepSet* keep, const sp& assets) { status_t err; + + // tag:attribute pairs that should be checked in layout files. + 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; + addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment"); + addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment"); + const Vector >& dirs = assets->resDirs(); const size_t K = dirs.size(); for (size_t k=0; k& d = dirs.itemAt(k); const String8& dirName = d->getLeaf(); const char* startTag = NULL; - const char* altTag = NULL; + const KeyedVector* tagAttrPairs = NULL; if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { - altTag = "view"; + tagAttrPairs = &kLayoutTagAttrPairs; } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) { startTag = "PreferenceScreen"; + tagAttrPairs = &kXmlTagAttrPairs; } else { continue; } - + const KeyedVector > groups = d->getFiles(); const size_t N = groups.size(); for (size_t i=0; i& assets) const DefaultKeyedVector >& files = group->getFiles(); const size_t M = files.size(); for (size_t j=0; j overlay = assets->getOverlay(); + if (overlay.get()) { + return writeProguardForLayouts(keep, overlay); + } return NO_ERROR; } @@ -2126,3 +2357,27 @@ writeProguardFile(Bundle* bundle, const sp& assets) return err; } + +// 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; + 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()); + deps++; + } + 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; +}