#include "ResourceTable.h"
#include "Images.h"
+#include "CrunchCache.h"
+#include "FileFinder.h"
+#include "CacheUpdater.h"
+
+#include <utils/WorkQueue.h>
+
+#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;
+
// ==========================================================================
// ==========================================================================
// ==========================================================================
{
}
+FilePathStore::FilePathStore()
+ :RefBase(),
+ Vector<String8>()
+{
+}
+
class ResourceDirIterator
{
public:
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<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
}
-static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
- const sp<ResourceTypeSet>& set)
+class PreProcessImageWorkUnit : public WorkQueue::WorkUnit {
+public:
+ PreProcessImageWorkUnit(const Bundle* bundle, const sp<AaptAssets>& assets,
+ const sp<AaptFile>& 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<AaptAssets> mAssets;
+ sp<AaptFile> mFile;
+ volatile bool* mHasErrors;
+};
+
+static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& assets,
+ const sp<ResourceTypeSet>& set, const char* type)
{
- ResourceDirIterator it(set, String8("drawable"));
- Vector<sp<AaptFile> > newNameFiles;
- Vector<String8> 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;
}
if (index < 0) {
sp<ResourceTypeSet> 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<ResourceTypeSet> 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<AaptGroup> existingGroup = set->valueAt(index);
- int M = files.size();
- for (int j=0; j<M; j++) {
- existingGroup->addFile(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; j<files.size(); j++) {
+ NOISY(printf("Adding file %s in group %s resType %s\n",
+ files.valueAt(j)->getSourceFile().string(),
+ files.keyAt(j).toDirName(String8()).string(),
+ resType.string()));
+ status_t err = existingGroup->addFile(files.valueAt(j));
}
}
}
for (int i=0; i<N; i++) {
sp<AaptDir> 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());
}
}
DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > 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());
}
}
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));
bundle->getMaxSdkVersion());
}
+ if (bundle->getDebugMode()) {
+ sp<XMLNode> 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) {
} \
} 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<AaptAssets>& assets)
{
// First, look for a package file to parse. This is required to
* 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;
}
sp<ResourceTypeSet> drawables;
sp<ResourceTypeSet> layouts;
sp<ResourceTypeSet> anims;
+ sp<ResourceTypeSet> animators;
+ sp<ResourceTypeSet> interpolators;
sp<ResourceTypeSet> xmls;
sp<ResourceTypeSet> raws;
sp<ResourceTypeSet> colors;
sp<ResourceTypeSet> menus;
+ sp<ResourceTypeSet> 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
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) {
}
}
+ 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) {
}
}
+ 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) {
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) {
static status_t writeSymbolClass(
FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
- const sp<AaptSymbols>& symbols, const String8& className, int indent)
+ const sp<AaptSymbols>& symbols, const String8& className, int indent,
+ bool nonConstantId)
{
fprintf(fp, "%spublic %sfinal class %s {\n",
getIndentSpace(indent),
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; i<N; i++) {
const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
continue;
}
- if (!includePrivate && !sym.isPublic) {
+ if (!assets->isJavaSymbol(sym, includePrivate)) {
continue;
}
String16 name(sym.name);
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);
}
if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) {
continue;
}
- if (!includePrivate && !sym.isPublic) {
+ if (!assets->isJavaSymbol(sym, includePrivate)) {
continue;
}
String16 name(sym.name);
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;
"\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;
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)
{
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;
} 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
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");
+ addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
const Vector<sp<AaptDir> >& dirs = assets->resDirs();
const size_t K = dirs.size();
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;
}
}
}
}
+ // Handle the overlays
+ sp<AaptAssets> overlay = assets->getOverlay();
+ if (overlay.get()) {
+ return writeProguardForLayouts(keep, overlay);
+ }
+
return NO_ERROR;
}
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<FilePathStore>& 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<AaptAssets>& assets, FILE* fp, bool includeRaw)
+{
+ status_t deps = -1;
+ deps += writePathsToFile(assets->getFullResPaths(), fp);
+ if (includeRaw) {
+ deps += writePathsToFile(assets->getFullAssetPaths(), fp);
+ }
+ return deps;
+}