Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58ffaf90 authored by Adam Lesinski's avatar Adam Lesinski Committed by Android (Google) Code Review
Browse files

Merge "Properly mangle file names"

parents f7d3a766 d5c4f872
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -603,6 +603,13 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na
                    mTable->getValueStringPool().makeRef(
                            styleStr, StringPool::Context{1, config}));
        } else {
            if (name.type != ResourceType::kString &&
                    util::stringStartsWith<char16_t>(str, u"res/")) {
                // This must be a FileReference.
                return util::make_unique<FileReference>(mTable->getValueStringPool().makeRef(
                            str, StringPool::Context{ 0, config }));
            }

            // There are no styles associated with this string, so treat it as
            // a simple string.
            return util::make_unique<String>(
+201 −141
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include "Linker.h"
#include "ManifestParser.h"
#include "ManifestValidator.h"
#include "NameMangler.h"
#include "Png.h"
#include "ResourceParser.h"
#include "ResourceTable.h"
@@ -266,22 +267,50 @@ struct CompileItem {

struct LinkItem {
    Source source;
    std::string apkPath;
    ResourceName name;
    ConfigDescription config;
    std::string originalPath;
    ZipFile* apk;
};

std::string buildFileReference(const CompileItem& item) {
template <typename TChar>
static BasicStringPiece<TChar> getExtension(const BasicStringPiece<TChar>& str) {
    auto iter = std::find(str.begin(), str.end(), static_cast<TChar>('.'));
    if (iter == str.end()) {
        return BasicStringPiece<TChar>();
    }
    size_t offset = (iter - str.begin()) + 1;
    return str.substr(offset, str.size() - offset);
}



std::string buildFileReference(const ResourceNameRef& name, const ConfigDescription& config,
                               const StringPiece& extension) {
    std::stringstream path;
    path << "res/" << item.name.type;
    if (item.config != ConfigDescription{}) {
        path << "-" << item.config;
    path << "res/" << name.type;
    if (config != ConfigDescription{}) {
        path << "-" << config;
    }
    path << "/" << util::utf16ToUtf8(name.entry);
    if (!extension.empty()) {
        path << "." << extension;
    }
    path << "/" << util::utf16ToUtf8(item.name.entry) + "." + item.extension;
    return path.str();
}

std::string buildFileReference(const CompileItem& item) {
    return buildFileReference(item.name, item.config, item.extension);
}

std::string buildFileReference(const LinkItem& item) {
    return buildFileReference(item.name, item.config, getExtension<char>(item.originalPath));
}

bool addFileReference(const std::shared_ptr<ResourceTable>& table, const CompileItem& item) {
    StringPool& pool = table->getValueStringPool();
    StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item)));
    StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item)),
                                       StringPool::Context{ 0, item.config });
    return table->addResource(item.name, item.config, item.source.line(0),
                              util::make_unique<FileReference>(ref));
}
@@ -418,8 +447,8 @@ bool linkXml(const AaptOptions& options, const std::shared_ptr<Resolver>& resolv
        return false;
    }

    if (outApk->add(outBuffer, item.apkPath.data(), ZipEntry::kCompressDeflated, nullptr) !=
            android::NO_ERROR) {
    if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressDeflated,
                nullptr) != android::NO_ERROR) {
        Logger::error(options.output) << "failed to write linked file '" << item.source
                                      << "' to apk." << std::endl;
        return false;
@@ -502,109 +531,6 @@ bool compileManifest(const AaptOptions& options, const std::shared_ptr<Resolver>
    return true;
}

bool loadAppInfo(const Source& source, AppInfo* outInfo) {
    std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
    if (!ifs) {
        Logger::error(source) << strerror(errno) << std::endl;
        return false;
    }

    ManifestParser parser;
    std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs);
    return parser.parse(source, pullParser, outInfo);
}

static void printCommandsAndDie() {
    std::cerr << "The following commands are supported:" << std::endl << std::endl;
    std::cerr << "compile       compiles a subset of resources" << std::endl;
    std::cerr << "link          links together compiled resources and libraries" << std::endl;
    std::cerr << std::endl;
    std::cerr << "run aapt2 with one of the commands and the -h flag for extra details."
              << std::endl;
    exit(1);
}

