From 458b877488c12ea4336d8fc00a95d9c0298bd6d0 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 25 Apr 2016 14:20:21 -0700 Subject: [PATCH] AAPT2: Add diff command Adds the diff command and various small fixes to issues discovered when diffing old AAPT built APKs with new AAPT2 built APKS. Bug:22775504 Change-Id: I682a7fe1cf4b3efa7cbd5d18b333cf2d1046fe1b --- tools/aapt2/Android.mk | 1 + tools/aapt2/Main.cpp | 5 +- tools/aapt2/ResourceParser.cpp | 12 +- tools/aapt2/ResourceTable.cpp | 11 + tools/aapt2/ResourceTable.h | 4 + tools/aapt2/ResourceUtils.cpp | 2 +- tools/aapt2/ResourceValues.cpp | 172 +++++++- tools/aapt2/ResourceValues.h | 43 +- tools/aapt2/ValueVisitor.h | 6 +- tools/aapt2/compile/PseudolocaleGenerator.cpp | 41 +- tools/aapt2/diff/Diff.cpp | 411 ++++++++++++++++++ tools/aapt2/proto/TableProtoDeserializer.cpp | 2 + 12 files changed, 649 insertions(+), 61 deletions(-) create mode 100644 tools/aapt2/diff/Diff.cpp diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk index 3a1e2bb3bf081..4b5ea65d8fedf 100644 --- a/tools/aapt2/Android.mk +++ b/tools/aapt2/Android.mk @@ -115,6 +115,7 @@ testSources := \ toolSources := \ compile/Compile.cpp \ + diff/Diff.cpp \ dump/Dump.cpp \ link/Link.cpp diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index a2fadd95db3f4..00d8aaeeda55e 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -24,6 +24,7 @@ namespace aapt { extern int compile(const std::vector& args); extern int link(const std::vector& args); extern int dump(const std::vector& args); +extern int diff(const std::vector& args); } // namespace aapt @@ -44,12 +45,14 @@ int main(int argc, char** argv) { return aapt::link(args); } else if (command == "dump" || command == "d") { return aapt::dump(args); + } else if (command == "diff") { + return aapt::diff(args); } std::cerr << "unknown command '" << command << "'\n"; } else { std::cerr << "no command specified\n"; } - std::cerr << "\nusage: aapt2 [compile|link|dump] ..." << std::endl; + std::cerr << "\nusage: aapt2 [compile|link|dump|diff] ..." << std::endl; return 1; } diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 9704d97029b79..a84c306e2733e 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -152,7 +152,7 @@ bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::u16strin break; } - spanStack.back().lastChar = builder.str().size(); + spanStack.back().lastChar = builder.str().size() - 1; outStyleString->spans.push_back(spanStack.back()); spanStack.pop_back(); @@ -1058,6 +1058,16 @@ bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* std::unique_ptr array = util::make_unique(); + bool translateable = mOptions.translatable; + if (Maybe translateableAttr = xml::findAttribute(parser, u"translatable")) { + if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { + mDiag->error(DiagMessage(outResource->source) + << "invalid value for 'translatable'. Must be a boolean"); + return false; + } + } + array->setTranslateable(translateable); + bool error = false; const size_t depth = parser->getDepth(); while (xml::XmlPullParser::nextChildNode(parser, depth)) { diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index 8d734f3fc36d8..e700ed98365a3 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -189,6 +189,17 @@ std::vector ResourceEntry::findAllValues(const ConfigDescr return results; } +std::vector ResourceEntry::findValuesIf( + const std::function& f) { + std::vector results; + for (auto& configValue : values) { + if (f(configValue.get())) { + results.push_back(configValue.get()); + } + } + return results; +} + /** * The default handler for collisions. A return value of -1 means keep the * existing value, 0 means fail, and +1 means take the incoming value. diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 7f5c2b8c0f370..5690ea6fa614f 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -26,6 +26,7 @@ #include "io/File.h" #include +#include #include #include #include @@ -109,6 +110,9 @@ public: ResourceConfigValue* findOrCreateValue(const ConfigDescription& config, const StringPiece& product); std::vector findAllValues(const ConfigDescription& config); + std::vector findValuesIf( + const std::function& f); + private: DISALLOW_COPY_AND_ASSIGN(ResourceEntry); diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 74c48b0f8426d..a0a7efc464767 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -289,7 +289,7 @@ std::unique_ptr tryParseEnumSymbol(const Attribute* enumAttr, std::unique_ptr tryParseFlagSymbol(const Attribute* flagAttr, const StringPiece16& str) { android::Res_value flags = { }; - flags.dataType = android::Res_value::TYPE_INT_DEC; + flags.dataType = android::Res_value::TYPE_INT_HEX; flags.data = 0u; if (util::trimWhitespace(str).empty()) { diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp index dd7ff013e5246..c10b134cb36ec 100644 --- a/tools/aapt2/ResourceValues.cpp +++ b/tools/aapt2/ResourceValues.cpp @@ -39,6 +39,14 @@ void BaseItem::accept(RawValueVisitor* visitor) { RawString::RawString(const StringPool::Ref& ref) : value(ref) { } +bool RawString::equals(const Value* value) const { + const RawString* other = valueCast(value); + if (!other) { + return false; + } + return *this->value == *other->value; +} + RawString* RawString::clone(StringPool* newPool) const { RawString* rs = new RawString(newPool->makeRef(*value)); rs->mComment = mComment; @@ -66,6 +74,15 @@ Reference::Reference(const ResourceNameRef& n, Type t) : Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type) { } +bool Reference::equals(const Value* value) const { + const Reference* other = valueCast(value); + if (!other) { + return false; + } + return referenceType == other->referenceType && privateReference == other->privateReference && + id == other->id && name == other->name; +} + bool Reference::flatten(android::Res_value* outValue) const { outValue->dataType = (referenceType == Reference::Type::kResource) ? android::Res_value::TYPE_REFERENCE : android::Res_value::TYPE_ATTRIBUTE; @@ -97,6 +114,10 @@ void Reference::print(std::ostream* out) const { } } +bool Id::equals(const Value* value) const { + return valueCast(value) != nullptr; +} + bool Id::flatten(android::Res_value* out) const { out->dataType = android::Res_value::TYPE_INT_BOOLEAN; out->data = util::hostToDevice32(0); @@ -111,15 +132,15 @@ void Id::print(std::ostream* out) const { *out << "(id)"; } -String::String(const StringPool::Ref& ref) : value(ref), mTranslateable(true) { +String::String(const StringPool::Ref& ref) : value(ref) { } -void String::setTranslateable(bool val) { - mTranslateable = val; -} - -bool String::isTranslateable() const { - return mTranslateable; +bool String::equals(const Value* value) const { + const String* other = valueCast(value); + if (!other) { + return false; + } + return *this->value == *other->value; } bool String::flatten(android::Res_value* outValue) const { @@ -144,15 +165,24 @@ void String::print(std::ostream* out) const { *out << "(string) \"" << *value << "\""; } -StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref), mTranslateable(true) { +StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) { } -void StyledString::setTranslateable(bool val) { - mTranslateable = val; -} +bool StyledString::equals(const Value* value) const { + const StyledString* other = valueCast(value); + if (!other) { + return false; + } -bool StyledString::isTranslateable() const { - return mTranslateable; + if (*this->value->str == *other->value->str) { + const std::vector& spansA = this->value->spans; + const std::vector& spansB = other->value->spans; + return std::equal(spansA.begin(), spansA.end(), spansB.begin(), + [](const StringPool::Span& a, const StringPool::Span& b) -> bool { + return *a.name == *b.name && a.firstChar == b.firstChar && a.lastChar == b.lastChar; + }); + } + return false; } bool StyledString::flatten(android::Res_value* outValue) const { @@ -174,11 +204,22 @@ StyledString* StyledString::clone(StringPool* newPool) const { void StyledString::print(std::ostream* out) const { *out << "(styled string) \"" << *value->str << "\""; + for (const StringPool::Span& span : value->spans) { + *out << " "<< *span.name << ":" << span.firstChar << "," << span.lastChar; + } } FileReference::FileReference(const StringPool::Ref& _path) : path(_path) { } +bool FileReference::equals(const Value* value) const { + const FileReference* other = valueCast(value); + if (!other) { + return false; + } + return *path == *other->path; +} + bool FileReference::flatten(android::Res_value* outValue) const { if (path.getIndex() > std::numeric_limits::max()) { return false; @@ -209,6 +250,14 @@ BinaryPrimitive::BinaryPrimitive(uint8_t dataType, uint32_t data) { value.data = data; } +bool BinaryPrimitive::equals(const Value* value) const { + const BinaryPrimitive* other = valueCast(value); + if (!other) { + return false; + } + return this->value.dataType == other->value.dataType && this->value.data == other->value.data; +} + bool BinaryPrimitive::flatten(android::Res_value* outValue) const { outValue->dataType = value.dataType; outValue->data = util::hostToDevice32(value.data); @@ -228,7 +277,7 @@ void BinaryPrimitive::print(std::ostream* out) const { *out << "(integer) " << static_cast(value.data); break; case android::Res_value::TYPE_INT_HEX: - *out << "(integer) " << std::hex << value.data << std::dec; + *out << "(integer) 0x" << std::hex << value.data << std::dec; break; case android::Res_value::TYPE_INT_BOOLEAN: *out << "(boolean) " << (value.data != 0 ? "true" : "false"); @@ -253,6 +302,21 @@ Attribute::Attribute(bool w, uint32_t t) : mWeak = w; } +bool Attribute::equals(const Value* value) const { + const Attribute* other = valueCast(value); + if (!other) { + return false; + } + + return this->typeMask == other->typeMask && this->minInt == other->minInt && + this->maxInt == other->maxInt && + std::equal(this->symbols.begin(), this->symbols.end(), + other->symbols.begin(), + [](const Symbol& a, const Symbol& b) -> bool { + return a.symbol.equals(&b.symbol) && a.value == b.value; + }); +} + Attribute* Attribute::clone(StringPool* /*newPool*/) const { return new Attribute(*this); } @@ -365,6 +429,14 @@ void Attribute::print(std::ostream* out) const { << "]"; } + if (minInt != std::numeric_limits::min()) { + *out << " min=" << minInt; + } + + if (maxInt != std::numeric_limits::max()) { + *out << " max=" << maxInt; + } + if (isWeak()) { *out << " [weak]"; } @@ -445,6 +517,21 @@ bool Attribute::matches(const Item* item, DiagMessage* outMsg) const { return true; } +bool Style::equals(const Value* value) const { + const Style* other = valueCast