diff options
Diffstat (limited to 'tools')
39 files changed, 514 insertions, 250 deletions
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index b4c4d05a7397..1e7875d435eb 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -1033,7 +1033,6 @@ static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) return NO_ERROR; } - ResXMLTree tree; Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING); if (asset == NULL) { fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n"); @@ -1041,11 +1040,17 @@ static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) } ssize_t result = NO_ERROR; - if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) { - fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); - result = UNKNOWN_ERROR; - } else { - result = extractPlatformBuildVersion(tree, bundle); + + // Create a new scope so that ResXMLTree is destroyed before we delete the memory over + // which it iterates (asset). + { + ResXMLTree tree; + if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) { + fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); + result = UNKNOWN_ERROR; + } else { + result = extractPlatformBuildVersion(tree, bundle); + } } delete asset; diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h index 1d39b722b053..a9794a419ca2 100644 --- a/tools/aapt2/AppInfo.h +++ b/tools/aapt2/AppInfo.h @@ -37,6 +37,16 @@ struct AppInfo { * The App's minimum SDK version. */ Maybe<std::string> minSdkVersion; + + /** + * The Version code of the app. + */ + Maybe<uint32_t> versionCode; + + /** + * The revision code of the app. + */ + Maybe<uint32_t> revisionCode; }; } // namespace aapt diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h index e86f2a8830e8..725027c2c45a 100644 --- a/tools/aapt2/Diagnostics.h +++ b/tools/aapt2/Diagnostics.h @@ -41,13 +41,13 @@ private: public: DiagMessage() = default; - DiagMessage(const StringPiece& src) : mSource(src) { + explicit DiagMessage(const StringPiece& src) : mSource(src) { } - DiagMessage(const Source& src) : mSource(src) { + explicit DiagMessage(const Source& src) : mSource(src) { } - DiagMessage(size_t line) : mSource(Source().withLine(line)) { + explicit DiagMessage(size_t line) : mSource(Source().withLine(line)) { } template <typename T> diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index a74b5aa2478e..ed55f852c24c 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -25,7 +25,7 @@ namespace aapt { static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. -static const char* sMinorVersion = "0"; +static const char* sMinorVersion = "1"; int printVersion() { std::cerr << "Android Asset Packaging Tool (aapt) " diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h index 505a982e5eab..b6aaa4df3bec 100644 --- a/tools/aapt2/NameMangler.h +++ b/tools/aapt2/NameMangler.h @@ -43,7 +43,7 @@ private: NameManglerPolicy mPolicy; public: - NameMangler(NameManglerPolicy policy) : mPolicy(policy) { + explicit NameMangler(NameManglerPolicy policy) : mPolicy(policy) { } Maybe<ResourceName> mangleName(const ResourceName& name) { diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index a144c6ab2be7..bcdf401077ee 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -500,8 +500,8 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const }; // Process the raw value. - std::unique_ptr<Item> processedItem = ResourceUtils::parseItemForAttribute(rawValue, typeMask, - onCreateReference); + std::unique_ptr<Item> processedItem = ResourceUtils::tryParseItemForAttribute( + rawValue, typeMask, onCreateReference); if (processedItem) { // Fix up the reference. if (Reference* ref = valueCast<Reference>(processedItem.get())) { @@ -528,20 +528,24 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* outResource) { bool formatted = true; if (Maybe<StringPiece> formattedAttr = xml::findAttribute(parser, "formatted")) { - if (!ResourceUtils::tryParseBool(formattedAttr.value(), &formatted)) { + Maybe<bool> maybeFormatted = ResourceUtils::parseBool(formattedAttr.value()); + if (!maybeFormatted) { mDiag->error(DiagMessage(outResource->source) << "invalid value for 'formatted'. Must be a boolean"); return false; } + formatted = maybeFormatted.value(); } bool translateable = mOptions.translatable; if (Maybe<StringPiece> translateableAttr = xml::findAttribute(parser, "translatable")) { - if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { + Maybe<bool> maybeTranslateable = ResourceUtils::parseBool(translateableAttr.value()); + if (!maybeTranslateable) { mDiag->error(DiagMessage(outResource->source) << "invalid value for 'translatable'. Must be a boolean"); return false; } + translateable = maybeTranslateable.value(); } outResource->value = parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString); @@ -590,7 +594,7 @@ bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* out outResource->name.type = *parsedType; if (Maybe<StringPiece> maybeIdStr = xml::findNonEmptyAttribute(parser, "id")) { - Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(maybeIdStr.value()); + Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(maybeIdStr.value()); if (!maybeId) { mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '" << maybeId.value() << "' in <public>"); @@ -630,7 +634,7 @@ bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource return false; } - Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(maybeIdStr.value()); + Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(maybeIdStr.value()); if (!maybeId) { mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '" << maybeIdStr.value() << "' in <public-group>"); @@ -1058,14 +1062,17 @@ bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* bool translateable = mOptions.translatable; if (Maybe<StringPiece> translateableAttr = xml::findAttribute(parser, "translatable")) { - if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { + Maybe<bool> maybeTranslateable = ResourceUtils::parseBool(translateableAttr.value()); + if (!maybeTranslateable) { mDiag->error(DiagMessage(outResource->source) << "invalid value for 'translatable'. Must be a boolean"); return false; } + translateable = maybeTranslateable.value(); } array->setTranslateable(translateable); + bool error = false; const size_t depth = parser->getDepth(); while (xml::XmlPullParser::nextChildNode(parser, depth)) { diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 773616d6aa71..11619fa9c67d 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -124,7 +124,7 @@ bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef, bool* ou return true; } -bool tryParseReference(const StringPiece& str, ResourceNameRef* outRef, bool* outCreate, +bool parseReference(const StringPiece& str, ResourceNameRef* outRef, bool* outCreate, bool* outPrivate) { StringPiece trimmedStr(util::trimWhitespace(str)); if (trimmedStr.empty()) { @@ -171,10 +171,10 @@ bool tryParseReference(const StringPiece& str, ResourceNameRef* outRef, bool* ou } bool isReference(const StringPiece& str) { - return tryParseReference(str, nullptr, nullptr, nullptr); + return parseReference(str, nullptr, nullptr, nullptr); } -bool tryParseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) { +bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) { StringPiece trimmedStr(util::trimWhitespace(str)); if (trimmedStr.empty()) { return false; @@ -208,7 +208,7 @@ bool tryParseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) } bool isAttributeReference(const StringPiece& str) { - return tryParseAttributeReference(str, nullptr); + return parseAttributeReference(str, nullptr); } /* @@ -271,13 +271,13 @@ Maybe<Reference> parseStyleParentReference(const StringPiece& str, std::string* std::unique_ptr<Reference> tryParseReference(const StringPiece& str, bool* outCreate) { ResourceNameRef ref; bool privateRef = false; - if (tryParseReference(str, &ref, outCreate, &privateRef)) { + if (parseReference(str, &ref, outCreate, &privateRef)) { std::unique_ptr<Reference> value = util::make_unique<Reference>(ref); value->privateReference = privateRef; return value; } - if (tryParseAttributeReference(str, &ref)) { + if (parseAttributeReference(str, &ref)) { if (outCreate) { *outCreate = false; } @@ -420,23 +420,26 @@ std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str) { return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value); } -bool tryParseBool(const StringPiece& str, bool* outValue) { +Maybe<bool> parseBool(const StringPiece& str) { StringPiece trimmedStr(util::trimWhitespace(str)); if (trimmedStr == "true" || trimmedStr == "TRUE" || trimmedStr == "True") { - if (outValue) { - *outValue = true; - } - return true; + return Maybe<bool>(true); } else if (trimmedStr == "false" || trimmedStr == "FALSE" || trimmedStr == "False") { - if (outValue) { - *outValue = false; - } - return true; + return Maybe<bool>(false); } - return false; + return {}; +} + +Maybe<uint32_t> parseInt(const StringPiece& str) { + std::u16string str16 = util::utf8ToUtf16(str); + android::Res_value value; + if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { + return value.data; + } + return {}; } -Maybe<ResourceId> tryParseResourceId(const StringPiece& str) { +Maybe<ResourceId> parseResourceId(const StringPiece& str) { StringPiece trimmedStr(util::trimWhitespace(str)); std::u16string str16 = util::utf8ToUtf16(trimmedStr); @@ -452,7 +455,7 @@ Maybe<ResourceId> tryParseResourceId(const StringPiece& str) { return {}; } -Maybe<int> tryParseSdkVersion(const StringPiece& str) { +Maybe<int> parseSdkVersion(const StringPiece& str) { StringPiece trimmedStr(util::trimWhitespace(str)); std::u16string str16 = util::utf8ToUtf16(trimmedStr); @@ -470,12 +473,11 @@ Maybe<int> tryParseSdkVersion(const StringPiece& str) { } std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str) { - bool result = false; - if (tryParseBool(str, &result)) { + if (Maybe<bool> maybeResult = parseBool(str)) { android::Res_value value = {}; value.dataType = android::Res_value::TYPE_INT_BOOLEAN; - if (result) { + if (maybeResult.value()) { value.data = 0xffffffffu; } else { value.data = 0; @@ -542,7 +544,7 @@ uint32_t androidTypeToAttributeTypeMask(uint16_t type) { }; } -std::unique_ptr<Item> parseItemForAttribute( +std::unique_ptr<Item> tryParseItemForAttribute( const StringPiece& value, uint32_t typeMask, const std::function<void(const ResourceName&)>& onCreateReference) { @@ -602,11 +604,11 @@ std::unique_ptr<Item> parseItemForAttribute( * We successively try to parse the string as a resource type that the Attribute * allows. */ -std::unique_ptr<Item> parseItemForAttribute( +std::unique_ptr<Item> tryParseItemForAttribute( const StringPiece& str, const Attribute* attr, const std::function<void(const ResourceName&)>& onCreateReference) { const uint32_t typeMask = attr->typeMask; - std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference); + std::unique_ptr<Item> value = tryParseItemForAttribute(str, typeMask, onCreateReference); if (value) { return value; } diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index a57d89dd0233..244047bae117 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -28,11 +28,6 @@ namespace aapt { namespace ResourceUtils { -/** - * Convert an android::ResTable::resource_name to an aapt::ResourceName struct. - */ -Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& name); - /* * Extracts the package, type, and name from a string of the format: * @@ -60,8 +55,8 @@ bool parseResourceName(const StringPiece& str, ResourceNameRef* outResource, * If '+' was present in the reference, `outCreate` is set to true. * If '*' was present in the reference, `outPrivate` is set to true. */ -bool tryParseReference(const StringPiece& str, ResourceNameRef* outReference, - bool* outCreate = nullptr, bool* outPrivate = nullptr); +bool parseReference(const StringPiece& str, ResourceNameRef* outReference, + bool* outCreate = nullptr, bool* outPrivate = nullptr); /* * Returns true if the string is in the form of a resource reference (@[+][package:]type/name). @@ -72,7 +67,7 @@ bool isReference(const StringPiece& str); * Returns true if the string was parsed as an attribute reference (?[package:][type/]name), * with `outReference` set to the parsed reference. */ -bool tryParseAttributeReference(const StringPiece& str, ResourceNameRef* outReference); +bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outReference); /** * Returns true if the string is in the form of an attribute reference(?[package:][type/]name). @@ -80,19 +75,29 @@ bool tryParseAttributeReference(const StringPiece& str, ResourceNameRef* outRefe bool isAttributeReference(const StringPiece& str); /** - * Returns true if the value is a boolean, putting the result in `outValue`. + * Convert an android::ResTable::resource_name to an aapt::ResourceName struct. + */ +Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& name); + +/** + * Returns a boolean value if the string is equal to TRUE, true, True, FALSE, false, or False. + */ +Maybe<bool> parseBool(const StringPiece& str); + +/** + * Returns a uint32_t if the string is an integer. */ -bool tryParseBool(const StringPiece& str, bool* outValue); +Maybe<uint32_t> parseInt(const StringPiece& str); /** * Returns an ID if it the string represented a valid ID. */ -Maybe<ResourceId> tryParseResourceId(const StringPiece& str); +Maybe<ResourceId> parseResourceId(const StringPiece& str); /** * Parses an SDK version, which can be an integer, or a letter from A-Z. */ -Maybe<int> tryParseSdkVersion(const StringPiece& str); +Maybe<int> parseSdkVersion(const StringPiece& str); /* * Returns a Reference, or None Maybe instance if the string `str` was parsed as a @@ -161,11 +166,11 @@ std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* enumAttr, * The callback function onCreateReference is called when the parsed item is a * reference to an ID that must be created (@+id/foo). */ -std::unique_ptr<Item> parseItemForAttribute( +std::unique_ptr<Item> tryParseItemForAttribute( const StringPiece& value, const Attribute* attr, const std::function<void(const ResourceName&)>& onCreateReference = {}); -std::unique_ptr<Item> parseItemForAttribute( +std::unique_ptr<Item> tryParseItemForAttribute( const StringPiece& value, uint32_t typeMask, const std::function<void(const ResourceName&)>& onCreateReference = {}); diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp index fb76914cc495..894cfcf72144 100644 --- a/tools/aapt2/ResourceUtils_test.cpp +++ b/tools/aapt2/ResourceUtils_test.cpp @@ -21,24 +21,12 @@ namespace aapt { TEST(ResourceUtilsTest, ParseBool) { - bool val = false; - EXPECT_TRUE(ResourceUtils::tryParseBool("true", &val)); - EXPECT_TRUE(val); - - EXPECT_TRUE(ResourceUtils::tryParseBool("TRUE", &val)); - EXPECT_TRUE(val); - - EXPECT_TRUE(ResourceUtils::tryParseBool("True", &val)); - EXPECT_TRUE(val); - - EXPECT_TRUE(ResourceUtils::tryParseBool("false", &val)); - EXPECT_FALSE(val); - - EXPECT_TRUE(ResourceUtils::tryParseBool("FALSE", &val)); - EXPECT_FALSE(val); - - EXPECT_TRUE(ResourceUtils::tryParseBool("False", &val)); - EXPECT_FALSE(val); + EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("true")); + EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("TRUE")); + EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("True")); + EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("false")); + EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("FALSE")); + EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("False")); } TEST(ResourceUtilsTest, ParseResourceName) { @@ -64,7 +52,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) { ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference("@color/foo", &actual, &create, &privateRef)); + EXPECT_TRUE(ResourceUtils::parseReference("@color/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); EXPECT_FALSE(privateRef); @@ -75,7 +63,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithPackage) { ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference("@android:color/foo", &actual, &create, + EXPECT_TRUE(ResourceUtils::parseReference("@android:color/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); @@ -87,7 +75,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) { ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference("\t @android:color/foo\n \n\t", &actual, + EXPECT_TRUE(ResourceUtils::parseReference("\t @android:color/foo\n \n\t", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); @@ -99,7 +87,7 @@ TEST(ResourceUtilsTest, ParseAutoCreateIdReference) { ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference("@+android:id/foo", &actual, &create, + EXPECT_TRUE(ResourceUtils::parseReference("@+android:id/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_TRUE(create); @@ -111,7 +99,7 @@ TEST(ResourceUtilsTest, ParsePrivateReference) { ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference("@*android:id/foo", &actual, &create, + EXPECT_TRUE(ResourceUtils::parseReference("@*android:id/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); @@ -122,7 +110,7 @@ TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) { bool create = false; bool privateRef = false; ResourceNameRef actual; - EXPECT_FALSE(ResourceUtils::tryParseReference("@+android:color/foo", &actual, &create, + EXPECT_FALSE(ResourceUtils::parseReference("@+android:color/foo", &actual, &create, &privateRef)); } diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp index 4a865799372d..73682ab110c7 100644 --- a/tools/aapt2/ResourceValues.cpp +++ b/tools/aapt2/ResourceValues.cpp @@ -449,9 +449,7 @@ void Attribute::print(std::ostream* out) const { printMask(out); if (!symbols.empty()) { - *out << " [" - << util::joiner(symbols.begin(), symbols.end(), ", ") - << "]"; + *out << " [" << util::joiner(symbols, ", ") << "]"; } if (minInt != std::numeric_limits<int32_t>::min()) { @@ -600,7 +598,7 @@ void Style::print(std::ostream* out) const { *out << parent.value().name.value(); } *out << " [" - << util::joiner(entries.begin(), entries.end(), ", ") + << util::joiner(entries, ", ") << "]"; } @@ -645,7 +643,7 @@ Array* Array::clone(StringPool* newPool) const { void Array::print(std::ostream* out) const { *out << "(array) [" - << util::joiner(items.begin(), items.end(), ", ") + << util::joiner(items, ", ") << "]"; } @@ -730,7 +728,7 @@ Styleable* Styleable::clone(StringPool* /*newPool*/) const { void Styleable::print(std::ostream* out) const { *out << "(styleable) " << " [" - << util::joiner(entries.begin(), entries.end(), ", ") + << util::joiner(entries, ", ") << "]"; } diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h index 319528e0ea1b..8a1021d49c39 100644 --- a/tools/aapt2/Source.h +++ b/tools/aapt2/Source.h @@ -35,7 +35,7 @@ struct Source { Source() = default; - inline Source(const StringPiece& path) : path(path.toString()) { + inline Source(const StringPiece& path) : path(path.toString()) { // NOLINT(implicit) } inline Source(const StringPiece& path, size_t line) : path(path.toString()), line(line) { diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h index b8bc5db7d6e4..9dc6a9c2b0d2 100644 --- a/tools/aapt2/ValueVisitor.h +++ b/tools/aapt2/ValueVisitor.h @@ -45,8 +45,9 @@ struct RawValueVisitor { virtual void visit(Styleable* value) {} }; +// NOLINT, do not add parentheses around T. #define DECL_VISIT_COMPOUND_VALUE(T) \ - virtual void visit(T* value) { \ + virtual void visit(T* value) { /* NOLINT */ \ visitSubValues(value); \ } diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h index 345ff6c56870..f835b06e762b 100644 --- a/tools/aapt2/compile/Png.h +++ b/tools/aapt2/compile/Png.h @@ -32,7 +32,7 @@ struct PngOptions { class Png { public: - Png(IDiagnostics* diag) : mDiag(diag) { + explicit Png(IDiagnostics* diag) : mDiag(diag) { } bool process(const Source& source, std::istream* input, BigBuffer* outBuffer, diff --git a/tools/aapt2/compile/Pseudolocalizer.h b/tools/aapt2/compile/Pseudolocalizer.h index 7db88de13536..91d17d174d29 100644 --- a/tools/aapt2/compile/Pseudolocalizer.h +++ b/tools/aapt2/compile/Pseudolocalizer.h @@ -43,7 +43,7 @@ public: kBidi, }; - Pseudolocalizer(Method method); + explicit Pseudolocalizer(Method method); void setMethod(Method method); std::string start() { return mImpl->start(); } std::string end() { return mImpl->end(); } diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp index 9fc979c34353..3901419636b4 100644 --- a/tools/aapt2/compile/XmlIdCollector.cpp +++ b/tools/aapt2/compile/XmlIdCollector.cpp @@ -42,7 +42,7 @@ struct IdCollector : public xml::Visitor { for (xml::Attribute& attr : element->attributes) { ResourceNameRef name; bool create = false; - if (ResourceUtils::tryParseReference(attr.value, &name, &create, nullptr)) { + if (ResourceUtils::parseReference(attr.value, &name, &create, nullptr)) { if (create && name.type == ResourceType::kId) { auto iter = std::lower_bound(mOutSymbols->begin(), mOutSymbols->end(), name, cmpName); diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/flatten/TableFlattener.h index 0ab01974044b..b416f202ced5 100644 --- a/tools/aapt2/flatten/TableFlattener.h +++ b/tools/aapt2/flatten/TableFlattener.h @@ -26,7 +26,7 @@ class ResourceTable; class TableFlattener : public IResourceTableConsumer { public: - TableFlattener(BigBuffer* buffer) : mBuffer(buffer) { + explicit TableFlattener(BigBuffer* buffer) : mBuffer(buffer) { } bool consume(IAaptContext* context, ResourceTable* table) override; diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h index f0559c03a8b8..72a932a499dd 100644 --- a/tools/aapt2/io/FileSystem.h +++ b/tools/aapt2/io/FileSystem.h @@ -29,7 +29,7 @@ namespace io { */ class RegularFile : public IFile { public: - RegularFile(const Source& source); + explicit RegularFile(const Source& source); std::unique_ptr<IData> openAsData() override; const Source& getSource() const override; @@ -42,7 +42,7 @@ class FileCollection; class FileCollectionIterator : public IFileCollectionIterator { public: - FileCollectionIterator(FileCollection* collection); + explicit FileCollectionIterator(FileCollection* collection); bool hasNext() override; io::IFile* next() override; diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h index 5ad119d1d6d4..565588e2db57 100644 --- a/tools/aapt2/io/ZipArchive.h +++ b/tools/aapt2/io/ZipArchive.h @@ -47,7 +47,7 @@ class ZipFileCollection; class ZipFileCollectionIterator : public IFileCollectionIterator { public: - ZipFileCollectionIterator(ZipFileCollection* collection); + explicit ZipFileCollectionIterator(ZipFileCollection* collection); bool hasNext() override; io::IFile* next() override; diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h index d45328fedba2..e84c2741e4ce 100644 --- a/tools/aapt2/java/ClassDefinition.h +++ b/tools/aapt2/java/ClassDefinition.h @@ -110,7 +110,7 @@ using StringMember = PrimitiveMember<std::string>; template <typename T> class PrimitiveArrayMember : public ClassMember { public: - PrimitiveArrayMember(const StringPiece& name) : + explicit PrimitiveArrayMember(const StringPiece& name) : mName(name.toString()) { } diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index ff777a3b60eb..5a2bb6ad9a72 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -59,11 +59,14 @@ struct LinkOptions { std::string manifestPath; std::vector<std::string> includePaths; std::vector<std::string> overlayFiles; + + // Java/Proguard options. Maybe<std::string> generateJavaClassPath; Maybe<std::string> customJavaPackage; std::set<std::string> extraJavaPackages; Maybe<std::string> generateProguardRulesPath; Maybe<std::string> generateMainDexProguardRulesPath; + bool noAutoVersion = false; bool noVersionVectors = false; bool staticLib = false; @@ -77,7 +80,13 @@ struct LinkOptions { Maybe<std::string> privateSymbols; ManifestFixerOptions manifestFixerOptions; std::unordered_set<std::string> products; + + // Split APK options. TableSplitterOptions tableSplitterOptions; + std::vector<SplitConstraints> splitConstraints; + std::vector<std::string> splitPaths; + + // Stable ID options. std::unordered_map<ResourceName, ResourceId> stableIdMap; Maybe<std::string> resourceIdMapPath; }; @@ -585,7 +594,7 @@ static bool loadStableIdMap(IDiagnostics* diag, const std::string& path, const size_t resIdStrLen = line.size() - resIdStartIdx; StringPiece resIdStr = util::trimWhitespace(line.substr(resIdStartIdx, resIdStrLen)); - Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(resIdStr); + Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(resIdStr); if (!maybeId) { diag->error(DiagMessage(Source(path, lineNo)) << "invalid resource ID '" << resIdStr << "'"); @@ -597,6 +606,28 @@ static bool loadStableIdMap(IDiagnostics* diag, const std::string& path, return true; } +static bool parseSplitParameter(const StringPiece& arg, IDiagnostics* diag, + std::string* outPath, SplitConstraints* outSplit) { + std::vector<std::string> parts = util::split(arg, ':'); + if (parts.size() != 2) { + diag->error(DiagMessage() << "invalid split parameter '" << arg << "'"); + diag->note(DiagMessage() << "should be --split path/to/output.apk:<config>[,<config>...]"); + return false; + } + *outPath = parts[0]; + std::vector<ConfigDescription> configs; + for (const StringPiece& configStr : util::tokenize(parts[1], ',')) { + configs.push_back({}); + if (!ConfigDescription::parse(configStr, &configs.back())) { + diag->error(DiagMessage() << "invalid config '" << configStr + << "' in split parameter '" << arg << "'"); + return false; + } + } + outSplit->configs.insert(configs.begin(), configs.end()); + return true; +} + class LinkCommand { public: LinkCommand(LinkContext* context, const LinkOptions& options) : @@ -676,6 +707,30 @@ public: appInfo.package = packageAttr->value; + if (xml::Attribute* versionCodeAttr = + manifestEl->findAttribute(xml::kSchemaAndroid, "versionCode")) { + Maybe<uint32_t> maybeCode = ResourceUtils::parseInt(versionCodeAttr->value); + if (!maybeCode) { + diag->error(DiagMessage(xmlRes->file.source.withLine(manifestEl->lineNumber)) + << "invalid android:versionCode '" + << versionCodeAttr->value << "'"); + return {}; + } + appInfo.versionCode = maybeCode.value(); + } + + if (xml::Attribute* revisionCodeAttr = + manifestEl->findAttribute(xml::kSchemaAndroid, "revisionCode")) { + Maybe<uint32_t> maybeCode = ResourceUtils::parseInt(revisionCodeAttr->value); + if (!maybeCode) { + diag->error(DiagMessage(xmlRes->file.source.withLine(manifestEl->lineNumber)) + << "invalid android:revisionCode '" + << revisionCodeAttr->value << "'"); + return {}; + } + appInfo.revisionCode = maybeCode.value(); + } + if (xml::Element* usesSdkEl = manifestEl->findChild({}, "uses-sdk")) { if (xml::Attribute* minSdk = usesSdkEl->findAttribute(xml::kSchemaAndroid, "minSdkVersion")) { @@ -767,11 +822,11 @@ public: return true; } - std::unique_ptr<IArchiveWriter> makeArchiveWriter() { + std::unique_ptr<IArchiveWriter> makeArchiveWriter(const StringPiece& out) { if (mOptions.outputToDirectory) { - return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath); + return createDirectoryArchiveWriter(mContext->getDiagnostics(), out); } else { - return createZipFileArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath); + return createZipFileArchiveWriter(mContext->getDiagnostics(), out); } } @@ -1179,6 +1234,94 @@ public: return true; } + std::unique_ptr<xml::XmlResource> generateSplitManifest(const AppInfo& appInfo, + const SplitConstraints& constraints) { + std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>(); + + std::unique_ptr<xml::Namespace> namespaceAndroid = util::make_unique<xml::Namespace>(); + namespaceAndroid->namespaceUri = xml::kSchemaAndroid; + namespaceAndroid->namespacePrefix = "android"; + + std::unique_ptr<xml::Element> manifestEl = util::make_unique<xml::Element>(); + manifestEl->name = "manifest"; + manifestEl->attributes.push_back( + xml::Attribute{ "", "package", appInfo.package }); + + if (appInfo.versionCode) { + manifestEl->attributes.push_back(xml::Attribute{ + xml::kSchemaAndroid, + "versionCode", + std::to_string(appInfo.versionCode.value()) }); + } + + if (appInfo.revisionCode) { + manifestEl->attributes.push_back(xml::Attribute{ + xml::kSchemaAndroid, + "revisionCode", std::to_string(appInfo.revisionCode.value()) }); + } + + std::stringstream splitName; + splitName << "config." << util::joiner(constraints.configs, "_"); + + manifestEl->attributes.push_back( + xml::Attribute{ "", "split", splitName.str() }); + + std::unique_ptr<xml::Element> applicationEl = util::make_unique<xml::Element>(); + applicationEl->name = "application"; + applicationEl->attributes.push_back( + xml::Attribute{ xml::kSchemaAndroid, "hasCode", "false" }); + + manifestEl->addChild(std::move(applicationEl)); + namespaceAndroid->addChild(std::move(manifestEl)); + doc->root = std::move(namespaceAndroid); + return doc; + } + + /** + * Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable + * to the IArchiveWriter. + */ + bool writeApk(IArchiveWriter* writer, proguard::KeepSet* keepSet, xml::XmlResource* manifest, + ResourceTable* table) { + const bool keepRawValues = mOptions.staticLib; + bool result = flattenXml(manifest, "AndroidManifest.xml", {}, keepRawValues, writer, + mContext); + if (!result) { + return false; + } + + ResourceFileFlattenerOptions fileFlattenerOptions; + fileFlattenerOptions.keepRawValues = keepRawValues; + fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything; + fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress; + fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion; + fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors; + fileFlattenerOptions.updateProguardSpec = + static_cast<bool>(mOptions.generateProguardRulesPath); + + ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, keepSet); + + if (!fileFlattener.flatten(table, writer)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources"); + return false; + } + + if (mOptions.staticLib) { + if (!flattenTableToPb(table, writer)) { + mContext->getDiagnostics()->error(DiagMessage() + << "failed to write resources.arsc.flat"); + return false; + } + } else { + if (!flattenTable(table, writer)) { + mContext->getDiagnostics()->error(DiagMessage() + << "failed to write resources.arsc"); + return false; + } + } + return true; + } + int run(const std::vector<std::string>& inputFiles) { // Load the AndroidManifest.xml std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath, @@ -1187,30 +1330,33 @@ public: return 1; } + // First extract the Package name without modifying it (via --rename-manifest-package). if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(), mContext->getDiagnostics())) { - AppInfo& appInfo = maybeAppInfo.value(); + const AppInfo& appInfo = maybeAppInfo.value(); mContext->setCompilationPackage(appInfo.package); - if (appInfo.minSdkVersion) { - if (Maybe<int> maybeMinSdkVersion = - ResourceUtils::tryParseSdkVersion(appInfo.minSdkVersion.value())) { - mContext->setMinSdkVersion(maybeMinSdkVersion.value()); - } - } - } else { + } + + ManifestFixer manifestFixer(mOptions.manifestFixerOptions); + if (!manifestFixer.consume(mContext, manifestXml.get())) { return 1; } - if (!util::isJavaPackageName(mContext->getCompilationPackage())) { - mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath) - << "invalid package name '" - << mContext->getCompilationPackage() - << "'"); + Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(), + mContext->getDiagnostics()); + if (!maybeAppInfo) { return 1; } - mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() }); + const AppInfo& appInfo = maybeAppInfo.value(); + if (appInfo.minSdkVersion) { + if (Maybe<int> maybeMinSdkVersion = + ResourceUtils::parseSdkVersion(appInfo.minSdkVersion.value())) { + mContext->setMinSdkVersion(maybeMinSdkVersion.value()); + } + } + mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() }); if (mContext->getCompilationPackage() == "android") { mContext->setPackageId(0x01); } else { @@ -1258,9 +1404,7 @@ public: DiagMessage() << "failed moving private attributes"); return 1; } - } - if (!mOptions.staticLib) { // Assign IDs if we are building a regular app. IdAssigner idAssigner(&mOptions.stableIdMap); if (!idAssigner.consume(mContext, &mFinalTable)) { @@ -1304,45 +1448,118 @@ public: mContext->getExternalSymbols()->prependSource( util::make_unique<ResourceTableSymbolSource>(&mFinalTable)); - { - ReferenceLinker linker; - if (!linker.consume(mContext, &mFinalTable)) { - mContext->getDiagnostics()->error(DiagMessage() << "failed linking references"); + ReferenceLinker linker; + if (!linker.consume(mContext, &mFinalTable)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed linking references"); + return 1; + } + + if (mOptions.staticLib) { + if (!mOptions.products.empty()) { + mContext->getDiagnostics()->warn( + DiagMessage() << "can't select products when building static library"); + } + } else { + ProductFilter productFilter(mOptions.products); + if (!productFilter.consume(mContext, &mFinalTable)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products"); + return 1; + } + } + + if (!mOptions.noAutoVersion) { + AutoVersioner versioner; + if (!versioner.consume(mContext, &mFinalTable)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles"); return 1; } + } + + if (!mOptions.staticLib && mContext->getMinSdkVersion() > 0) { + if (mContext->verbose()) { + mContext->getDiagnostics()->note( + DiagMessage() << "collapsing resource versions for minimum SDK " + << mContext->getMinSdkVersion()); + } - if (mOptions.staticLib) { - if (!mOptions.products.empty()) { - mContext->getDiagnostics()->warn( - DiagMessage() << "can't select products when building static library"); + VersionCollapser collapser; + if (!collapser.consume(mContext, &mFinalTable)) { + return 1; + } + } + + proguard::KeepSet proguardKeepSet; + proguard::KeepSet proguardMainDexKeepSet; + + if (mOptions.staticLib) { + if (mOptions.tableSplitterOptions.configFilter != nullptr || + mOptions.tableSplitterOptions.preferredDensity) { + mContext->getDiagnostics()->warn( + DiagMessage() << "can't strip resources when building static library"); + } + } else { + // Adjust the SplitConstraints so that their SDK version is stripped if it is less + // than or equal to the minSdk. Otherwise the resources that have had their SDK version + // stripped due to minSdk won't ever match. + std::vector<SplitConstraints> adjustedConstraintsList; + adjustedConstraintsList.reserve(mOptions.splitConstraints.size()); + for (const SplitConstraints& constraints : mOptions.splitConstraints) { + SplitConstraints adjustedConstraints; + for (const ConfigDescription& config : constraints.configs) { + if (config.sdkVersion <= mContext->getMinSdkVersion()) { + adjustedConstraints.configs.insert(config.copyWithoutSdkVersion()); + } else { + adjustedConstraints.configs.insert(config); + } } + adjustedConstraintsList.push_back(std::move(adjustedConstraints)); + } - if (mOptions.tableSplitterOptions.configFilter != nullptr || - mOptions.tableSplitterOptions.preferredDensity) { - mContext->getDiagnostics()->warn( - DiagMessage() << "can't strip resources when building static library"); + TableSplitter tableSplitter(adjustedConstraintsList, mOptions.tableSplitterOptions); + if (!tableSplitter.verifySplitConstraints(mContext)) { + return 1; + } + tableSplitter.splitTable(&mFinalTable); + + // Now we need to write out the Split APKs. + auto pathIter = mOptions.splitPaths.begin(); + auto splitConstraintsIter = adjustedConstraintsList.begin(); + for (std::unique_ptr<ResourceTable>& splitTable : tableSplitter.getSplits()) { + if (mContext->verbose()) { + mContext->getDiagnostics()->note( + DiagMessage(*pathIter) << "generating split with configurations '" + << util::joiner(splitConstraintsIter->configs, ", ") << "'"); } - } else { - ProductFilter productFilter(mOptions.products); - if (!productFilter.consume(mContext, &mFinalTable)) { - mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products"); + + std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(*pathIter); + if (!archiveWriter) { + mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive"); + return 1; + } + + // Generate an AndroidManifest.xml for each split. + std::unique_ptr<xml::XmlResource> splitManifest = + generateSplitManifest(appInfo, *splitConstraintsIter); + + XmlReferenceLinker linker; + if (!linker.consume(mContext, splitManifest.get())) { + mContext->getDiagnostics()->error( + DiagMessage() << "failed to create Split AndroidManifest.xml"); return 1; } - // TODO(adamlesinski): Actually pass in split constraints and handle splits at the file - // level. - TableSplitter tableSplitter({}, mOptions.tableSplitterOptions); - if (!tableSplitter.verifySplitConstraints(mContext)) { + if (!writeApk(archiveWriter.get(), &proguardKeepSet, splitManifest.get(), + splitTable.get())) { return 1; } - tableSplitter.splitTable(&mFinalTable); + + ++pathIter; + ++splitConstraintsIter; } } - proguard::KeepSet proguardKeepSet; - proguard::KeepSet proguardMainDexKeepSet; - - std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(); + // Start writing the base APK. + std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(mOptions.outputPath); if (!archiveWriter) { mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive"); return 1; @@ -1350,11 +1567,6 @@ public: bool error = false; { - ManifestFixer manifestFixer(mOptions.manifestFixerOptions); - if (!manifestFixer.consume(mContext, manifestXml.get())) { - error = true; - } - // AndroidManifest.xml has no resource name, but the CallSite is built from the name // (aka, which package the AndroidManifest.xml is coming from). // So we give it a package name so it can see local resources. @@ -1382,13 +1594,6 @@ public: error = true; } } - - const bool keepRawValues = mOptions.staticLib; - bool result = flattenXml(manifestXml.get(), "AndroidManifest.xml", {}, - keepRawValues, archiveWriter.get(), mContext); - if (!result) { - error = true; - } } else { error = true; } @@ -1399,58 +1604,10 @@ public: return 1; } - if (!mOptions.noAutoVersion) { - AutoVersioner versioner; - if (!versioner.consume(mContext, &mFinalTable)) { - mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles"); - return 1; - } - } - - if (!mOptions.staticLib && mContext->getMinSdkVersion() > 0) { - if (mContext->verbose()) { - mContext->getDiagnostics()->note( - DiagMessage() << "collapsing resource versions for minimum SDK " - << mContext->getMinSdkVersion()); - } - - VersionCollapser collapser; - if (!collapser.consume(mContext, &mFinalTable)) { - return 1; - } - } - - // Write out the table to an archive. Optimizations to the table should come before this - // step. - ResourceFileFlattenerOptions fileFlattenerOptions; - fileFlattenerOptions.keepRawValues = mOptions.staticLib; - fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything; - fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress; - fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion; - fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors; - fileFlattenerOptions.updateProguardSpec = - static_cast<bool>(mOptions.generateProguardRulesPath); - ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, &proguardKeepSet); - - if (!fileFlattener.flatten(&mFinalTable, archiveWriter.get())) { - mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources"); + if (!writeApk(archiveWriter.get(), &proguardKeepSet, manifestXml.get(), &mFinalTable)) { return 1; } - if (mOptions.staticLib) { - if (!flattenTableToPb(&mFinalTable, archiveWriter.get())) { - mContext->getDiagnostics()->error(DiagMessage() - << "failed to write resources.arsc.flat"); - return 1; - } - } else { - if (!flattenTable(&mFinalTable, archiveWriter.get())) { - mContext->getDiagnostics()->error(DiagMessage() - << "failed to write resources.arsc"); - return 1; - } - } - if (mOptions.generateJavaClassPath) { JavaClassGeneratorOptions options; options.types = JavaClassGeneratorOptions::SymbolTypes::kAll; @@ -1538,6 +1695,7 @@ int link(const std::vector<StringPiece>& args) { bool requireLocalization = false; bool verbose = false; Maybe<std::string> stableIdFilePath; + std::vector<std::string> splitArgs; Flags flags = Flags() .requiredFlag("-o", "Output path", &options.outputPath) .requiredFlag("--manifest", "Path to the Android manifest to build", @@ -1623,6 +1781,9 @@ int link(const std::vector<StringPiece>& args) { &options.manifestFixerOptions.renameInstrumentationTargetPackage) .optionalFlagList("-0", "File extensions not to compress", &options.extensionsToNotCompress) + .optionalFlagList("--split", "Split resources matching a set of configs out to a " + "Split APK.\nSyntax: path/to/output.apk:<config>[,<config>[...]]", + &splitArgs) .optionalSwitch("-v", "Enables verbose logging", &verbose); @@ -1741,6 +1902,16 @@ int link(const std::vector<StringPiece>& args) { ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"}); + // Parse the split parameters. + for (const std::string& splitArg : splitArgs) { + options.splitPaths.push_back({}); + options.splitConstraints.push_back({}); + if (!parseSplitParameter(splitArg, context.getDiagnostics(), &options.splitPaths.back(), + &options.splitConstraints.back())) { + return 1; + } + } + // Turn off auto versioning for static-libs. if (options.staticLib) { options.noAutoVersion = true; diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h index 55b587e57013..2e81266015e1 100644 --- a/tools/aapt2/link/ManifestFixer.h +++ b/tools/aapt2/link/ManifestFixer.h @@ -41,7 +41,7 @@ struct ManifestFixerOptions { */ class ManifestFixer : public IXmlResourceConsumer { public: - ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) { + explicit ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) { } bool consume(IAaptContext* context, xml::XmlResource* doc) override; diff --git a/tools/aapt2/link/ProductFilter.h b/tools/aapt2/link/ProductFilter.h index d2edd38289dc..7724e140427b 100644 --- a/tools/aapt2/link/ProductFilter.h +++ b/tools/aapt2/link/ProductFilter.h @@ -29,7 +29,7 @@ class ProductFilter { public: using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator; - ProductFilter(std::unordered_set<std::string> products) : mProducts(products) { } + explicit ProductFilter(std::unordered_set<std::string> products) : mProducts(products) { } ResourceConfigValueIter selectProductToKeep(const ResourceNameRef& name, const ResourceConfigValueIter begin, diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp index 6aa9c0edd059..be7aca3ca49f 100644 --- a/tools/aapt2/link/ReferenceLinker.cpp +++ b/tools/aapt2/link/ReferenceLinker.cpp @@ -138,7 +138,7 @@ private: const Attribute* attr) { if (RawString* rawString = valueCast<RawString>(value.get())) { std::unique_ptr<Item> transformed = - ResourceUtils::parseItemForAttribute(*rawString->value, attr); + ResourceUtils::tryParseItemForAttribute(*rawString->value, attr); // If we could not parse as any specific type, try a basic STRING. if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) { diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp index a29d8dcfe3c6..59ffe15fd4a4 100644 --- a/tools/aapt2/link/XmlReferenceLinker.cpp +++ b/tools/aapt2/link/XmlReferenceLinker.cpp @@ -106,7 +106,7 @@ public: } const Attribute* attribute = &attr.compiledAttribute.value().attribute; - attr.compiledValue = ResourceUtils::parseItemForAttribute(attr.value, + attr.compiledValue = ResourceUtils::tryParseItemForAttribute(attr.value, attribute); if (!attr.compiledValue && !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) { diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h index bd01b64281d5..bd31416a5cee 100644 --- a/tools/aapt2/process/SymbolTable.h +++ b/tools/aapt2/process/SymbolTable.h @@ -54,7 +54,7 @@ public: Symbol() : Symbol(Maybe<ResourceId>{}) { } - Symbol(const Maybe<ResourceId>& i) : Symbol(i, nullptr) { + explicit Symbol(const Maybe<ResourceId>& i) : Symbol(i, nullptr) { } Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr) : diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md index 95c0173f921a..a68d6f67324f 100644 --- a/tools/aapt2/readme.md +++ b/tools/aapt2/readme.md @@ -1,5 +1,14 @@ # Android Asset Packaging Tool 2.0 (AAPT2) release notes +## Version 2.1 +### `aapt2 link ...` +- Configuration Split APK support: supports splitting resources that match a set of + configurations to a separate APK which can be loaded alongside the base APK on + API 21+ devices. This is done using the flag + `--split path/to/split.apk:<config1>[,<config2>,...]`. +- SDK version resource filtering: Resources with an SDK version qualifier that is unreachable + at runtime due to the minimum SDK level declared by the AndroidManifest.xml are stripped. + ## Version 2.0 ### `aapt2 compile ...` - Pseudo-localization: generates pseudolocalized versions of default strings when the diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp index 2dfe2a239900..08b9ee9cbe1b 100644 --- a/tools/aapt2/split/TableSplitter.cpp +++ b/tools/aapt2/split/TableSplitter.cpp @@ -17,6 +17,7 @@ #include "ConfigDescription.h" #include "ResourceTable.h" #include "split/TableSplitter.h" +#include "util/Util.h" #include <algorithm> #include <map> @@ -76,7 +77,6 @@ public: // in multiple splits. const ConfigDescription& config = entry.first; const std::vector<ResourceConfigValue*>& relatedValues = entry.second; - auto densityValueIter = mDensityDependentConfigToDensityMap.find(config); if (densityValueIter != mDensityDependentConfigToDensityMap.end()) { // Select the best one! @@ -89,12 +89,12 @@ public: thisValue->config.isBetterThan(bestValue->config, &targetDensity)) { bestValue = thisValue; } - - // When we select one of these, they are all claimed such that the base - // doesn't include any anymore. - (*claimedValues)[thisValue] = true; } assert(bestValue); + + // When we select one of these, they are all claimed such that the base + // doesn't include any anymore. + (*claimedValues)[bestValue] = true; selected.push_back(bestValue); } } @@ -135,7 +135,6 @@ static void markNonPreferredDensitiesAsClaimed(uint16_t preferredDensity, assert(bestValue); } } - bool TableSplitter::verifySplitConstraints(IAaptContext* context) { bool error = false; for (size_t i = 0; i < mSplitConstraints.size(); i++) { diff --git a/tools/aapt2/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h index 15e0764c4259..2fa5c47d5bd6 100644 --- a/tools/aapt2/split/TableSplitter.h +++ b/tools/aapt2/split/TableSplitter.h @@ -60,7 +60,7 @@ public: void splitTable(ResourceTable* originalTable); - const std::vector<std::unique_ptr<ResourceTable>>& getSplits() { + std::vector<std::unique_ptr<ResourceTable>>& getSplits() { return mSplits; } diff --git a/tools/aapt2/split/TableSplitter_test.cpp b/tools/aapt2/split/TableSplitter_test.cpp index bad02a5cb332..5150e82b6d93 100644 --- a/tools/aapt2/split/TableSplitter_test.cpp +++ b/tools/aapt2/split/TableSplitter_test.cpp @@ -52,6 +52,71 @@ TEST(TableSplitterTest, NoSplitPreferredDensity) { EXPECT_NE(nullptr, test::getValue<Id>(table.get(), "android:string/one")); } +TEST(TableSplitterTest, SplitTableByDensity) { + std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() + .addFileReference("android:drawable/foo", "res/drawable-mdpi/foo.png", + test::parseConfigOrDie("mdpi")) + .addFileReference("android:drawable/foo", "res/drawable-hdpi/foo.png", + test::parseConfigOrDie("hdpi")) + .addFileReference("android:drawable/foo", "res/drawable-xhdpi/foo.png", + test::parseConfigOrDie("xhdpi")) + .addFileReference("android:drawable/foo", "res/drawable-xxhdpi/foo.png", + test::parseConfigOrDie("xxhdpi")) + .build(); + + std::vector<SplitConstraints> constraints; + constraints.push_back(SplitConstraints{ { test::parseConfigOrDie("mdpi") } }); + constraints.push_back(SplitConstraints{ { test::parseConfigOrDie("hdpi") } }); + constraints.push_back(SplitConstraints{ { test::parseConfigOrDie("xhdpi") } }); + + TableSplitter splitter(constraints, TableSplitterOptions{}); + splitter.splitTable(table.get()); + + ASSERT_EQ(3u, splitter.getSplits().size()); + + ResourceTable* splitOne = splitter.getSplits()[0].get(); + ResourceTable* splitTwo = splitter.getSplits()[1].get(); + ResourceTable* splitThree = splitter.getSplits()[2].get(); + + // Just xxhdpi should be in the base. + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(), "android:drawable/foo", + test::parseConfigOrDie("mdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(), "android:drawable/foo", + test::parseConfigOrDie("hdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(), "android:drawable/foo", + test::parseConfigOrDie("xhdpi"))); + EXPECT_NE(nullptr, test::getValueForConfig<FileReference>(table.get(), "android:drawable/foo", + test::parseConfigOrDie("xxhdpi"))); + + // Each split should have one and only one drawable. + EXPECT_NE(nullptr, test::getValueForConfig<FileReference>(splitOne, "android:drawable/foo", + test::parseConfigOrDie("mdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitOne, "android:drawable/foo", + test::parseConfigOrDie("hdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitOne, "android:drawable/foo", + test::parseConfigOrDie("xhdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitOne, "android:drawable/foo", + test::parseConfigOrDie("xxhdpi"))); + + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitTwo, "android:drawable/foo", + test::parseConfigOrDie("mdpi"))); + EXPECT_NE(nullptr, test::getValueForConfig<FileReference>(splitTwo, "android:drawable/foo", + test::parseConfigOrDie("hdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitTwo, "android:drawable/foo", + test::parseConfigOrDie("xhdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitTwo, "android:drawable/foo", + test::parseConfigOrDie("xxhdpi"))); + + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitThree, "android:drawable/foo", + test::parseConfigOrDie("mdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitThree, "android:drawable/foo", + test::parseConfigOrDie("hdpi"))); + EXPECT_NE(nullptr, test::getValueForConfig<FileReference>(splitThree, "android:drawable/foo", + test::parseConfigOrDie("xhdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitThree, "android:drawable/foo", + test::parseConfigOrDie("xxhdpi"))); +} + TEST(TableSplitterTest, SplitTableByConfigAndDensity) { ResourceTable table; @@ -78,12 +143,12 @@ TEST(TableSplitterTest, SplitTableByConfigAndDensity) { ResourceTable* splitOne = splitter.getSplits()[0].get(); ResourceTable* splitTwo = splitter.getSplits()[1].get(); - // Since a split was defined, all densities should be gone from base. + // All but the xxhdpi resource should be gone, since there were closer matches in land-xhdpi. EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, "android:string/foo", test::parseConfigOrDie("land-hdpi"))); EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, "android:string/foo", test::parseConfigOrDie("land-xhdpi"))); - EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, "android:string/foo", + EXPECT_NE(nullptr, test::getValueForConfig<Id>(&table, "android:string/foo", test::parseConfigOrDie("land-xxhdpi"))); EXPECT_NE(nullptr, test::getValueForConfig<Id>(splitOne, "android:string/foo", diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index c0c016032921..637e991a573f 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -46,12 +46,12 @@ public: return *this; } - ResourceTableBuilder& addSimple(const StringPiece& name, const ResourceId id = {}) { + ResourceTableBuilder& addSimple(const StringPiece& name, const ResourceId& id = {}) { return addValue(name, id, util::make_unique<Id>()); } ResourceTableBuilder& addSimple(const StringPiece& name, const ConfigDescription& config, - const ResourceId id = {}) { + const ResourceId& id = {}) { return addValue(name, config, id, util::make_unique<Id>()); } @@ -59,7 +59,7 @@ public: return addReference(name, {}, ref); } - ResourceTableBuilder& addReference(const StringPiece& name, const ResourceId id, + ResourceTableBuilder& addReference(const StringPiece& name, const ResourceId& id, const StringPiece& ref) { return addValue(name, id, util::make_unique<Reference>(parseNameOrDie(ref))); } @@ -68,12 +68,12 @@ public: return addString(name, {}, str); } - ResourceTableBuilder& addString(const StringPiece& name, const ResourceId id, + ResourceTableBuilder& addString(const StringPiece& name, const ResourceId& id, const StringPiece& str) { return addValue(name, id, util::make_unique<String>(mTable->stringPool.makeRef(str))); } - ResourceTableBuilder& addString(const StringPiece& name, const ResourceId id, + ResourceTableBuilder& addString(const StringPiece& name, const ResourceId& id, const ConfigDescription& config, const StringPiece& str) { return addValue(name, config, id, util::make_unique<String>(mTable->stringPool.makeRef(str))); @@ -83,7 +83,7 @@ public: return addFileReference(name, {}, path); } - ResourceTableBuilder& addFileReference(const StringPiece& name, const ResourceId id, + ResourceTableBuilder& addFileReference(const StringPiece& name, const ResourceId& id, const StringPiece& path) { return addValue(name, id, util::make_unique<FileReference>(mTable->stringPool.makeRef(path))); @@ -100,13 +100,13 @@ public: return addValue(name, {}, std::move(value)); } - ResourceTableBuilder& addValue(const StringPiece& name, const ResourceId id, + ResourceTableBuilder& addValue(const StringPiece& name, const ResourceId& id, std::unique_ptr<Value> value) { return addValue(name, {}, id, std::move(value)); } ResourceTableBuilder& addValue(const StringPiece& name, const ConfigDescription& config, - const ResourceId id, std::unique_ptr<Value> value) { + const ResourceId& id, std::unique_ptr<Value> value) { ResourceName resName = parseNameOrDie(name); bool result = mTable->addResourceAllowMangled(resName, id, config, {}, std::move(value), &mDiagnostics); @@ -114,7 +114,7 @@ public: return *this; } - ResourceTableBuilder& setSymbolState(const StringPiece& name, ResourceId id, + ResourceTableBuilder& setSymbolState(const StringPiece& name, const ResourceId& id, SymbolState state) { ResourceName resName = parseNameOrDie(name); Symbol symbol; @@ -130,7 +130,7 @@ public: }; inline std::unique_ptr<Reference> buildReference(const StringPiece& ref, - Maybe<ResourceId> id = {}) { + const Maybe<ResourceId>& id = {}) { std::unique_ptr<Reference> reference = util::make_unique<Reference>(parseNameOrDie(ref)); reference->id = id; return reference; @@ -151,7 +151,7 @@ private: public: template <typename... Args> - ValueBuilder(Args&&... args) : mValue(new T{ std::forward<Args>(args)... }) { + explicit ValueBuilder(Args&&... args) : mValue(new T{ std::forward<Args>(args)... }) { } template <typename... Args> @@ -175,7 +175,7 @@ private: std::unique_ptr<Attribute> mAttr; public: - AttributeBuilder(bool weak = false) : mAttr(util::make_unique<Attribute>(weak)) { + explicit AttributeBuilder(bool weak = false) : mAttr(util::make_unique<Attribute>(weak)) { mAttr->typeMask = android::ResTable_map::TYPE_ANY; } @@ -211,7 +211,7 @@ public: return *this; } - StyleBuilder& addItem(const StringPiece& str, ResourceId id, std::unique_ptr<Item> value) { + StyleBuilder& addItem(const StringPiece& str, const ResourceId& id, std::unique_ptr<Item> value) { addItem(str, std::move(value)); mStyle->entries.back().key.id = id; return *this; @@ -227,7 +227,7 @@ private: std::unique_ptr<Styleable> mStyleable = util::make_unique<Styleable>(); public: - StyleableBuilder& addItem(const StringPiece& str, Maybe<ResourceId> id = {}) { + StyleableBuilder& addItem(const StringPiece& str, const Maybe<ResourceId>& id = {}) { mStyleable->entries.push_back(Reference(parseNameOrDie(str))); mStyleable->entries.back().id = id; return *this; diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h index 624a71a382c9..7fafcbeb1a04 100644 --- a/tools/aapt2/test/Common.h +++ b/tools/aapt2/test/Common.h @@ -104,7 +104,7 @@ private: Source mSource; public: - TestFile(const StringPiece& path) : mSource(path) {} + explicit TestFile(const StringPiece& path) : mSource(path) {} std::unique_ptr<io::IData> openAsData() override { return {}; diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h index b053e07eafde..54f16db3d2f9 100644 --- a/tools/aapt2/test/Context.h +++ b/tools/aapt2/test/Context.h @@ -87,7 +87,7 @@ public: return *this; } - ContextBuilder& setNameManglerPolicy(NameManglerPolicy policy) { + ContextBuilder& setNameManglerPolicy(const NameManglerPolicy& policy) { mContext->mNameMangler = NameMangler(policy); return *this; } diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h index cad2a2e63be1..ba8532f829e6 100644 --- a/tools/aapt2/util/BigBuffer.h +++ b/tools/aapt2/util/BigBuffer.h @@ -63,7 +63,7 @@ public: * Create a BigBuffer with block allocation sizes * of blockSize. */ - BigBuffer(size_t blockSize); + explicit BigBuffer(size_t blockSize); BigBuffer(const BigBuffer&) = delete; // No copying. diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h index 4d8a1feb63b1..52c2052aec3d 100644 --- a/tools/aapt2/util/Files.h +++ b/tools/aapt2/util/Files.h @@ -109,7 +109,7 @@ bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outAr */ class FileFilter { public: - FileFilter(IDiagnostics* diag) : mDiag(diag) { + explicit FileFilter(IDiagnostics* diag) : mDiag(diag) { } /* diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h index a31bc89fa98b..129f6d9d9716 100644 --- a/tools/aapt2/util/Maybe.h +++ b/tools/aapt2/util/Maybe.h @@ -43,12 +43,12 @@ public: Maybe(const Maybe& rhs); template <typename U> - Maybe(const Maybe<U>& rhs); + Maybe(const Maybe<U>& rhs); // NOLINT(implicit) Maybe(Maybe&& rhs); template <typename U> - Maybe(Maybe<U>&& rhs); + Maybe(Maybe<U>&& rhs); // NOLINT(implicit) Maybe& operator=(const Maybe& rhs); @@ -63,12 +63,12 @@ public: /** * Construct a Maybe holding a value. */ - Maybe(const T& value); + Maybe(const T& value); // NOLINT(implicit) /** * Construct a Maybe holding a value. */ - Maybe(T&& value); + Maybe(T&& value); // NOLINT(implicit) /** * True if this holds a value, false if diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h index f5c985bd9fff..4300a67d3581 100644 --- a/tools/aapt2/util/StringPiece.h +++ b/tools/aapt2/util/StringPiece.h @@ -41,8 +41,8 @@ public: BasicStringPiece(); BasicStringPiece(const BasicStringPiece<TChar>& str); - BasicStringPiece(const std::basic_string<TChar>& str); - BasicStringPiece(const TChar* str); + BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(implicit) + BasicStringPiece(const TChar* str); // NOLINT(implicit) BasicStringPiece(const TChar* str, size_t len); BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs); diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index 4a10987b4400..998ecf7702bd 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -101,12 +101,16 @@ std::unique_ptr<T> make_unique(Args&&... args) { * Writes a set of items to the std::ostream, joining the times with the provided * separator. */ -template <typename Iterator> -::std::function<::std::ostream&(::std::ostream&)> joiner(Iterator begin, Iterator end, - const char* sep) { - return [begin, end, sep](::std::ostream& out) -> ::std::ostream& { - for (auto iter = begin; iter != end; ++iter) { - if (iter != begin) { +template <typename Container> +::std::function<::std::ostream&(::std::ostream&)> joiner(const Container& container, + const char* sep) { + using std::begin; + using std::end; + const auto beginIter = begin(container); + const auto endIter = end(container); + return [beginIter, endIter, sep](::std::ostream& out) -> ::std::ostream& { + for (auto iter = beginIter; iter != endIter; ++iter) { + if (iter != beginIter) { out << sep; } out << *iter; @@ -242,7 +246,7 @@ private: const iterator mEnd; }; -inline Tokenizer tokenize(StringPiece str, char sep) { +inline Tokenizer tokenize(const StringPiece& str, char sep) { return Tokenizer(str, sep); } @@ -281,7 +285,7 @@ bool extractResFilePathParts(const StringPiece& path, StringPiece* outPrefix, * In the aapt namespace for lookup. */ inline ::std::ostream& operator<<(::std::ostream& out, - ::std::function<::std::ostream&(::std::ostream&)> f) { + const ::std::function<::std::ostream&(::std::ostream&)>& f) { return f(out); } diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h index ee51b360feb4..a24d109abf34 100644 --- a/tools/aapt2/xml/XmlPullParser.h +++ b/tools/aapt2/xml/XmlPullParser.h @@ -60,7 +60,7 @@ public: static bool skipCurrentElement(XmlPullParser* parser); static bool isGoodEvent(Event event); - XmlPullParser(std::istream& in); + explicit XmlPullParser(std::istream& in); ~XmlPullParser(); /** diff --git a/tools/split-select/SplitSelector.h b/tools/split-select/SplitSelector.h index 193fda7077d3..120354f608f3 100644 --- a/tools/split-select/SplitSelector.h +++ b/tools/split-select/SplitSelector.h @@ -29,7 +29,7 @@ namespace split { class SplitSelector { public: SplitSelector(); - SplitSelector(const android::Vector<SplitDescription>& splits); + explicit SplitSelector(const android::Vector<SplitDescription>& splits); android::Vector<SplitDescription> getBestSplits(const SplitDescription& target) const; |