static AaptOptions prepareArgs(int argc, char** argv) {
    if (argc < 2) {
        std::cerr << "no command specified." << std::endl << std::endl;
        printCommandsAndDie();
    }

    const StringPiece command(argv[1]);
    argc -= 2;
    argv += 2;

    AaptOptions options;

    if (command == "--version" || command == "version") {
        std::cout << kAaptVersionStr << std::endl;
        exit(0);
    } else if (command == "link") {
        options.phase = AaptOptions::Phase::Link;
    } else if (command == "compile") {
        options.phase = AaptOptions::Phase::Compile;
    } else {
        std::cerr << "invalid command '" << command << "'." << std::endl << std::endl;
        printCommandsAndDie();
    }

    if (options.phase == AaptOptions::Phase::Compile) {
        flag::requiredFlag("--package", "Android package name",
                [&options](const StringPiece& arg) {
                    options.appInfo.package = util::utf8ToUtf16(arg);
                });
        flag::optionalFlag("--binding", "Output directory for binding XML files",
                [&options](const StringPiece& arg) {
                    options.bindingOutput = Source{ arg.toString() };
                });
        flag::optionalSwitch("--no-version", "Disables automatic style and layout versioning",
                             false, &options.versionStylesAndLayouts);

    } else if (options.phase == AaptOptions::Phase::Link) {
        flag::requiredFlag("--manifest", "AndroidManifest.xml of your app",
                [&options](const StringPiece& arg) {
                    options.manifest = Source{ arg.toString() };
                });

        flag::optionalFlag("-I", "add an Android APK to link against",
                [&options](const StringPiece& arg) {
                    options.libraries.push_back(Source{ arg.toString() });
                });

        flag::optionalFlag("--java", "directory in which to generate R.java",
                [&options](const StringPiece& arg) {
                    options.generateJavaClass = Source{ arg.toString() };
                });
    }

    // Common flags for all steps.
    flag::requiredFlag("-o", "Output path", [&options](const StringPiece& arg) {
        options.output = Source{ arg.toString() };
    });

    bool help = false;
    flag::optionalSwitch("-v", "enables verbose logging", true, &options.verbose);
    flag::optionalSwitch("-h", "displays this help menu", true, &help);

    // Build the command string for output (eg. "aapt2 compile").
    std::string fullCommand = "aapt2";
    fullCommand += " ";
    fullCommand += command.toString();

    // Actually read the command line flags.
    flag::parse(argc, argv, fullCommand);

    if (help) {
        flag::usageAndDie(fullCommand);
    }

    // Copy all the remaining arguments.
    for (const std::string& arg : flag::getArgs()) {
        options.input.push_back(Source{ arg });
    }
    return options;
}

