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

Commit d5c4f872 authored by Adam Lesinski's avatar Adam Lesinski
Browse files

Properly mangle file names

Change-Id: I49c0f82e8c06f056198eb64b8369d83403b74321
parent d981c0d4
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