AAPT2: Fix product support
Previously the default product wasn't tried if 'default' wasn't specified on the command line. Also adds support for multiple products. Change-Id: I1e4872b34bb8d609b6444841a4e7e4dbb3bbb76b
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#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"
|
||||
@@ -64,26 +65,6 @@ static uint32_t parseFormatAttribute(const StringPiece16& str) {
|
||||
return mask;
|
||||
}
|
||||
|
||||
static bool shouldStripResource(const xml::XmlPullParser* parser,
|
||||
const Maybe<std::u16string> productToMatch) {
|
||||
assert(parser->getEvent() == xml::XmlPullParser::Event::kStartElement);
|
||||
|
||||
if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) {
|
||||
if (!productToMatch) {
|
||||
if (maybeProduct.value() != u"default" && maybeProduct.value() != u"phone") {
|
||||
// We didn't specify a product and this is not a default product, so skip.
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (productToMatch && maybeProduct.value() != productToMatch.value()) {
|
||||
// We specified a product, but they don't match.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A parsed resource ready to be added to the ResourceTable.
|
||||
*/
|
||||
@@ -97,6 +78,35 @@ 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, const ConfigDescription& config,
|
||||
IDiagnostics* diag, ParsedResource* res) {
|
||||
@@ -283,17 +293,20 @@ bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
|
||||
parsedResource.source = mSource.withLine(parser->getLineNumber());
|
||||
parsedResource.comment = std::move(comment);
|
||||
|
||||
// Check if we should skip this product. We still need to parse it for correctness.
|
||||
const bool stripResource = shouldStripResource(parser, mOptions.product);
|
||||
// Extract the product name if it exists.
|
||||
Maybe<std::u16string> product;
|
||||
if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) {
|
||||
product = maybeProduct.value().toString();
|
||||
}
|
||||
|
||||
// Parse the resource regardless of product.
|
||||
if (!parseResource(parser, &parsedResource)) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We successfully parsed the resource.
|
||||
|
||||
if (stripResource) {
|
||||
// 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);
|
||||
|
||||
@@ -34,11 +34,11 @@ struct ParsedResource;
|
||||
|
||||
struct ResourceParserOptions {
|
||||
/**
|
||||
* Optional product name by which to filter resources.
|
||||
* 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.
|
||||
*/
|
||||
Maybe<std::u16string> product;
|
||||
std::vector<std::u16string> products;
|
||||
|
||||
/**
|
||||
* Whether the default setting for this parser is to allow translation.
|
||||
@@ -101,6 +101,9 @@ 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;
|
||||
|
||||
@@ -48,11 +48,11 @@ struct ResourceParserTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
::testing::AssertionResult testParse(const StringPiece& str,
|
||||
Maybe<std::u16string> product = {}) {
|
||||
std::initializer_list<std::u16string> products = {}) {
|
||||
std::stringstream input(kXmlPreamble);
|
||||
input << "<resources>\n" << str << "\n</resources>" << std::endl;
|
||||
ResourceParserOptions parserOptions;
|
||||
parserOptions.product = product;
|
||||
parserOptions.products = products;
|
||||
ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {},
|
||||
parserOptions);
|
||||
xml::XmlPullParser xmlParser(input);
|
||||
@@ -514,11 +514,15 @@ TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
|
||||
std::string input = "<string name=\"foo\" product=\"phone\">hi</string>\n"
|
||||
"<string name=\"foo\" product=\"no-sdcard\">ho</string>\n"
|
||||
"<string name=\"bar\" product=\"\">wee</string>\n"
|
||||
"<string name=\"baz\">woo</string>\n";
|
||||
ASSERT_TRUE(testParse(input, std::u16string(u"no-sdcard")));
|
||||
std::string input = R"EOF(
|
||||
<string name="foo" product="phone">hi</string>
|
||||
<string name="foo" product="no-sdcard">ho</string>
|
||||
<string name="bar" product="">wee</string>
|
||||
<string name="baz">woo</string>
|
||||
<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);
|
||||
@@ -526,11 +530,25 @@ TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, FailWhenProductFilterStripsOutAllVersionsOfResource) {
|
||||
std::string input = "<string name=\"foo\" product=\"tablet\">hello</string>\n";
|
||||
ASSERT_FALSE(testParse(input, std::u16string(u"phone")));
|
||||
ASSERT_FALSE(testParse(input, { std::u16string(u"phone") }));
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
|
||||
|
||||
@@ -105,7 +105,7 @@ static Maybe<ResourcePathData> extractResourcePathData(const std::string& path,
|
||||
struct CompileOptions {
|
||||
std::string outputPath;
|
||||
Maybe<std::string> resDir;
|
||||
Maybe<std::u16string> product;
|
||||
std::vector<std::u16string> products;
|
||||
bool pseudolocalize = false;
|
||||
bool verbose = false;
|
||||
};
|
||||
@@ -191,7 +191,7 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options,
|
||||
xml::XmlPullParser xmlParser(fin);
|
||||
|
||||
ResourceParserOptions parserOptions;
|
||||
parserOptions.product = options.product;
|
||||
parserOptions.products = options.products;
|
||||
|
||||
// If the filename includes donottranslate, then the default translatable is false.
|
||||
parserOptions.translatable = pathData.name.find(u"donottranslate") == std::string::npos;
|
||||
@@ -430,10 +430,11 @@ public:
|
||||
int compile(const std::vector<StringPiece>& args) {
|
||||
CompileOptions options;
|
||||
|
||||
Maybe<std::string> product;
|
||||
Maybe<std::string> productList;
|
||||
Flags flags = Flags()
|
||||
.requiredFlag("-o", "Output path", &options.outputPath)
|
||||
.optionalFlag("--product", "Product type to compile", &product)
|
||||
.optionalFlag("--product", "Comma separated list of product types to compile",
|
||||
&productList)
|
||||
.optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
|
||||
.optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
|
||||
"(en-XA and ar-XB)", &options.pseudolocalize)
|
||||
@@ -442,8 +443,10 @@ int compile(const std::vector<StringPiece>& args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (product) {
|
||||
options.product = util::utf8ToUtf16(product.value());
|
||||
if (productList) {
|
||||
for (StringPiece part : util::tokenize<char>(productList.value(), ',')) {
|
||||
options.products.push_back(util::utf8ToUtf16(part));
|
||||
}
|
||||
}
|
||||
|
||||
CompileContext context;
|
||||
|
||||
Reference in New Issue
Block a user