static bool compileValues(const std::shared_ptr<ResourceTable>& table, const Source& source,
                          const ConfigDescription& config) {
    std::ifstream in(source.path, std::ifstream::binary);
@@ -630,6 +556,7 @@ struct ResourcePathData {
 * [--/res/]type[-config]/name
 */
static Maybe<ResourcePathData> extractResourcePathData(const Source& source) {
    // TODO(adamlesinski): Use Windows path separator on windows.
    std::vector<std::string> parts = util::splitAndLowercase(source.path, '/');
    if (parts.size() < 2) {
        Logger::error(source) << "bad resource path." << std::endl;
@@ -695,6 +622,38 @@ bool writeResourceTable(const AaptOptions& options, const std::shared_ptr<Resour
    return true;
}

/**
 * For each FileReference in the table, adds a LinkItem to the link queue for processing.
 */
static void addApkFilesToLinkQueue(const std::u16string& package, const Source& source,
                                   const std::shared_ptr<ResourceTable>& table,
                                   const std::unique_ptr<ZipFile>& apk,
                                   std::queue<LinkItem>* outLinkQueue) {
    bool mangle = package != table->getPackage();
    for (auto& type : *table) {
        for (auto& entry : type->entries) {
            ResourceName name = { package, type->type, entry->name };
            if (mangle) {
                NameMangler::mangle(table->getPackage(), &name.entry);
            }

            for (auto& value : entry->values) {
                visitFunc<FileReference>(*value.value, [&](FileReference& ref) {
                    std::string pathUtf8 = util::utf16ToUtf8(*ref.path);
                    outLinkQueue->push(LinkItem{
                            source, name, value.config, pathUtf8, apk.get() });
                    // Now rewrite the file path.
                    if (mangle) {
                        ref.path = table->getValueStringPool().makeRef(util::utf8ToUtf16(
                                    buildFileReference(name, value.config,
                                                       getExtension<char>(pathUtf8))));
                    }
                });
            }
        }
    }
}

static constexpr int kOpenFlags = ZipFile::kOpenCreate | ZipFile::kOpenTruncate |
        ZipFile::kOpenReadWrite;

@@ -740,9 +699,14 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
        linkedPackages.insert(table->getPackage());
    }

    std::queue<LinkItem> linkQueue;
    for (auto& p : apkFiles) {
        const std::shared_ptr<ResourceTable>& inTable = p.first;

        // Collect all FileReferences and add them to the queue for processing.
        addApkFilesToLinkQueue(options.appInfo.package, Source{}, inTable, p.second, &linkQueue);

        // Merge the tables.
        if (!outTable->merge(std::move(*inTable))) {
            return false;
        }
@@ -779,42 +743,35 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
        return false;
    }

    for (auto& p : apkFiles) {
        std::unique_ptr<ZipFile>& zipFile = p.second;

        // TODO(adamlesinski): Get list of files to read when processing config filter.
    for (; !linkQueue.empty(); linkQueue.pop()) {
        const LinkItem& item = linkQueue.front();

        const int numEntries = zipFile->getNumEntries();
        for (int i = 0; i < numEntries; i++) {
            ZipEntry* entry = zipFile->getEntryByIndex(i);
            assert(entry);

            StringPiece filename = entry->getFileName();
            if (!util::stringStartsWith<char>(filename, "res/")) {
                continue;
        ZipEntry* entry = item.apk->getEntryByName(item.originalPath.data());
        if (!entry) {
            Logger::error(item.source) << "failed to find '" << item.originalPath << "'."
                                       << std::endl;
            return false;
        }

            if (util::stringEndsWith<char>(filename, ".xml")) {
                void* uncompressedData = zipFile->uncompress(entry);
        if (util::stringEndsWith<char>(item.originalPath, ".xml")) {
            void* uncompressedData = item.apk->uncompress(entry);
            assert(uncompressedData);

                LinkItem item = { Source{ filename.toString() }, filename.toString() };

                if (!linkXml(options, resolver, item, uncompressedData,
                            entry->getUncompressedLen(), &outApk)) {
                    Logger::error(options.output) << "failed to link '" << filename << "'."
            if (!linkXml(options, resolver, item, uncompressedData, entry->getUncompressedLen(),
                    &outApk)) {
                Logger::error(options.output) << "failed to link '" << item.originalPath << "'."
                                              << std::endl;
                return false;
            }
        } else {
                if (outApk.add(zipFile.get(), entry, 0, nullptr) != android::NO_ERROR) {
                    Logger::error(options.output) << "failed to copy '" << filename << "'."
            if (outApk.add(item.apk, entry, buildFileReference(item).data(), 0, nullptr) !=
                    android::NO_ERROR) {
                Logger::error(options.output) << "failed to copy '" << item.originalPath << "'."
                                              << std::endl;
                return false;
            }
        }
    }
    }

    // Generate the Java class file.
    if (options.generateJavaClass) {
@@ -957,6 +914,109 @@ bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t
    return true;
}

bool loadAppInfo(const Source& source, AppInfo* outInfo) {
    std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
    if (!ifs) {
        Logger::error(source) << strerror(errno) << std::endl;
        return false;
    }

    ManifestParser parser;
    std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs);
    return parser.parse(source, pullParser, outInfo);
}

static void printCommandsAndDie() {
    std::cerr << "The following commands are supported:" << std::endl << std::endl;
    std::cerr << "compile       compiles a subset of resources" << std::endl;
    std::cerr << "link          links together compiled resources and libraries" << std::endl;
    std::cerr << std::endl;
    std::cerr << "run aapt2 with one of the commands and the -h flag for extra details."
              << std::endl;
    exit(1);
}

static AaptOptions prepareArgs(int argc, char** argv) {
    if (argc < 2) {
        std::cerr << "no command specified." << std::endl << std::endl;
        printCommandsAndDie();
    }

    const StringPiece command(argv[1]);
    argc -= 2;
    argv += 2;

    AaptOptions options;

    if (command == "--version" || command == "version") {
        std::cout << kAaptVersionStr << std::endl;
        exit(0);
    } else if (command == "link") {
        options.phase = AaptOptions::Phase::Link;
    } else if (command == "compile") {
        options.phase = AaptOptions::Phase::Compile;
    } else {
        std::cerr << "invalid command '" << command << "'." << std::endl << std::endl;
        printCommandsAndDie();
    }

    if (options.phase == AaptOptions::Phase::Compile) {
        flag::requiredFlag("--package", "Android package name",
                [&options](const StringPiece& arg) {
                    options.appInfo.package = util::utf8ToUtf16(arg);
                });
        flag::optionalFlag("--binding", "Output directory for binding XML files",
                [&options](const StringPiece& arg) {
                    options.bindingOutput = Source{ arg.toString() };
                });
        flag::optionalSwitch("--no-version", "Disables automatic style and layout versioning",
                             false, &options.versionStylesAndLayouts);

    } else if (options.phase == AaptOptions::Phase::Link) {
        flag::requiredFlag("--manifest", "AndroidManifest.xml of your app",
                [&options](const StringPiece& arg) {
                    options.manifest = Source{ arg.toString() };
                });

        flag::optionalFlag("-I", "add an Android APK to link against",
                [&options](const StringPiece& arg) {
                    options.libraries.push_back(Source{ arg.toString() });
                });

        flag::optionalFlag("--java", "directory in which to generate R.java",
                [&options](const StringPiece& arg) {
                    options.generateJavaClass = Source{ arg.toString() };
                });
    }

    // Common flags for all steps.
    flag::requiredFlag("-o", "Output path", [&options](const StringPiece& arg) {
        options.output = Source{ arg.toString() };
    });

    bool help = false;
    flag::optionalSwitch("-v", "enables verbose logging", true, &options.verbose);
    flag::optionalSwitch("-h", "displays this help menu", true, &help);

    // Build the command string for output (eg. "aapt2 compile").
    std::string fullCommand = "aapt2";
    fullCommand += " ";
    fullCommand += command.toString();

    // Actually read the command line flags.
    flag::parse(argc, argv, fullCommand);

    if (help) {
        flag::usageAndDie(fullCommand);
    }

    // Copy all the remaining arguments.
    for (const std::string& arg : flag::getArgs()) {
        options.input.push_back(Source{ arg });
    }
    return options;
}

int main(int argc, char** argv) {
    Logger::setLog(std::make_shared<Log>(std::cerr, std::cerr));
    AaptOptions options = prepareArgs(argc, argv);
+44 −28
Original line number Diff line number Diff line
@@ -43,8 +43,7 @@ struct FlatEntry {
 */
class MapFlattener : public ConstValueVisitor {
public:
    MapFlattener(BigBuffer* out, const FlatEntry& flatEntry,
                 std::vector<std::pair<ResourceNameRef, uint32_t>>& symbols) :
    MapFlattener(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols) :
            mOut(out), mSymbols(symbols) {
        mMap = mOut->nextBlock<android::ResTable_map_entry>();
        mMap->key.index = flatEntry.entryKey;
@@ -65,7 +64,7 @@ public:

    void flattenParent(const Reference& ref) {
        if (!ref.id.isValid()) {
            mSymbols.push_back({
            mSymbols->push_back({
                    ResourceNameRef(ref.name),
                    (mOut->size() - mMap->size) + sizeof(*mMap) - sizeof(android::ResTable_entry)
            });
@@ -80,7 +79,7 @@ public:

        // Write the key.
        if (!Res_INTERNALID(key.id.id) && !key.id.isValid()) {
            mSymbols.push_back(std::make_pair(ResourceNameRef(key.name),
            mSymbols->push_back(std::make_pair(ResourceNameRef(key.name),
                    mOut->size() - sizeof(*outMapEntry)));
        }
        outMapEntry->name.ident = key.id.id;
@@ -90,7 +89,7 @@ public:

        if (outMapEntry->value.data == 0x0) {
            visitFunc<Reference>(value, [&](const Reference& reference) {
                mSymbols.push_back(std::make_pair(ResourceNameRef(reference.name),
                mSymbols->push_back(std::make_pair(ResourceNameRef(reference.name),
                        mOut->size() - sizeof(outMapEntry->value.data)));
            });
        }
@@ -188,16 +187,47 @@ public:

private:
    BigBuffer* mOut;
    std::vector<std::pair<ResourceNameRef, uint32_t>>& mSymbols;
    SymbolEntryVector* mSymbols;
    android::ResTable_map_entry* mMap;
};

/**
 * Flattens a value, with special handling for References.
 */
struct ValueFlattener : ConstValueVisitor {
    ValueFlattener(BigBuffer* out, SymbolEntryVector* symbols) :
            result(false), mOut(out), mOutValue(nullptr), mSymbols(symbols) {
        mOutValue = mOut->nextBlock<android::Res_value>();
    }

    virtual void visit(const Reference& ref, ValueVisitorArgs& a) override {
        visitItem(ref, a);
        if (mOutValue->data == 0x0) {
            mSymbols->push_back({
                    ResourceNameRef(ref.name),
                    mOut->size() - sizeof(mOutValue->data)});
        }
    }

    virtual void visitItem(const Item& item, ValueVisitorArgs&) override {
        result = item.flatten(*mOutValue);
        mOutValue->size = sizeof(*mOutValue);
    }

    bool result;

private:
    BigBuffer* mOut;
    android::Res_value* mOutValue;
    SymbolEntryVector* mSymbols;
};

TableFlattener::TableFlattener(Options options)
: mOptions(options) {
}

bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry,
        std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries) {
                                  SymbolEntryVector* symbols) {
    if (flatEntry.value.isItem()) {
        android::ResTable_entry* entry = out->nextBlock<android::ResTable_entry>();

@@ -218,30 +248,16 @@ bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry,
            ResTable_entry_source* sourceBlock = out->nextBlock<ResTable_entry_source>();
            sourceBlock->pathIndex = flatEntry.sourcePathKey;
            sourceBlock->line = flatEntry.sourceLine;

            entry->size += sizeof(*sourceBlock);
        }

        android::Res_value* outValue = out->nextBlock<android::Res_value>();

        const Item& item = static_cast<const Item&>(flatEntry.value);
        if (!item.flatten(*outValue)) {
            return false;
        }

        if (outValue->data == 0x0) {
            visitFunc<Reference>(item, [&](const Reference& reference) {
                symbolEntries.push_back({
                        ResourceNameRef(reference.name),
                        out->size() - sizeof(outValue->data)
                });
            });
        }
        outValue->size = sizeof(*outValue);
        return true;
        const Item* item = static_cast<const Item*>(&flatEntry.value);
        ValueFlattener flattener(out, symbols);
        item->accept(flattener, {});
        return flattener.result;
    }

    MapFlattener flattener(out, flatEntry, symbolEntries);
    MapFlattener flattener(out, flatEntry, symbols);
    flatEntry.value.accept(flattener, {});
    return true;
}
@@ -263,7 +279,7 @@ bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) {
        return false;
    }

    std::vector<std::pair<ResourceNameRef, uint32_t>> symbolEntries;
    SymbolEntryVector symbolEntries;

    StringPool typePool;
    StringPool keyPool;
@@ -401,7 +417,7 @@ bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) {
            for (const FlatEntry& flatEntry : entry.second) {
                assert(flatEntry.entry.entryId < type->entries.size());
                indices[flatEntry.entry.entryId] = typeBlock.size() - entryStart;
                if (!flattenValue(&typeBlock, flatEntry, symbolEntries)) {
                if (!flattenValue(&typeBlock, flatEntry, &symbolEntries)) {
                    Logger::error()
                            << "failed to flatten resource '"
                            << ResourceNameRef {
+3 −2
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@

namespace aapt {

using SymbolEntryVector = std::vector<std::pair<ResourceNameRef, uint32_t>>;

struct FlatEntry;

/**
@@ -49,8 +51,7 @@ struct TableFlattener {
    bool flatten(BigBuffer* out, const ResourceTable& table);

private:
    bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry,
                      std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries);
    bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols);

    Options mOptions;
};
+7 −1
Original line number Diff line number Diff line
@@ -144,9 +144,15 @@ void ZipEntry::initNew(const char* fileName, const char* comment)
 * Initializes the CDE and the LFH.
 */
status_t ZipEntry::initFromExternal(const ZipFile* /* pZipFile */,
    const ZipEntry* pEntry)
    const ZipEntry* pEntry, const char* storageName)
{
    mCDE = pEntry->mCDE;
    if (storageName && *storageName != 0) {
        mCDE.mFileNameLength = strlen(storageName);
        mCDE.mFileName = new unsigned char[mCDE.mFileNameLength + 1];
        strcpy((char*) mCDE.mFileName, storageName);
    }

    // Check whether we got all the memory needed.
    if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) ||
            (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) ||
Loading