Merge "AAPT2: Add diff command" into nyc-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
1a1461058e
@@ -115,6 +115,7 @@ testSources := \
|
||||
|
||||
toolSources := \
|
||||
compile/Compile.cpp \
|
||||
diff/Diff.cpp \
|
||||
dump/Dump.cpp \
|
||||
link/Link.cpp
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace aapt {
|
||||
extern int compile(const std::vector<StringPiece>& args);
|
||||
extern int link(const std::vector<StringPiece>& args);
|
||||
extern int dump(const std::vector<StringPiece>& args);
|
||||
extern int diff(const std::vector<StringPiece>& 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;
|
||||
}
|
||||
|
||||
@@ -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> array = util::make_unique<Array>();
|
||||
|
||||
bool translateable = mOptions.translatable;
|
||||
if (Maybe<StringPiece16> 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)) {
|
||||
|
||||
@@ -189,6 +189,17 @@ std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(const ConfigDescr
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector<ResourceConfigValue*> ResourceEntry::findValuesIf(
|
||||
const std::function<bool(ResourceConfigValue*)>& f) {
|
||||
std::vector<ResourceConfigValue*> 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.
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "io/File.h"
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -109,6 +110,9 @@ public:
|
||||
ResourceConfigValue* findOrCreateValue(const ConfigDescription& config,
|
||||
const StringPiece& product);
|
||||
std::vector<ResourceConfigValue*> findAllValues(const ConfigDescription& config);
|
||||
std::vector<ResourceConfigValue*> findValuesIf(
|
||||
const std::function<bool(ResourceConfigValue*)>& f);
|
||||
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ResourceEntry);
|
||||
|
||||
@@ -289,7 +289,7 @@ std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
|
||||
std::unique_ptr<BinaryPrimitive> 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()) {
|
||||
|
||||
@@ -39,6 +39,14 @@ void BaseItem<Derived>::accept(RawValueVisitor* visitor) {
|
||||
RawString::RawString(const StringPool::Ref& ref) : value(ref) {
|
||||
}
|
||||
|
||||
bool RawString::equals(const Value* value) const {
|
||||
const RawString* other = valueCast<RawString>(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<Reference>(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<Id>(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<String>(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<StyledString>(value);
|
||||
if (!other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StyledString::isTranslateable() const {
|
||||
return mTranslateable;
|
||||
if (*this->value->str == *other->value->str) {
|
||||
const std::vector<StringPool::Span>& spansA = this->value->spans;
|
||||
const std::vector<StringPool::Span>& 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<FileReference>(value);
|
||||
if (!other) {
|
||||
return false;
|
||||
}
|
||||
return *path == *other->path;
|
||||
}
|
||||
|
||||
bool FileReference::flatten(android::Res_value* outValue) const {
|
||||
if (path.getIndex() > std::numeric_limits<uint32_t>::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<BinaryPrimitive>(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<int32_t>(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<Attribute>(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<int32_t>::min()) {
|
||||
*out << " min=" << minInt;
|
||||
}
|
||||
|
||||
if (maxInt != std::numeric_limits<int32_t>::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<Style>(value);
|
||||
if (!other) {
|
||||
return false;
|
||||
}
|
||||
if (bool(parent) != bool(other->parent) ||
|
||||
(parent && other->parent && !parent.value().equals(&other->parent.value()))) {
|
||||
return false;
|
||||
}
|
||||
return std::equal(entries.begin(), entries.end(), other->entries.begin(),
|
||||
[](const Entry& a, const Entry& b) -> bool {
|
||||
return a.key.equals(&b.key) && a.value->equals(b.value.get());
|
||||
});
|
||||
}
|
||||
|
||||
Style* Style::clone(StringPool* newPool) const {
|
||||
Style* style = new Style();
|
||||
style->parent = parent;
|
||||
@@ -484,6 +571,18 @@ static ::std::ostream& operator<<(::std::ostream& out, const Style::Entry& value
|
||||
return out;
|
||||
}
|
||||
|
||||
bool Array::equals(const Value* value) const {
|
||||
const Array* other = valueCast<Array>(value);
|
||||
if (!other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::equal(items.begin(), items.end(), other->items.begin(),
|
||||
[](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
|
||||
return a->equals(b.get());
|
||||
});
|
||||
}
|
||||
|
||||
Array* Array::clone(StringPool* newPool) const {
|
||||
Array* array = new Array();
|
||||
array->mComment = mComment;
|
||||
@@ -500,6 +599,21 @@ void Array::print(std::ostream* out) const {
|
||||
<< "]";
|
||||
}
|
||||
|
||||
bool Plural::equals(const Value* value) const {
|
||||
const Plural* other = valueCast<Plural>(value);
|
||||
if (!other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::equal(values.begin(), values.end(), other->values.begin(),
|
||||
[](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
|
||||
if (bool(a) != bool(b)) {
|
||||
return false;
|
||||
}
|
||||
return bool(a) == bool(b) || a->equals(b.get());
|
||||
});
|
||||
}
|
||||
|
||||
Plural* Plural::clone(StringPool* newPool) const {
|
||||
Plural* p = new Plural();
|
||||
p->mComment = mComment;
|
||||
@@ -515,12 +629,42 @@ Plural* Plural::clone(StringPool* newPool) const {
|
||||
|
||||
void Plural::print(std::ostream* out) const {
|
||||
*out << "(plural)";
|
||||
if (values[Zero]) {
|
||||
*out << " zero=" << *values[Zero];
|
||||
}
|
||||
|
||||
if (values[One]) {
|
||||
*out << " one=" << *values[One];
|
||||
}
|
||||
|
||||
if (values[Two]) {
|
||||
*out << " two=" << *values[Two];
|
||||
}
|
||||
|
||||
if (values[Few]) {
|
||||
*out << " few=" << *values[Few];
|
||||
}
|
||||
|
||||
if (values[Many]) {
|
||||
*out << " many=" << *values[Many];
|
||||
}
|
||||
}
|
||||
|
||||
static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Item>& item) {
|
||||
return out << *item;
|
||||
}
|
||||
|
||||
bool Styleable::equals(const Value* value) const {
|
||||
const Styleable* other = valueCast<Styleable>(value);
|
||||
if (!other) {
|
||||
return false;
|
||||
}
|
||||
return std::equal(entries.begin(), entries.end(), other->entries.begin(),
|
||||
[](const Reference& a, const Reference& b) -> bool {
|
||||
return a.equals(&b);
|
||||
});
|
||||
}
|
||||
|
||||
Styleable* Styleable::clone(StringPool* /*newPool*/) const {
|
||||
return new Styleable(*this);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,18 @@ struct Value {
|
||||
mWeak = val;
|
||||
}
|
||||
|
||||
// Whether the value is marked as translateable.
|
||||
// This does not persist when flattened.
|
||||
// It is only used during compilation phase.
|
||||
void setTranslateable(bool val) {
|
||||
mTranslateable = val;
|
||||
}
|
||||
|
||||
// Default true.
|
||||
bool isTranslateable() const {
|
||||
return mTranslateable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source where this value was defined.
|
||||
*/
|
||||
@@ -84,6 +96,8 @@ struct Value {
|
||||
mComment = std::move(str);
|
||||
}
|
||||
|
||||
virtual bool equals(const Value* value) const = 0;
|
||||
|
||||
/**
|
||||
* Calls the appropriate overload of ValueVisitor.
|
||||
*/
|
||||
@@ -103,6 +117,7 @@ protected:
|
||||
Source mSource;
|
||||
std::u16string mComment;
|
||||
bool mWeak = false;
|
||||
bool mTranslateable = true;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -158,6 +173,7 @@ struct Reference : public BaseItem<Reference> {
|
||||
explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
|
||||
explicit Reference(const ResourceId& i, Type type = Type::kResource);
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
bool flatten(android::Res_value* outValue) const override;
|
||||
Reference* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
@@ -168,6 +184,7 @@ struct Reference : public BaseItem<Reference> {
|
||||
*/
|
||||
struct Id : public BaseItem<Id> {
|
||||
Id() { mWeak = true; }
|
||||
bool equals(const Value* value) const override;
|
||||
bool flatten(android::Res_value* out) const override;
|
||||
Id* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
@@ -183,6 +200,7 @@ struct RawString : public BaseItem<RawString> {
|
||||
|
||||
RawString(const StringPool::Ref& ref);
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
bool flatten(android::Res_value* outValue) const override;
|
||||
RawString* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
@@ -193,17 +211,10 @@ struct String : public BaseItem<String> {
|
||||
|
||||
String(const StringPool::Ref& ref);
|
||||
|
||||
// Whether the string is marked as translateable. This does not persist when flattened.
|
||||
// It is only used during compilation phase.
|
||||
void setTranslateable(bool val);
|
||||
bool isTranslateable() const;
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
bool flatten(android::Res_value* outValue) const override;
|
||||
String* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
|
||||
private:
|
||||
bool mTranslateable;
|
||||
};
|
||||
|
||||
struct StyledString : public BaseItem<StyledString> {
|
||||
@@ -211,17 +222,10 @@ struct StyledString : public BaseItem<StyledString> {
|
||||
|
||||
StyledString(const StringPool::StyleRef& ref);
|
||||
|
||||
// Whether the string is marked as translateable. This does not persist when flattened.
|
||||
// It is only used during compilation phase.
|
||||
void setTranslateable(bool val);
|
||||
bool isTranslateable() const;
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
bool flatten(android::Res_value* outValue) const override;
|
||||
StyledString* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
|
||||
private:
|
||||
bool mTranslateable;
|
||||
};
|
||||
|
||||
struct FileReference : public BaseItem<FileReference> {
|
||||
@@ -235,6 +239,7 @@ struct FileReference : public BaseItem<FileReference> {
|
||||
FileReference() = default;
|
||||
FileReference(const StringPool::Ref& path);
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
bool flatten(android::Res_value* outValue) const override;
|
||||
FileReference* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
@@ -250,6 +255,7 @@ struct BinaryPrimitive : public BaseItem<BinaryPrimitive> {
|
||||
BinaryPrimitive(const android::Res_value& val);
|
||||
BinaryPrimitive(uint8_t dataType, uint32_t data);
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
bool flatten(android::Res_value* outValue) const override;
|
||||
BinaryPrimitive* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
@@ -268,6 +274,7 @@ struct Attribute : public BaseValue<Attribute> {
|
||||
|
||||
Attribute(bool w, uint32_t t = 0u);
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
Attribute* clone(StringPool* newPool) const override;
|
||||
void printMask(std::ostream* out) const;
|
||||
void print(std::ostream* out) const override;
|
||||
@@ -290,6 +297,7 @@ struct Style : public BaseValue<Style> {
|
||||
|
||||
std::vector<Entry> entries;
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
Style* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
};
|
||||
@@ -297,6 +305,7 @@ struct Style : public BaseValue<Style> {
|
||||
struct Array : public BaseValue<Array> {
|
||||
std::vector<std::unique_ptr<Item>> items;
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
Array* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
};
|
||||
@@ -314,6 +323,7 @@ struct Plural : public BaseValue<Plural> {
|
||||
|
||||
std::array<std::unique_ptr<Item>, Count> values;
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
Plural* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
};
|
||||
@@ -321,6 +331,7 @@ struct Plural : public BaseValue<Plural> {
|
||||
struct Styleable : public BaseValue<Styleable> {
|
||||
std::vector<Reference> entries;
|
||||
|
||||
bool equals(const Value* value) const override;
|
||||
Styleable* clone(StringPool* newPool) const override;
|
||||
void print(std::ostream* out) const override;
|
||||
};
|
||||
|
||||
@@ -127,6 +127,11 @@ struct DynCastVisitor<Item> : public RawValueVisitor {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
const T* valueCast(const Value* value) {
|
||||
return valueCast<T>(const_cast<Value*>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a valid pointer to T if the Value is of subtype T.
|
||||
* Otherwise, returns nullptr.
|
||||
@@ -141,7 +146,6 @@ T* valueCast(Value* value) {
|
||||
return visitor.value;
|
||||
}
|
||||
|
||||
|
||||
inline void visitAllValuesInPackage(ResourceTablePackage* pkg, RawValueVisitor* visitor) {
|
||||
for (auto& type : pkg->types) {
|
||||
for (auto& entry : type->entries) {
|
||||
|
||||
@@ -128,23 +128,6 @@ struct Visitor : public RawValueVisitor {
|
||||
mPool(pool), mMethod(method), mLocalizer(method) {
|
||||
}
|
||||
|
||||
void visit(Array* array) override {
|
||||
std::unique_ptr<Array> localized = util::make_unique<Array>();
|
||||
localized->items.resize(array->items.size());
|
||||
for (size_t i = 0; i < array->items.size(); i++) {
|
||||
Visitor subVisitor(mPool, mMethod);
|
||||
array->items[i]->accept(&subVisitor);
|
||||
if (subVisitor.mItem) {
|
||||
localized->items[i] = std::move(subVisitor.mItem);
|
||||
} else {
|
||||
localized->items[i] = std::unique_ptr<Item>(array->items[i]->clone(mPool));
|
||||
}
|
||||
}
|
||||
localized->setSource(array->getSource());
|
||||
localized->setWeak(true);
|
||||
mValue = std::move(localized);
|
||||
}
|
||||
|
||||
void visit(Plural* plural) override {
|
||||
std::unique_ptr<Plural> localized = util::make_unique<Plural>();
|
||||
for (size_t i = 0; i < plural->values.size(); i++) {
|
||||
@@ -164,10 +147,6 @@ struct Visitor : public RawValueVisitor {
|
||||
}
|
||||
|
||||
void visit(String* string) override {
|
||||
if (!string->isTranslateable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::u16string result = mLocalizer.start() + mLocalizer.text(*string->value) +
|
||||
mLocalizer.end();
|
||||
std::unique_ptr<String> localized = util::make_unique<String>(mPool->makeRef(result));
|
||||
@@ -177,10 +156,6 @@ struct Visitor : public RawValueVisitor {
|
||||
}
|
||||
|
||||
void visit(StyledString* string) override {
|
||||
if (!string->isTranslateable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mItem = pseudolocalizeStyledString(string, mMethod, mPool);
|
||||
mItem->setWeak(true);
|
||||
}
|
||||
@@ -238,14 +213,26 @@ void pseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A value is pseudolocalizable if it does not define a locale (or is the default locale)
|
||||
* and is translateable.
|
||||
*/
|
||||
static bool isPseudolocalizable(ResourceConfigValue* configValue) {
|
||||
const int diff = configValue->config.diff(ConfigDescription::defaultConfig());
|
||||
if (diff & ConfigDescription::CONFIG_LOCALE) {
|
||||
return false;
|
||||
}
|
||||
return configValue->value->isTranslateable();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool PseudolocaleGenerator::consume(IAaptContext* context, ResourceTable* table) {
|
||||
for (auto& package : table->packages) {
|
||||
for (auto& type : package->types) {
|
||||
for (auto& entry : type->entries) {
|
||||
std::vector<ResourceConfigValue*> values = entry->findAllValues(
|
||||
ConfigDescription::defaultConfig());
|
||||
std::vector<ResourceConfigValue*> values = entry->findValuesIf(isPseudolocalizable);
|
||||
|
||||
for (ResourceConfigValue* value : values) {
|
||||
pseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
|
||||
&table->stringPool, entry.get());
|
||||
|
||||
411
tools/aapt2/diff/Diff.cpp
Normal file
411
tools/aapt2/diff/Diff.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Flags.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "io/ZipArchive.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "process/SymbolTable.h"
|
||||
#include "unflatten/BinaryResourceParser.h"
|
||||
|
||||
#include <android-base/macros.h>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
class DiffContext : public IAaptContext {
|
||||
public:
|
||||
const std::u16string& getCompilationPackage() override {
|
||||
return mEmpty;
|
||||
}
|
||||
|
||||
uint8_t getPackageId() override {
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
IDiagnostics* getDiagnostics() override {
|
||||
return &mDiagnostics;
|
||||
}
|
||||
|
||||
NameMangler* getNameMangler() override {
|
||||
return &mNameMangler;
|
||||
}
|
||||
|
||||
SymbolTable* getExternalSymbols() override {
|
||||
return &mSymbolTable;
|
||||
}
|
||||
|
||||
bool verbose() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::u16string mEmpty;
|
||||
StdErrDiagnostics mDiagnostics;
|
||||
NameMangler mNameMangler = NameMangler(NameManglerPolicy{});
|
||||
SymbolTable mSymbolTable;
|
||||
};
|
||||
|
||||
class LoadedApk {
|
||||
public:
|
||||
LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
|
||||
std::unique_ptr<ResourceTable> table) :
|
||||
mSource(source), mApk(std::move(apk)), mTable(std::move(table)) {
|
||||
}
|
||||
|
||||
io::IFileCollection* getFileCollection() {
|
||||
return mApk.get();
|
||||
}
|
||||
|
||||
ResourceTable* getResourceTable() {
|
||||
return mTable.get();
|
||||
}
|
||||
|
||||
const Source& getSource() {
|
||||
return mSource;
|
||||
}
|
||||
|
||||
private:
|
||||
Source mSource;
|
||||
std::unique_ptr<io::IFileCollection> mApk;
|
||||
std::unique_ptr<ResourceTable> mTable;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LoadedApk);
|
||||
};
|
||||
|
||||
static std::unique_ptr<LoadedApk> loadApkFromPath(IAaptContext* context, const StringPiece& path) {
|
||||
Source source(path);
|
||||
std::string error;
|
||||
std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::create(path, &error);
|
||||
if (!apk) {
|
||||
context->getDiagnostics()->error(DiagMessage(source) << error);
|
||||
return {};
|
||||
}
|
||||
|
||||
io::IFile* file = apk->findFile("resources.arsc");
|
||||
if (!file) {
|
||||
context->getDiagnostics()->error(DiagMessage(source) << "no resources.arsc found");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<io::IData> data = file->openAsData();
|
||||
if (!data) {
|
||||
context->getDiagnostics()->error(DiagMessage(source) << "could not open resources.arsc");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
|
||||
BinaryResourceParser parser(context, table.get(), source, data->data(), data->size());
|
||||
if (!parser.parse()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
|
||||
}
|
||||
|
||||
static void emitDiffLine(const Source& source, const StringPiece& message) {
|
||||
std::cerr << source << ": " << message << "\n";
|
||||
}
|
||||
|
||||
static bool isSymbolVisibilityDifferent(const Symbol& symbolA, const Symbol& symbolB) {
|
||||
return symbolA.state != symbolB.state;
|
||||
}
|
||||
|
||||
template <typename Id>
|
||||
static bool isIdDiff(const Symbol& symbolA, const Maybe<Id>& idA,
|
||||
const Symbol& symbolB, const Maybe<Id>& idB) {
|
||||
if (symbolA.state == SymbolState::kPublic || symbolB.state == SymbolState::kPublic) {
|
||||
return idA != idB;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool emitResourceConfigValueDiff(IAaptContext* context,
|
||||
LoadedApk* apkA,
|
||||
ResourceTablePackage* pkgA,
|
||||
ResourceTableType* typeA,
|
||||
ResourceEntry* entryA,
|
||||
ResourceConfigValue* configValueA,
|
||||
LoadedApk* apkB,
|
||||
ResourceTablePackage* pkgB,
|
||||
ResourceTableType* typeB,
|
||||
ResourceEntry* entryB,
|
||||
ResourceConfigValue* configValueB) {
|
||||
Value* valueA = configValueA->value.get();
|
||||
Value* valueB = configValueB->value.get();
|
||||
if (!valueA->equals(valueB)) {
|
||||
std::stringstream strStream;
|
||||
strStream << "value " << pkgA->name << ":" << typeA->type << "/" << entryA->name
|
||||
<< " config=" << configValueA->config << " does not match:\n";
|
||||
valueA->print(&strStream);
|
||||
strStream << "\n vs \n";
|
||||
valueB->print(&strStream);
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool emitResourceEntryDiff(IAaptContext* context,
|
||||
LoadedApk* apkA,
|
||||
ResourceTablePackage* pkgA,
|
||||
ResourceTableType* typeA,
|
||||
ResourceEntry* entryA,
|
||||
LoadedApk* apkB,
|
||||
ResourceTablePackage* pkgB,
|
||||
ResourceTableType* typeB,
|
||||
ResourceEntry* entryB) {
|
||||
bool diff = false;
|
||||
for (std::unique_ptr<ResourceConfigValue>& configValueA : entryA->values) {
|
||||
ResourceConfigValue* configValueB = entryB->findValue(configValueA->config);
|
||||
if (!configValueB) {
|
||||
std::stringstream strStream;
|
||||
strStream << "missing " << pkgA->name << ":" << typeA->type << "/" << entryA->name
|
||||
<< " config=" << configValueA->config;
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
} else {
|
||||
diff |= emitResourceConfigValueDiff(context, apkA, pkgA, typeA, entryA,
|
||||
configValueA.get(), apkB, pkgB, typeB, entryB,
|
||||
configValueB);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any newly added config values.
|
||||
for (std::unique_ptr<ResourceConfigValue>& configValueB : entryB->values) {
|
||||
ResourceConfigValue* configValueA = entryA->findValue(configValueB->config);
|
||||
if (!configValueA) {
|
||||
std::stringstream strStream;
|
||||
strStream << "new config " << pkgB->name << ":" << typeB->type << "/" << entryB->name
|
||||
<< " config=" << configValueB->config;
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool emitResourceTypeDiff(IAaptContext* context,
|
||||
LoadedApk* apkA,
|
||||
ResourceTablePackage* pkgA,
|
||||
ResourceTableType* typeA,
|
||||
LoadedApk* apkB,
|
||||
ResourceTablePackage* pkgB,
|
||||
ResourceTableType* typeB) {
|
||||
bool diff = false;
|
||||
for (std::unique_ptr<ResourceEntry>& entryA : typeA->entries) {
|
||||
ResourceEntry* entryB = typeB->findEntry(entryA->name);
|
||||
if (!entryB) {
|
||||
std::stringstream strStream;
|
||||
strStream << "missing " << pkgA->name << ":" << typeA->type << "/" << entryA->name;
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
} else {
|
||||
if (isSymbolVisibilityDifferent(entryA->symbolStatus, entryB->symbolStatus)) {
|
||||
std::stringstream strStream;
|
||||
strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
|
||||
<< " has different visibility (";
|
||||
if (entryB->symbolStatus.state == SymbolState::kPublic) {
|
||||
strStream << "PUBLIC";
|
||||
} else {
|
||||
strStream << "PRIVATE";
|
||||
}
|
||||
strStream << " vs ";
|
||||
if (entryA->symbolStatus.state == SymbolState::kPublic) {
|
||||
strStream << "PUBLIC";
|
||||
} else {
|
||||
strStream << "PRIVATE";
|
||||
}
|
||||
strStream << ")";
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
} else if (isIdDiff(entryA->symbolStatus, entryA->id,
|
||||
entryB->symbolStatus, entryB->id)) {
|
||||
std::stringstream strStream;
|
||||
strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
|
||||
<< " has different public ID (";
|
||||
if (entryB->id) {
|
||||
strStream << "0x" << std::hex << entryB->id.value();
|
||||
} else {
|
||||
strStream << "none";
|
||||
}
|
||||
strStream << " vs ";
|
||||
if (entryA->id) {
|
||||
strStream << "0x " << std::hex << entryA->id.value();
|
||||
} else {
|
||||
strStream << "none";
|
||||
}
|
||||
strStream << ")";
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
}
|
||||
diff |= emitResourceEntryDiff(context, apkA, pkgA, typeA, entryA.get(),
|
||||
apkB, pkgB, typeB, entryB);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any newly added entries.
|
||||
for (std::unique_ptr<ResourceEntry>& entryB : typeB->entries) {
|
||||
ResourceEntry* entryA = typeA->findEntry(entryB->name);
|
||||
if (!entryA) {
|
||||
std::stringstream strStream;
|
||||
strStream << "new entry " << pkgB->name << ":" << typeB->type << "/" << entryB->name;
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
static bool emitResourcePackageDiff(IAaptContext* context, LoadedApk* apkA,
|
||||
ResourceTablePackage* pkgA,
|
||||
LoadedApk* apkB, ResourceTablePackage* pkgB) {
|
||||
bool diff = false;
|
||||
for (std::unique_ptr<ResourceTableType>& typeA : pkgA->types) {
|
||||
ResourceTableType* typeB = pkgB->findType(typeA->type);
|
||||
if (!typeB) {
|
||||
std::stringstream strStream;
|
||||
strStream << "missing " << pkgA->name << ":" << typeA->type;
|
||||
emitDiffLine(apkA->getSource(), strStream.str());
|
||||
diff = true;
|
||||
} else {
|
||||
if (isSymbolVisibilityDifferent(typeA->symbolStatus, typeB->symbolStatus)) {
|
||||
std::stringstream strStream;
|
||||
strStream << pkgA->name << ":" << typeA->type << " has different visibility (";
|
||||
if (typeB->symbolStatus.state == SymbolState::kPublic) {
|
||||
strStream << "PUBLIC";
|
||||
} else {
|
||||
strStream << "PRIVATE";
|
||||
}
|
||||
strStream << " vs ";
|
||||
if (typeA->symbolStatus.state == SymbolState::kPublic) {
|
||||
strStream << "PUBLIC";
|
||||
} else {
|
||||
strStream << "PRIVATE";
|
||||
}
|
||||
strStream << ")";
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
} else if (isIdDiff(typeA->symbolStatus, typeA->id, typeB->symbolStatus, typeB->id)) {
|
||||
std::stringstream strStream;
|
||||
strStream << pkgA->name << ":" << typeA->type << " has different public ID (";
|
||||
if (typeB->id) {
|
||||
strStream << "0x" << std::hex << typeB->id.value();
|
||||
} else {
|
||||
strStream << "none";
|
||||
}
|
||||
strStream << " vs ";
|
||||
if (typeA->id) {
|
||||
strStream << "0x " << std::hex << typeA->id.value();
|
||||
} else {
|
||||
strStream << "none";
|
||||
}
|
||||
strStream << ")";
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
}
|
||||
diff |= emitResourceTypeDiff(context, apkA, pkgA, typeA.get(), apkB, pkgB, typeB);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any newly added types.
|
||||
for (std::unique_ptr<ResourceTableType>& typeB : pkgB->types) {
|
||||
ResourceTableType* typeA = pkgA->findType(typeB->type);
|
||||
if (!typeA) {
|
||||
std::stringstream strStream;
|
||||
strStream << "new type " << pkgB->name << ":" << typeB->type;
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA, LoadedApk* apkB) {
|
||||
ResourceTable* tableA = apkA->getResourceTable();
|
||||
ResourceTable* tableB = apkB->getResourceTable();
|
||||
|
||||
bool diff = false;
|
||||
for (std::unique_ptr<ResourceTablePackage>& pkgA : tableA->packages) {
|
||||
ResourceTablePackage* pkgB = tableB->findPackage(pkgA->name);
|
||||
if (!pkgB) {
|
||||
std::stringstream strStream;
|
||||
strStream << "missing package " << pkgA->name;
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
} else {
|
||||
if (pkgA->id != pkgB->id) {
|
||||
std::stringstream strStream;
|
||||
strStream << "package '" << pkgA->name << "' has different id (";
|
||||
if (pkgB->id) {
|
||||
strStream << "0x" << std::hex << pkgB->id.value();
|
||||
} else {
|
||||
strStream << "none";
|
||||
}
|
||||
strStream << " vs ";
|
||||
if (pkgA->id) {
|
||||
strStream << "0x" << std::hex << pkgA->id.value();
|
||||
} else {
|
||||
strStream << "none";
|
||||
}
|
||||
strStream << ")";
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
}
|
||||
diff |= emitResourcePackageDiff(context, apkA, pkgA.get(), apkB, pkgB);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any newly added packages.
|
||||
for (std::unique_ptr<ResourceTablePackage>& pkgB : tableB->packages) {
|
||||
ResourceTablePackage* pkgA = tableA->findPackage(pkgB->name);
|
||||
if (!pkgA) {
|
||||
std::stringstream strStream;
|
||||
strStream << "new package " << pkgB->name;
|
||||
emitDiffLine(apkB->getSource(), strStream.str());
|
||||
diff = true;
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
int diff(const std::vector<StringPiece>& args) {
|
||||
DiffContext context;
|
||||
|
||||
Flags flags;
|
||||
if (!flags.parse("aapt2 diff", args, &std::cerr)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (flags.getArgs().size() != 2u) {
|
||||
std::cerr << "must have two apks as arguments.\n\n";
|
||||
flags.usage("aapt2 diff", &std::cerr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::unique_ptr<LoadedApk> apkA = loadApkFromPath(&context, flags.getArgs()[0]);
|
||||
std::unique_ptr<LoadedApk> apkB = loadApkFromPath(&context, flags.getArgs()[1]);
|
||||
if (!apkA || !apkB) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (emitResourceTableDiff(&context, apkA.get(), apkB.get())) {
|
||||
// We emitted a diff, so return 1 (failure).
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
@@ -234,6 +234,8 @@ private:
|
||||
const pb::Attribute& pbAttr = pbCompoundValue.attr();
|
||||
std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
|
||||
attr->typeMask = pbAttr.format_flags();
|
||||
attr->minInt = pbAttr.min_int();
|
||||
attr->maxInt = pbAttr.max_int();
|
||||
for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) {
|
||||
Attribute::Symbol symbol;
|
||||
deserializeItemCommon(pbSymbol, &symbol.symbol);
|
||||
|
||||
Reference in New Issue
Block a user