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

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

AAPT2: Introduce notion of 'product' to ResourceTable

This allows us to preserve the various product definitions during the compile
phase, and allows us to select the product in the link phase.

This allows compiled files to remain product-independent, so that they do not need
to be recompiled when switching targets.

Bug:25958912
Change-Id: Iaa7eed25c834b67a39cdc9be43613e8b5ab6cdd7
parent 59e04c6f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ sources := \
	io/ZipArchive.cpp \
	link/AutoVersioner.cpp \
	link/ManifestFixer.cpp \
	link/ProductFilter.cpp \
	link/PrivateAttributeMover.cpp \
	link/ReferenceLinker.cpp \
	link/TableMerger.cpp \
@@ -83,6 +84,7 @@ testSources := \
	link/AutoVersioner_test.cpp \
	link/ManifestFixer_test.cpp \
	link/PrivateAttributeMover_test.cpp \
	link/ProductFilter_test.cpp \
	link/ReferenceLinker_test.cpp \
	link/TableMerger_test.cpp \
	link/XmlReferenceLinker_test.cpp \
+3 −3
Original line number Diff line number Diff line
@@ -144,8 +144,8 @@ void Debug::printTable(ResourceTable* table) {

                PrintVisitor visitor;
                for (const auto& value : entry->values) {
                    std::cout << "      (" << value.config << ") ";
                    value.value->accept(&visitor);
                    std::cout << "      (" << value->config << ") ";
                    value->value->accept(&visitor);
                    std::cout << std::endl;
                }
            }
@@ -176,7 +176,7 @@ void Debug::printStyleGraph(ResourceTable* table, const ResourceName& targetStyl
        if (result) {
            ResourceEntry* entry = result.value().entry;
            for (const auto& value : entry->values) {
                if (Style* style = valueCast<Style>(value.value.get())) {
                if (Style* style = valueCast<Style>(value->value.get())) {
                    if (style->parent && style->parent.value().name) {
                        parents.insert(style->parent.value().name.value());
                        stylesToVisit.push(style->parent.value().name.value());
+6 −40
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "util/Comparators.h"
#include "util/ImmutableMap.h"
#include "util/Util.h"
#include "xml/XmlPullParser.h"
@@ -71,6 +70,7 @@ static uint32_t parseFormatAttribute(const StringPiece16& str) {
struct ParsedResource {
    ResourceName name;
    ConfigDescription config;
    std::string product;
    Source source;
    ResourceId id;
    Maybe<SymbolState> symbolState;
@@ -79,35 +79,6 @@ struct ParsedResource {
    std::list<ParsedResource> childResources;
};

bool ResourceParser::shouldStripResource(const ResourceNameRef& name,
                                         const Maybe<std::u16string>& product) const {
    if (product) {
        for (const std::u16string& productToMatch : mOptions.products) {
            if (product.value() == productToMatch) {
                // We specified a product, and it is in the list, so don't strip.
                return false;
            }
        }
    }

    // Nothing matched, try 'default'. Default only matches if we didn't already use another
    // product variant.
    if (!product || product.value() == u"default") {
        if (Maybe<ResourceTable::SearchResult> result = mTable->findResource(name)) {
            const ResourceEntry* entry = result.value().entry;
            auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), mConfig,
                                         cmp::lessThanConfig);
            if (iter != entry->values.end() && iter->config == mConfig && !iter->value->isWeak()) {
                // We have a value for this config already, and it is not weak,
                // so filter out this default.
                return true;
            }
        }
        return false;
    }
    return true;
}

// Recursively adds resources to the ResourceTable.
static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
    if (res->symbolState) {
@@ -125,7 +96,8 @@ static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed
        res->value->setComment(std::move(res->comment));
        res->value->setSource(std::move(res->source));

        if (!table->addResource(res->name, res->id, res->config, std::move(res->value), diag)) {
        if (!table->addResource(res->name, res->id, res->config, res->product,
                                std::move(res->value), diag)) {
            return false;
        }
    }
@@ -295,9 +267,8 @@ bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
        parsedResource.comment = std::move(comment);

        // Extract the product name if it exists.
        Maybe<std::u16string> product;
        if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) {
            product = maybeProduct.value().toString();
            parsedResource.product = util::utf16ToUtf8(maybeProduct.value());
        }

        // Parse the resource regardless of product.
@@ -306,12 +277,7 @@ bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
            continue;
        }

        // We successfully parsed the resource. Check if we should include it or strip it.
        if (shouldStripResource(parsedResource.name, product)) {
            // Record that we stripped out this resource name.
            // We will check that at least one variant of this resource was included.
            strippedResources.insert(parsedResource.name);
        } else if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
        if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
            error = true;
        }
    }
@@ -524,7 +490,7 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const
        // name.package can be empty here, as it will assume the package name of the table.
        std::unique_ptr<Id> id = util::make_unique<Id>();
        id->setSource(mSource.withLine(beginXmlLine));
        mTable->addResource(name, {}, std::move(id), mDiag);
        mTable->addResource(name, {}, {}, std::move(id), mDiag);
    };

    // Process the raw value.
+0 −10
Original line number Diff line number Diff line
@@ -33,13 +33,6 @@ namespace aapt {
struct ParsedResource;

struct ResourceParserOptions {
    /**
     * Optional product names by which to filter resources.
     * This is like a preprocessor definition in that we strip out resources
     * that don't match before we compile them.
     */
    std::vector<std::u16string> products;

    /**
     * Whether the default setting for this parser is to allow translation.
     */
@@ -106,9 +99,6 @@ private:
    bool parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
    bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource);

    bool shouldStripResource(const ResourceNameRef& name,
                             const Maybe<std::u16string>& product) const;

    IDiagnostics* mDiag;
    ResourceTable* mTable;
    Source mSource;
+21 −39
Original line number Diff line number Diff line
@@ -48,24 +48,13 @@ struct ResourceParserTest : public ::testing::Test {
    }

    ::testing::AssertionResult testParse(const StringPiece& str) {
        return testParse(str, ConfigDescription{}, {});
        return testParse(str, ConfigDescription{});
    }

    ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config) {
        return testParse(str, config, {});
    }

    ::testing::AssertionResult testParse(const StringPiece& str,
                                         std::initializer_list<std::u16string> products) {
        return testParse(str, {}, std::move(products));
    }

    ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config,
                                         std::initializer_list<std::u16string> products) {
        std::stringstream input(kXmlPreamble);
        input << "<resources>\n" << str << "\n</resources>" << std::endl;
        ResourceParserOptions parserOptions;
        parserOptions.products = products;
        ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, config,
                              parserOptions);
        xml::XmlPullParser xmlParser(input);
@@ -546,7 +535,7 @@ TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
    ASSERT_NE(nullptr, id);
}

TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
TEST_F(ResourceParserTest, KeepAllProducts) {
    std::string input = R"EOF(
        <string name="foo" product="phone">hi</string>
        <string name="foo" product="no-sdcard">ho</string>
@@ -555,33 +544,26 @@ TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
        <string name="bit" product="phablet">hoot</string>
        <string name="bot" product="default">yes</string>
    )EOF";
    ASSERT_TRUE(testParse(input, { std::u16string(u"no-sdcard"), std::u16string(u"phablet") }));

    String* fooStr = test::getValue<String>(&mTable, u"@string/foo");
    ASSERT_NE(nullptr, fooStr);
    EXPECT_EQ(StringPiece16(u"ho"), *fooStr->value);

    EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bar"));
    EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/baz"));
    EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bit"));
    EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bot"));
}

TEST_F(ResourceParserTest, FilterProductsThatBothMatchInOrder) {
    std::string input = R"EOF(
        <string name="foo" product="phone">phone</string>
        <string name="foo" product="default">default</string>
    )EOF";
    ASSERT_TRUE(testParse(input, { std::u16string(u"phone") }));

    String* foo = test::getValue<String>(&mTable, u"@string/foo");
    ASSERT_NE(nullptr, foo);
    EXPECT_EQ(std::u16string(u"phone"), *foo->value);
}
    ASSERT_TRUE(testParse(input));

TEST_F(ResourceParserTest, FailWhenProductFilterStripsOutAllVersionsOfResource) {
    std::string input = "<string name=\"foo\" product=\"tablet\">hello</string>\n";
    ASSERT_FALSE(testParse(input, { std::u16string(u"phone") }));
    EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/foo",
                                                                 ConfigDescription::defaultConfig(),
                                                                 "phone"));
    EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/foo",
                                                                 ConfigDescription::defaultConfig(),
                                                                 "no-sdcard"));
    EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bar",
                                                                 ConfigDescription::defaultConfig(),
                                                                 ""));
    EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/baz",
                                                                 ConfigDescription::defaultConfig(),
                                                                 ""));
    EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bit",
                                                                 ConfigDescription::defaultConfig(),
                                                                 "phablet"));
    EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bot",
                                                                 ConfigDescription::defaultConfig(),
                                                                 "default"));
}

TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
Loading