Merge "AAPT2: Implement attribute compat versioning" into oc-dev
am: e229113d46
Change-Id: I0200771f675cdfa69c458c374d3c16b0d91c5756
This commit is contained in:
@@ -99,6 +99,7 @@ cc_library_host_static {
|
||||
"link/PrivateAttributeMover.cpp",
|
||||
"link/ReferenceLinker.cpp",
|
||||
"link/TableMerger.cpp",
|
||||
"link/XmlCompatVersioner.cpp",
|
||||
"link/XmlNamespaceRemover.cpp",
|
||||
"link/XmlReferenceLinker.cpp",
|
||||
"optimize/ResourceDeduper.cpp",
|
||||
|
||||
@@ -37,6 +37,7 @@ struct Debug {
|
||||
const ResourceName& target_style);
|
||||
static void DumpHex(const void* data, size_t len);
|
||||
static void DumpXml(xml::XmlResource* doc);
|
||||
static std::string ToString(xml::XmlResource* doc);
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace aapt {
|
||||
static const char* sMajorVersion = "2";
|
||||
|
||||
// Update minor version whenever a feature or flag is added.
|
||||
static const char* sMinorVersion = "15";
|
||||
static const char* sMinorVersion = "16";
|
||||
|
||||
int PrintVersion() {
|
||||
std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
|
||||
|
||||
@@ -388,13 +388,20 @@ template <>
|
||||
struct hash<aapt::ResourceName> {
|
||||
size_t operator()(const aapt::ResourceName& name) const {
|
||||
android::hash_t h = 0;
|
||||
h = android::JenkinsHashMix(h, hash<string>()(name.package));
|
||||
h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.package)));
|
||||
h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type));
|
||||
h = android::JenkinsHashMix(h, hash<string>()(name.entry));
|
||||
h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.entry)));
|
||||
return static_cast<size_t>(h);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<aapt::ResourceId> {
|
||||
size_t operator()(const aapt::ResourceId& id) const {
|
||||
return id.id;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // AAPT_RESOURCE_H
|
||||
|
||||
@@ -333,6 +333,12 @@ void BinaryPrimitive::Print(std::ostream* out) const {
|
||||
}
|
||||
}
|
||||
|
||||
Attribute::Attribute()
|
||||
: type_mask(0u),
|
||||
min_int(std::numeric_limits<int32_t>::min()),
|
||||
max_int(std::numeric_limits<int32_t>::max()) {
|
||||
}
|
||||
|
||||
Attribute::Attribute(bool w, uint32_t t)
|
||||
: type_mask(t),
|
||||
min_int(std::numeric_limits<int32_t>::min()),
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define AAPT_RESOURCE_VALUES_H
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
@@ -251,6 +252,7 @@ struct Attribute : public BaseValue<Attribute> {
|
||||
int32_t max_int;
|
||||
std::vector<Symbol> symbols;
|
||||
|
||||
Attribute();
|
||||
explicit Attribute(bool w, uint32_t t = 0u);
|
||||
|
||||
bool Equals(const Value* value) const override;
|
||||
|
||||
@@ -26,9 +26,9 @@ using android::StringPiece;
|
||||
namespace aapt {
|
||||
|
||||
static const char* sDevelopmentSdkCodeName = "O";
|
||||
static int sDevelopmentSdkLevel = 26;
|
||||
static ApiVersion sDevelopmentSdkLevel = 26;
|
||||
|
||||
static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = {
|
||||
static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
|
||||
{0x021c, 1},
|
||||
{0x021d, 2},
|
||||
{0x0269, SDK_CUPCAKE},
|
||||
@@ -48,26 +48,29 @@ static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = {
|
||||
{0x03f1, SDK_KITKAT},
|
||||
{0x03f6, SDK_KITKAT_WATCH},
|
||||
{0x04ce, SDK_LOLLIPOP},
|
||||
{0x04d8, SDK_LOLLIPOP_MR1},
|
||||
{0x04f1, SDK_MARSHMALLOW},
|
||||
{0x0527, SDK_NOUGAT},
|
||||
{0x0530, SDK_NOUGAT_MR1},
|
||||
{0x0568, SDK_O},
|
||||
};
|
||||
|
||||
static bool less_entry_id(const std::pair<uint16_t, size_t>& p,
|
||||
uint16_t entryId) {
|
||||
static bool less_entry_id(const std::pair<uint16_t, ApiVersion>& p, uint16_t entryId) {
|
||||
return p.first < entryId;
|
||||
}
|
||||
|
||||
size_t FindAttributeSdkLevel(const ResourceId& id) {
|
||||
if (id.package_id() != 0x01 && id.type_id() != 0x01) {
|
||||
ApiVersion FindAttributeSdkLevel(const ResourceId& id) {
|
||||
if (id.package_id() != 0x01 || id.type_id() != 0x01) {
|
||||
return 0;
|
||||
}
|
||||
auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(),
|
||||
id.entry_id(), less_entry_id);
|
||||
auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(), id.entry_id(), less_entry_id);
|
||||
if (iter == sAttrIdMap.end()) {
|
||||
return SDK_LOLLIPOP_MR1;
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
static const std::unordered_map<std::string, size_t> sAttrMap = {
|
||||
static const std::unordered_map<std::string, ApiVersion> sAttrMap = {
|
||||
{"marqueeRepeatLimit", 2},
|
||||
{"windowNoDisplay", 3},
|
||||
{"backgroundDimEnabled", 3},
|
||||
@@ -729,7 +732,7 @@ static const std::unordered_map<std::string, size_t> sAttrMap = {
|
||||
{"windowActivityTransitions", 21},
|
||||
{"colorEdgeEffect", 21}};
|
||||
|
||||
size_t FindAttributeSdkLevel(const ResourceName& name) {
|
||||
ApiVersion FindAttributeSdkLevel(const ResourceName& name) {
|
||||
if (name.package != "android" && name.type != ResourceType::kAttr) {
|
||||
return 0;
|
||||
}
|
||||
@@ -741,9 +744,8 @@ size_t FindAttributeSdkLevel(const ResourceName& name) {
|
||||
return SDK_LOLLIPOP_MR1;
|
||||
}
|
||||
|
||||
std::pair<StringPiece, int> GetDevelopmentSdkCodeNameAndVersion() {
|
||||
return std::make_pair(StringPiece(sDevelopmentSdkCodeName),
|
||||
sDevelopmentSdkLevel);
|
||||
std::pair<StringPiece, ApiVersion> GetDevelopmentSdkCodeNameAndVersion() {
|
||||
return std::make_pair(StringPiece(sDevelopmentSdkCodeName), sDevelopmentSdkLevel);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
|
||||
namespace aapt {
|
||||
|
||||
enum : int {
|
||||
using ApiVersion = int;
|
||||
|
||||
enum : ApiVersion {
|
||||
SDK_CUPCAKE = 3,
|
||||
SDK_DONUT = 4,
|
||||
SDK_ECLAIR = 5,
|
||||
@@ -49,12 +51,12 @@ enum : int {
|
||||
SDK_MARSHMALLOW = 23,
|
||||
SDK_NOUGAT = 24,
|
||||
SDK_NOUGAT_MR1 = 25,
|
||||
SDK_O = 26, // STOPSHIP Replace with real version
|
||||
SDK_O = 26,
|
||||
};
|
||||
|
||||
size_t FindAttributeSdkLevel(const ResourceId& id);
|
||||
size_t FindAttributeSdkLevel(const ResourceName& name);
|
||||
std::pair<android::StringPiece, int> GetDevelopmentSdkCodeNameAndVersion();
|
||||
ApiVersion FindAttributeSdkLevel(const ResourceId& id);
|
||||
ApiVersion FindAttributeSdkLevel(const ResourceName& name);
|
||||
std::pair<android::StringPiece, ApiVersion> GetDevelopmentSdkCodeNameAndVersion();
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
|
||||
@@ -21,18 +21,11 @@
|
||||
namespace aapt {
|
||||
|
||||
TEST(SdkConstantsTest, FirstAttributeIsSdk1) {
|
||||
EXPECT_EQ(1u, FindAttributeSdkLevel(ResourceId(0x01010000)));
|
||||
EXPECT_EQ(1, FindAttributeSdkLevel(ResourceId(0x01010000)));
|
||||
}
|
||||
|
||||
TEST(SdkConstantsTest, AllAttributesAfterLollipopAreLollipopMR1) {
|
||||
EXPECT_EQ(SDK_LOLLIPOP, FindAttributeSdkLevel(ResourceId(0x010103f7)));
|
||||
EXPECT_EQ(SDK_LOLLIPOP, FindAttributeSdkLevel(ResourceId(0x010104ce)));
|
||||
|
||||
EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104cf)));
|
||||
EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104d8)));
|
||||
|
||||
EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104d9)));
|
||||
EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x0101ffff)));
|
||||
TEST(SdkConstantsTest, NonFrameworkAttributeIsSdk0) {
|
||||
EXPECT_EQ(0, FindAttributeSdkLevel(ResourceId(0x7f010345)));
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "link/ManifestFixer.h"
|
||||
#include "link/ReferenceLinker.h"
|
||||
#include "link/TableMerger.h"
|
||||
#include "link/XmlCompatVersioner.h"
|
||||
#include "optimize/ResourceDeduper.h"
|
||||
#include "optimize/VersionCollapser.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
@@ -247,25 +248,20 @@ class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
|
||||
IAaptContext* context_;
|
||||
};
|
||||
|
||||
static bool FlattenXml(xml::XmlResource* xml_res, const StringPiece& path,
|
||||
Maybe<size_t> max_sdk_level, bool keep_raw_values, IArchiveWriter* writer,
|
||||
IAaptContext* context) {
|
||||
static bool FlattenXml(IAaptContext* context, xml::XmlResource* xml_res, const StringPiece& path,
|
||||
bool keep_raw_values, IArchiveWriter* writer) {
|
||||
BigBuffer buffer(1024);
|
||||
XmlFlattenerOptions options = {};
|
||||
options.keep_raw_values = keep_raw_values;
|
||||
options.max_sdk_level = max_sdk_level;
|
||||
XmlFlattener flattener(&buffer, options);
|
||||
if (!flattener.Consume(context, xml_res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context->IsVerbose()) {
|
||||
DiagMessage msg;
|
||||
msg << "writing " << path << " to archive";
|
||||
if (max_sdk_level) {
|
||||
msg << " maxSdkLevel=" << max_sdk_level.value() << " keepRawValues=" << keep_raw_values;
|
||||
}
|
||||
context->GetDiagnostics()->Note(msg);
|
||||
context->GetDiagnostics()->Note(DiagMessage(path) << "writing to archive (keep_raw_values="
|
||||
<< (keep_raw_values ? "true" : "false")
|
||||
<< ")");
|
||||
}
|
||||
|
||||
io::BigBufferInputStream input_stream(&buffer);
|
||||
@@ -311,12 +307,33 @@ struct ResourceFileFlattenerOptions {
|
||||
std::unordered_set<std::string> extensions_to_not_compress;
|
||||
};
|
||||
|
||||
// A sampling of public framework resource IDs.
|
||||
struct R {
|
||||
struct attr {
|
||||
enum : uint32_t {
|
||||
paddingLeft = 0x010100d6u,
|
||||
paddingRight = 0x010100d8u,
|
||||
paddingHorizontal = 0x0101053du,
|
||||
|
||||
paddingTop = 0x010100d7u,
|
||||
paddingBottom = 0x010100d9u,
|
||||
paddingVertical = 0x0101053eu,
|
||||
|
||||
layout_marginLeft = 0x010100f7u,
|
||||
layout_marginRight = 0x010100f9u,
|
||||
layout_marginHorizontal = 0x0101053bu,
|
||||
|
||||
layout_marginTop = 0x010100f8u,
|
||||
layout_marginBottom = 0x010100fau,
|
||||
layout_marginVertical = 0x0101053cu,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
class ResourceFileFlattener {
|
||||
public:
|
||||
ResourceFileFlattener(const ResourceFileFlattenerOptions& options, IAaptContext* context,
|
||||
proguard::KeepSet* keep_set)
|
||||
: options_(options), context_(context), keep_set_(keep_set) {
|
||||
}
|
||||
proguard::KeepSet* keep_set);
|
||||
|
||||
bool Flatten(ResourceTable* table, IArchiveWriter* archive_writer);
|
||||
|
||||
@@ -325,7 +342,7 @@ class ResourceFileFlattener {
|
||||
ConfigDescription config;
|
||||
|
||||
// The entry this file came from.
|
||||
const ResourceEntry* entry;
|
||||
ResourceEntry* entry;
|
||||
|
||||
// The file to copy as-is.
|
||||
io::IFile* file_to_copy;
|
||||
@@ -335,19 +352,72 @@ class ResourceFileFlattener {
|
||||
|
||||
// The destination to write this file to.
|
||||
std::string dst_path;
|
||||
bool skip_version = false;
|
||||
};
|
||||
|
||||
uint32_t GetCompressionFlags(const StringPiece& str);
|
||||
|
||||
bool LinkAndVersionXmlFile(ResourceTable* table, FileOperation* file_op,
|
||||
std::queue<FileOperation>* out_file_op_queue);
|
||||
std::vector<std::unique_ptr<xml::XmlResource>> LinkAndVersionXmlFile(ResourceTable* table,
|
||||
FileOperation* file_op);
|
||||
|
||||
ResourceFileFlattenerOptions options_;
|
||||
IAaptContext* context_;
|
||||
proguard::KeepSet* keep_set_;
|
||||
XmlCompatVersioner::Rules rules_;
|
||||
};
|
||||
|
||||
ResourceFileFlattener::ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
|
||||
IAaptContext* context, proguard::KeepSet* keep_set)
|
||||
: options_(options), context_(context), keep_set_(keep_set) {
|
||||
SymbolTable* symm = context_->GetExternalSymbols();
|
||||
|
||||
// Build up the rules for degrading newer attributes to older ones.
|
||||
// NOTE(adamlesinski): These rules are hardcoded right now, but they should be
|
||||
// generated from the attribute definitions themselves (b/62028956).
|
||||
if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingHorizontal)) {
|
||||
std::vector<ReplacementAttr> replacements{
|
||||
{"paddingLeft", R::attr::paddingLeft,
|
||||
Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
|
||||
{"paddingRight", R::attr::paddingRight,
|
||||
Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
|
||||
};
|
||||
rules_[R::attr::paddingHorizontal] =
|
||||
util::make_unique<DegradeToManyRule>(std::move(replacements));
|
||||
}
|
||||
|
||||
if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingVertical)) {
|
||||
std::vector<ReplacementAttr> replacements{
|
||||
{"paddingTop", R::attr::paddingTop,
|
||||
Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
|
||||
{"paddingBottom", R::attr::paddingBottom,
|
||||
Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
|
||||
};
|
||||
rules_[R::attr::paddingVertical] =
|
||||
util::make_unique<DegradeToManyRule>(std::move(replacements));
|
||||
}
|
||||
|
||||
if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginHorizontal)) {
|
||||
std::vector<ReplacementAttr> replacements{
|
||||
{"layout_marginLeft", R::attr::layout_marginLeft,
|
||||
Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
|
||||
{"layout_marginRight", R::attr::layout_marginRight,
|
||||
Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
|
||||
};
|
||||
rules_[R::attr::layout_marginHorizontal] =
|
||||
util::make_unique<DegradeToManyRule>(std::move(replacements));
|
||||
}
|
||||
|
||||
if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginVertical)) {
|
||||
std::vector<ReplacementAttr> replacements{
|
||||
{"layout_marginTop", R::attr::layout_marginTop,
|
||||
Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
|
||||
{"layout_marginBottom", R::attr::layout_marginBottom,
|
||||
Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
|
||||
};
|
||||
rules_[R::attr::layout_marginVertical] =
|
||||
util::make_unique<DegradeToManyRule>(std::move(replacements));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ResourceFileFlattener::GetCompressionFlags(const StringPiece& str) {
|
||||
if (options_.do_not_compress_anything) {
|
||||
return 0;
|
||||
@@ -369,8 +439,19 @@ static bool IsTransitionElement(const std::string& name) {
|
||||
name == "transitionManager";
|
||||
}
|
||||
|
||||
bool ResourceFileFlattener::LinkAndVersionXmlFile(ResourceTable* table, FileOperation* file_op,
|
||||
std::queue<FileOperation>* out_file_op_queue) {
|
||||
static bool IsVectorElement(const std::string& name) {
|
||||
return name == "vector" || name == "animated-vector";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> make_singleton_vec(T&& val) {
|
||||
std::vector<T> vec;
|
||||
vec.emplace_back(std::forward<T>(val));
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVersionXmlFile(
|
||||
ResourceTable* table, FileOperation* file_op) {
|
||||
xml::XmlResource* doc = file_op->xml_to_flatten.get();
|
||||
const Source& src = doc->file.source;
|
||||
|
||||
@@ -380,107 +461,60 @@ bool ResourceFileFlattener::LinkAndVersionXmlFile(ResourceTable* table, FileOper
|
||||
|
||||
XmlReferenceLinker xml_linker;
|
||||
if (!xml_linker.Consume(context_, doc)) {
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (options_.update_proguard_spec && !proguard::CollectProguardRules(src, doc, keep_set_)) {
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (options_.no_xml_namespaces) {
|
||||
XmlNamespaceRemover namespace_remover;
|
||||
if (!namespace_remover.Consume(context_, doc)) {
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (!options_.no_auto_version) {
|
||||
if (options_.no_version_vectors) {
|
||||
// Skip this if it is a vector or animated-vector.
|
||||
xml::Element* el = xml::FindRootElement(doc);
|
||||
if (el && el->namespace_uri.empty()) {
|
||||
if (el->name == "vector" || el->name == "animated-vector") {
|
||||
// We are NOT going to version this file.
|
||||
file_op->skip_version = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options_.no_version_transitions) {
|
||||
// Skip this if it is a transition resource.
|
||||
xml::Element* el = xml::FindRootElement(doc);
|
||||
if (el && el->namespace_uri.empty()) {
|
||||
if (IsTransitionElement(el->name)) {
|
||||
// We are NOT going to version this file.
|
||||
file_op->skip_version = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options_.no_auto_version) {
|
||||
return make_singleton_vec(std::move(file_op->xml_to_flatten));
|
||||
}
|
||||
|
||||
const ConfigDescription& config = file_op->config;
|
||||
|
||||
// Find the first SDK level used that is higher than this defined config and
|
||||
// not superseded by a lower or equal SDK level resource.
|
||||
const int min_sdk_version = context_->GetMinSdkVersion();
|
||||
for (int sdk_level : xml_linker.sdk_levels()) {
|
||||
if (sdk_level > min_sdk_version && sdk_level > config.sdkVersion) {
|
||||
if (!ShouldGenerateVersionedResource(file_op->entry, config, sdk_level)) {
|
||||
// If we shouldn't generate a versioned resource, stop checking.
|
||||
break;
|
||||
}
|
||||
|
||||
ResourceFile versioned_file_desc = doc->file;
|
||||
versioned_file_desc.config.sdkVersion = (uint16_t)sdk_level;
|
||||
|
||||
FileOperation new_file_op;
|
||||
new_file_op.xml_to_flatten = util::make_unique<xml::XmlResource>(
|
||||
versioned_file_desc, StringPool{}, doc->root->Clone());
|
||||
new_file_op.config = versioned_file_desc.config;
|
||||
new_file_op.entry = file_op->entry;
|
||||
new_file_op.dst_path =
|
||||
ResourceUtils::BuildResourceFileName(versioned_file_desc, context_->GetNameMangler());
|
||||
|
||||
if (context_->IsVerbose()) {
|
||||
context_->GetDiagnostics()->Note(DiagMessage(versioned_file_desc.source)
|
||||
<< "auto-versioning resource from config '" << config
|
||||
<< "' -> '" << versioned_file_desc.config << "'");
|
||||
}
|
||||
|
||||
bool added = table->AddFileReferenceAllowMangled(
|
||||
versioned_file_desc.name, versioned_file_desc.config, versioned_file_desc.source,
|
||||
new_file_op.dst_path, nullptr, context_->GetDiagnostics());
|
||||
if (!added) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out_file_op_queue->push(std::move(new_file_op));
|
||||
break;
|
||||
if (options_.no_version_vectors || options_.no_version_transitions) {
|
||||
// Skip this if it is a vector or animated-vector.
|
||||
xml::Element* el = xml::FindRootElement(doc);
|
||||
if (el && el->namespace_uri.empty()) {
|
||||
if ((options_.no_version_vectors && IsVectorElement(el->name)) ||
|
||||
(options_.no_version_transitions && IsTransitionElement(el->name))) {
|
||||
return make_singleton_vec(std::move(file_op->xml_to_flatten));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
const ConfigDescription& config = file_op->config;
|
||||
ResourceEntry* entry = file_op->entry;
|
||||
|
||||
XmlCompatVersioner xml_compat_versioner(&rules_);
|
||||
const util::Range<ApiVersion> api_range{config.sdkVersion,
|
||||
FindNextApiVersionForConfig(entry, config)};
|
||||
return xml_compat_versioner.Process(context_, doc, api_range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not insert or remove any resources while executing in this function. It
|
||||
* will
|
||||
* corrupt the iteration order.
|
||||
*/
|
||||
bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archive_writer) {
|
||||
bool error = false;
|
||||
std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
|
||||
|
||||
for (auto& pkg : table->packages) {
|
||||
for (auto& type : pkg->types) {
|
||||
// Sort by config and name, so that we get better locality in the zip
|
||||
// file.
|
||||
// Sort by config and name, so that we get better locality in the zip file.
|
||||
config_sorted_files.clear();
|
||||
std::queue<FileOperation> file_operations;
|
||||
|
||||
// Populate the queue with all files in the ResourceTable.
|
||||
for (auto& entry : type->entries) {
|
||||
for (auto& config_value : entry->values) {
|
||||
// WARNING! Do not insert or remove any resources while executing in this scope. It will
|
||||
// corrupt the iteration order.
|
||||
|
||||
FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
|
||||
if (!file_ref) {
|
||||
continue;
|
||||
@@ -497,6 +531,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
|
||||
file_op.entry = entry.get();
|
||||
file_op.dst_path = *file_ref->path;
|
||||
file_op.config = config_value->config;
|
||||
file_op.file_to_copy = file;
|
||||
|
||||
const StringPiece src_path = file->GetSource().path;
|
||||
if (type->type != ResourceType::kRaw &&
|
||||
@@ -518,68 +553,51 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
|
||||
file_op.xml_to_flatten->file.config = config_value->config;
|
||||
file_op.xml_to_flatten->file.source = file_ref->GetSource();
|
||||
file_op.xml_to_flatten->file.name = ResourceName(pkg->name, type->type, entry->name);
|
||||
|
||||
// Enqueue the XML files to be processed.
|
||||
file_operations.push(std::move(file_op));
|
||||
} else {
|
||||
file_op.file_to_copy = file;
|
||||
|
||||
// NOTE(adamlesinski): Explicitly construct a StringPiece here, or
|
||||
// else we end up copying the string in the std::make_pair() method,
|
||||
// then creating a StringPiece from the copy, which would cause us
|
||||
// to end up referencing garbage in the map.
|
||||
const StringPiece entry_name(entry->name);
|
||||
config_sorted_files[std::make_pair(config_value->config, entry_name)] =
|
||||
std::move(file_op);
|
||||
}
|
||||
|
||||
// NOTE(adamlesinski): Explicitly construct a StringPiece here, or
|
||||
// else we end up copying the string in the std::make_pair() method,
|
||||
// then creating a StringPiece from the copy, which would cause us
|
||||
// to end up referencing garbage in the map.
|
||||
const StringPiece entry_name(entry->name);
|
||||
config_sorted_files[std::make_pair(config_value->config, entry_name)] =
|
||||
std::move(file_op);
|
||||
}
|
||||
}
|
||||
|
||||
// Now process the XML queue
|
||||
for (; !file_operations.empty(); file_operations.pop()) {
|
||||
FileOperation& file_op = file_operations.front();
|
||||
|
||||
if (!LinkAndVersionXmlFile(table, &file_op, &file_operations)) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE(adamlesinski): Explicitly construct a StringPiece here, or else
|
||||
// we end up copying the string in the std::make_pair() method, then
|
||||
// creating a StringPiece from the copy, which would cause us to end up
|
||||
// referencing garbage in the map.
|
||||
const StringPiece entry_name(file_op.entry->name);
|
||||
config_sorted_files[std::make_pair(file_op.config, entry_name)] = std::move(file_op);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now flatten the sorted values.
|
||||
for (auto& map_entry : config_sorted_files) {
|
||||
const ConfigDescription& config = map_entry.first.first;
|
||||
const FileOperation& file_op = map_entry.second;
|
||||
FileOperation& file_op = map_entry.second;
|
||||
|
||||
if (file_op.xml_to_flatten) {
|
||||
Maybe<size_t> max_sdk_level;
|
||||
if (!options_.no_auto_version && !file_op.skip_version) {
|
||||
max_sdk_level = std::max<size_t>(std::max<size_t>(config.sdkVersion, 1u),
|
||||
context_->GetMinSdkVersion());
|
||||
}
|
||||
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
|
||||
LinkAndVersionXmlFile(table, &file_op);
|
||||
for (std::unique_ptr<xml::XmlResource>& doc : versioned_docs) {
|
||||
std::string dst_path = file_op.dst_path;
|
||||
if (doc->file.config != file_op.config) {
|
||||
// Only add the new versioned configurations.
|
||||
if (context_->IsVerbose()) {
|
||||
context_->GetDiagnostics()->Note(DiagMessage(doc->file.source)
|
||||
<< "auto-versioning resource from config '"
|
||||
<< config << "' -> '" << doc->file.config << "'");
|
||||
}
|
||||
|
||||
bool result = FlattenXml(file_op.xml_to_flatten.get(), file_op.dst_path, max_sdk_level,
|
||||
options_.keep_raw_values, archive_writer, context_);
|
||||
if (!result) {
|
||||
error = true;
|
||||
dst_path =
|
||||
ResourceUtils::BuildResourceFileName(doc->file, context_->GetNameMangler());
|
||||
bool result = table->AddFileReferenceAllowMangled(doc->file.name, doc->file.config,
|
||||
doc->file.source, dst_path, nullptr,
|
||||
context_->GetDiagnostics());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
error |= !FlattenXml(context_, doc.get(), dst_path, options_.keep_raw_values,
|
||||
archive_writer);
|
||||
}
|
||||
} else {
|
||||
bool result =
|
||||
io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path,
|
||||
GetCompressionFlags(file_op.dst_path), archive_writer);
|
||||
if (!result) {
|
||||
error = true;
|
||||
}
|
||||
error |= !io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path,
|
||||
GetCompressionFlags(file_op.dst_path), archive_writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1358,8 +1376,7 @@ class LinkCommand {
|
||||
bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
|
||||
ResourceTable* table) {
|
||||
const bool keep_raw_values = context_->GetPackageType() == PackageType::kStaticLib;
|
||||
bool result =
|
||||
FlattenXml(manifest, "AndroidManifest.xml", {}, keep_raw_values, writer, context_);
|
||||
bool result = FlattenXml(context_, manifest, "AndroidManifest.xml", keep_raw_values, writer);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ std::vector<SplitConstraints> AdjustSplitConstraintsForMinSdk(
|
||||
}
|
||||
|
||||
static xml::AaptAttribute CreateAttributeWithId(const ResourceId& id) {
|
||||
return xml::AaptAttribute{id, Attribute(true)};
|
||||
return xml::AaptAttribute(Attribute(), id);
|
||||
}
|
||||
|
||||
std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
|
||||
|
||||
@@ -194,16 +194,9 @@ class XmlFlattenerVisitor : public xml::Visitor {
|
||||
|
||||
// Filter the attributes.
|
||||
for (xml::Attribute& attr : node->attributes) {
|
||||
if (options_.max_sdk_level && attr.compiled_attribute && attr.compiled_attribute.value().id) {
|
||||
size_t sdk_level = FindAttributeSdkLevel(attr.compiled_attribute.value().id.value());
|
||||
if (sdk_level > options_.max_sdk_level.value()) {
|
||||
continue;
|
||||
}
|
||||
if (attr.namespace_uri != xml::kSchemaTools) {
|
||||
filtered_attrs_.push_back(&attr);
|
||||
}
|
||||
if (attr.namespace_uri == xml::kSchemaTools) {
|
||||
continue;
|
||||
}
|
||||
filtered_attrs_.push_back(&attr);
|
||||
}
|
||||
|
||||
if (filtered_attrs_.empty()) {
|
||||
|
||||
@@ -30,11 +30,6 @@ struct XmlFlattenerOptions {
|
||||
* Keep attribute raw string values along with typed values.
|
||||
*/
|
||||
bool keep_raw_values = false;
|
||||
|
||||
/**
|
||||
* If set, the max SDK level of attribute to flatten. All others are ignored.
|
||||
*/
|
||||
Maybe<size_t> max_sdk_level;
|
||||
};
|
||||
|
||||
class XmlFlattener : public IXmlResourceConsumer {
|
||||
|
||||
@@ -149,31 +149,6 @@ TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
|
||||
ASSERT_EQ(android::ResXMLTree::END_DOCUMENT, tree.next());
|
||||
}
|
||||
|
||||
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingStart="1dp"
|
||||
android:colorAccent="#ffffff"/>)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
ASSERT_TRUE(linker.sdk_levels().count(17) == 1);
|
||||
ASSERT_TRUE(linker.sdk_levels().count(21) == 1);
|
||||
|
||||
android::ResXMLTree tree;
|
||||
XmlFlattenerOptions options;
|
||||
options.max_sdk_level = 17;
|
||||
ASSERT_TRUE(Flatten(doc.get(), &tree, options));
|
||||
|
||||
while (tree.next() != android::ResXMLTree::START_TAG) {
|
||||
ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
|
||||
ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
|
||||
}
|
||||
|
||||
ASSERT_EQ(1u, tree.getAttributeCount());
|
||||
EXPECT_EQ(uint32_t(0x010103b3), tree.getAttributeNameResID(0));
|
||||
}
|
||||
|
||||
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripOnlyTools) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
|
||||
<View xmlns:tools="http://schemas.android.com/tools"
|
||||
@@ -234,13 +209,11 @@ TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
|
||||
}
|
||||
|
||||
const StringPiece16 kPackage = u"package";
|
||||
EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()),
|
||||
0);
|
||||
EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
|
||||
}
|
||||
|
||||
TEST_F(XmlFlattenerTest, EmptyStringValueInAttributeIsNotNull) {
|
||||
std::unique_ptr<xml::XmlResource> doc =
|
||||
test::BuildXmlDom("<View package=\"\"/>");
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom("<View package=\"\"/>");
|
||||
|
||||
android::ResXMLTree tree;
|
||||
ASSERT_TRUE(Flatten(doc.get(), &tree));
|
||||
@@ -251,8 +224,7 @@ TEST_F(XmlFlattenerTest, EmptyStringValueInAttributeIsNotNull) {
|
||||
}
|
||||
|
||||
const StringPiece16 kPackage = u"package";
|
||||
ssize_t idx =
|
||||
tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
|
||||
ssize_t idx = tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
|
||||
ASSERT_GE(idx, 0);
|
||||
|
||||
size_t len;
|
||||
|
||||
23
tools/aapt2/integration-tests/AutoVersionTest/Android.mk
Normal file
23
tools/aapt2/integration-tests/AutoVersionTest/Android.mk
Normal file
@@ -0,0 +1,23 @@
|
||||
#
|
||||
# Copyright (C) 2017 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.
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_USE_AAPT2 := true
|
||||
LOCAL_PACKAGE_NAME := AaptAutoVersionTest
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
include $(BUILD_PACKAGE)
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.aapt.autoversiontest">
|
||||
|
||||
<uses-sdk android:minSdkVersion="7" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingVertical="24dp" />
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:autoStart="true"
|
||||
android:expandableListViewWhiteStyle="@empty"
|
||||
android:screenSize="large"
|
||||
android:subtitle="Hello"
|
||||
android:resizeMode="none"
|
||||
android:largestWidthLimitDp="999"
|
||||
android:uiOptions="none"
|
||||
android:parentActivityName="Hello"
|
||||
android:paddingStart="999dp"
|
||||
android:requiredForAllUsers="true"
|
||||
android:category="Hello"
|
||||
android:isGame="true"
|
||||
android:colorPrimary="#ffffff"
|
||||
android:revisionCode="999"
|
||||
android:autoVerify="true"
|
||||
android:use32bitAbi="true"
|
||||
android:shortcutId="@+id/id" />
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
class="foo">
|
||||
<View
|
||||
android:paddingHorizontal="24dp" />
|
||||
<View
|
||||
android:paddingHorizontal="24dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp" />
|
||||
<View
|
||||
android:paddingHorizontal="24dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp" />
|
||||
<View
|
||||
android:paddingVertical="24dp" />
|
||||
<View
|
||||
android:paddingVertical="24dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp" />
|
||||
<View
|
||||
android:layout_marginHorizontal="24dp" />
|
||||
<View
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp" />
|
||||
<View
|
||||
android:layout_marginVertical="24dp" />
|
||||
<View
|
||||
android:layout_marginVertical="24dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp" />
|
||||
</View>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingHorizontal="24dp" />
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<style name="Style2">
|
||||
<!-- API 7 -->
|
||||
<item name="android:autoStart">false</item>
|
||||
|
||||
<!-- API 17 -->
|
||||
<item name="android:paddingStart">0dp</item>
|
||||
|
||||
<!-- API 21 -->
|
||||
<item name="android:colorPrimary">#00ff00</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<style name="Style1">
|
||||
<!-- API 7 -->
|
||||
<item name="android:autoStart">true</item>
|
||||
|
||||
<!-- API 8 -->
|
||||
<item name="android:expandableListViewWhiteStyle">@null</item>
|
||||
|
||||
<!-- API 9 -->
|
||||
<item name="android:screenSize">large</item>
|
||||
|
||||
<!-- API 11 -->
|
||||
<item name="android:subtitle">Hello</item>
|
||||
|
||||
<!-- API 12 -->
|
||||
<item name="android:resizeMode">none</item>
|
||||
|
||||
<!-- API 13 -->
|
||||
<item name="android:largestWidthLimitDp">999</item>
|
||||
|
||||
<!-- API 14 -->
|
||||
<item name="android:uiOptions">none</item>
|
||||
|
||||
<!-- API 16 -->
|
||||
<item name="android:parentActivityName">Hello</item>
|
||||
|
||||
<!-- API 17 -->
|
||||
<item name="android:paddingStart">999dp</item>
|
||||
|
||||
<!-- API 18 -->
|
||||
<item name="android:requiredForAllUsers">true</item>
|
||||
|
||||
<!-- API 19 -->
|
||||
<item name="android:category">Hello</item>
|
||||
|
||||
<!-- API 20 -->
|
||||
<item name="android:isGame">true</item>
|
||||
|
||||
<!-- API 21 -->
|
||||
<item name="android:colorPrimary">#ffffff</item>
|
||||
|
||||
<!-- API 22 -->
|
||||
<item name="android:revisionCode">999</item>
|
||||
|
||||
<!-- API 23 -->
|
||||
<item name="android:autoVerify">true</item>
|
||||
|
||||
<!-- API 24 -->
|
||||
<item name="android:use32bitAbi">true</item>
|
||||
|
||||
<!-- API 25 -->
|
||||
<item name="android:shortcutId">@+id/id</item>
|
||||
|
||||
<!-- API 26 -->
|
||||
<item name="android:paddingHorizontal">999dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Style2">
|
||||
<!-- API 7 -->
|
||||
<item name="android:autoStart">true</item>
|
||||
|
||||
<!-- API 17 -->
|
||||
<item name="android:paddingStart">999dp</item>
|
||||
|
||||
<!-- API 21 -->
|
||||
<item name="android:colorPrimary">#ffffff</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -27,13 +27,15 @@
|
||||
|
||||
namespace aapt {
|
||||
|
||||
bool ShouldGenerateVersionedResource(const ResourceEntry* entry,
|
||||
const ConfigDescription& config,
|
||||
const int sdk_version_to_generate) {
|
||||
// We assume the caller is trying to generate a version greater than the
|
||||
// current configuration.
|
||||
bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
|
||||
const ApiVersion sdk_version_to_generate) {
|
||||
// We assume the caller is trying to generate a version greater than the current configuration.
|
||||
CHECK(sdk_version_to_generate > config.sdkVersion);
|
||||
return sdk_version_to_generate < FindNextApiVersionForConfig(entry, config);
|
||||
}
|
||||
|
||||
ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry,
|
||||
const ConfigDescription& config) {
|
||||
const auto end_iter = entry->values.end();
|
||||
auto iter = entry->values.begin();
|
||||
for (; iter != end_iter; ++iter) {
|
||||
@@ -46,26 +48,23 @@ bool ShouldGenerateVersionedResource(const ResourceEntry* entry,
|
||||
CHECK(iter != entry->values.end());
|
||||
++iter;
|
||||
|
||||
// The next configuration either only varies in sdkVersion, or it is
|
||||
// completely different
|
||||
// and therefore incompatible. If it is incompatible, we must generate the
|
||||
// versioned resource.
|
||||
// The next configuration either only varies in sdkVersion, or it is completely different
|
||||
// and therefore incompatible. If it is incompatible, we must generate the versioned resource.
|
||||
|
||||
// NOTE: The ordering of configurations takes sdkVersion as higher precedence
|
||||
// than other
|
||||
// NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
|
||||
// qualifiers, so we need to iterate through the entire list to be sure there
|
||||
// are no higher sdk level versions of this resource.
|
||||
ConfigDescription temp_config(config);
|
||||
for (; iter != end_iter; ++iter) {
|
||||
temp_config.sdkVersion = (*iter)->config.sdkVersion;
|
||||
if (temp_config == (*iter)->config) {
|
||||
// The two configs are the same, check the sdk version.
|
||||
return sdk_version_to_generate < (*iter)->config.sdkVersion;
|
||||
// The two configs are the same, return the sdkVersion.
|
||||
return (*iter)->config.sdkVersion;
|
||||
}
|
||||
}
|
||||
|
||||
// No match was found, so we should generate the versioned resource.
|
||||
return true;
|
||||
// Didn't find another config with a different sdk version, so return the highest possible value.
|
||||
return std::numeric_limits<ApiVersion>::max();
|
||||
}
|
||||
|
||||
bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
|
||||
@@ -86,7 +85,7 @@ bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
|
||||
}
|
||||
|
||||
if (Style* style = ValueCast<Style>(config_value->value.get())) {
|
||||
Maybe<size_t> min_sdk_stripped;
|
||||
Maybe<ApiVersion> min_sdk_stripped;
|
||||
std::vector<Style::Entry> stripped;
|
||||
|
||||
auto iter = style->entries.begin();
|
||||
@@ -95,17 +94,14 @@ bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
|
||||
|
||||
// Find the SDK level that is higher than the configuration
|
||||
// allows.
|
||||
const size_t sdk_level =
|
||||
FindAttributeSdkLevel(iter->key.id.value());
|
||||
if (sdk_level >
|
||||
std::max<size_t>(config_value->config.sdkVersion, 1)) {
|
||||
const ApiVersion sdk_level = FindAttributeSdkLevel(iter->key.id.value());
|
||||
if (sdk_level > std::max<ApiVersion>(config_value->config.sdkVersion, 1)) {
|
||||
// Record that we are about to strip this.
|
||||
stripped.emplace_back(std::move(*iter));
|
||||
|
||||
// We use the smallest SDK level to generate the new style.
|
||||
if (min_sdk_stripped) {
|
||||
min_sdk_stripped =
|
||||
std::min(min_sdk_stripped.value(), sdk_level);
|
||||
min_sdk_stripped = std::min(min_sdk_stripped.value(), sdk_level);
|
||||
} else {
|
||||
min_sdk_stripped = sdk_level;
|
||||
}
|
||||
@@ -126,10 +122,9 @@ bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
|
||||
min_sdk_stripped.value())) {
|
||||
// Let's create a new Style for this versioned resource.
|
||||
ConfigDescription new_config(config_value->config);
|
||||
new_config.sdkVersion = min_sdk_stripped.value();
|
||||
new_config.sdkVersion = static_cast<uint16_t>(min_sdk_stripped.value());
|
||||
|
||||
std::unique_ptr<Style> new_style(
|
||||
style->Clone(&table->string_pool));
|
||||
std::unique_ptr<Style> new_style(style->Clone(&table->string_pool));
|
||||
new_style->SetComment(style->GetComment());
|
||||
new_style->SetSource(style->GetSource());
|
||||
|
||||
@@ -140,8 +135,7 @@ bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
|
||||
std::make_move_iterator(stripped.end()));
|
||||
|
||||
// Insert the new Resource into the correct place.
|
||||
entry->FindOrCreateValue(new_config, {})->value =
|
||||
std::move(new_style);
|
||||
entry->FindOrCreateValue(new_config, {})->value = std::move(new_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "android-base/macros.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "SdkConstants.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
@@ -44,9 +45,12 @@ struct CallSite {
|
||||
* Determines whether a versioned resource should be created. If a versioned
|
||||
* resource already exists, it takes precedence.
|
||||
*/
|
||||
bool ShouldGenerateVersionedResource(const ResourceEntry* entry,
|
||||
const ConfigDescription& config,
|
||||
const int sdk_version_to_generate);
|
||||
bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
|
||||
const ApiVersion sdk_version_to_generate);
|
||||
|
||||
// Finds the next largest ApiVersion of the config which is identical to the given config except
|
||||
// for sdkVersion.
|
||||
ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry, const ConfigDescription& config);
|
||||
|
||||
class AutoVersioner : public IResourceTableConsumer {
|
||||
public:
|
||||
@@ -105,11 +109,10 @@ class ResourceConfigValue;
|
||||
|
||||
class ProductFilter : public IResourceTableConsumer {
|
||||
public:
|
||||
using ResourceConfigValueIter =
|
||||
std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
|
||||
using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
|
||||
|
||||
explicit ProductFilter(std::unordered_set<std::string> products)
|
||||
: products_(products) {}
|
||||
explicit ProductFilter(std::unordered_set<std::string> products) : products_(products) {
|
||||
}
|
||||
|
||||
ResourceConfigValueIter SelectProductToKeep(
|
||||
const ResourceNameRef& name, const ResourceConfigValueIter begin,
|
||||
@@ -118,19 +121,9 @@ class ProductFilter : public IResourceTableConsumer {
|
||||
bool Consume(IAaptContext* context, ResourceTable* table) override;
|
||||
|
||||
private:
|
||||
std::unordered_set<std::string> products_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProductFilter);
|
||||
};
|
||||
|
||||
class XmlAutoVersioner : public IXmlResourceConsumer {
|
||||
public:
|
||||
XmlAutoVersioner() = default;
|
||||
|
||||
bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(XmlAutoVersioner);
|
||||
std::unordered_set<std::string> products_;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -143,8 +136,7 @@ class XmlAutoVersioner : public IXmlResourceConsumer {
|
||||
*/
|
||||
class XmlNamespaceRemover : public IXmlResourceConsumer {
|
||||
public:
|
||||
explicit XmlNamespaceRemover(bool keep_uris = false)
|
||||
: keep_uris_(keep_uris){};
|
||||
explicit XmlNamespaceRemover(bool keep_uris = false) : keep_uris_(keep_uris){};
|
||||
|
||||
bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
|
||||
|
||||
@@ -165,17 +157,8 @@ class XmlReferenceLinker : public IXmlResourceConsumer {
|
||||
|
||||
bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
|
||||
|
||||
/**
|
||||
* Once the XmlResource has been consumed, this returns the various SDK levels
|
||||
* in which
|
||||
* framework attributes used within the XML document were defined.
|
||||
*/
|
||||
inline const std::set<int>& sdk_levels() const { return sdk_levels_found_; }
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(XmlReferenceLinker);
|
||||
|
||||
std::set<int> sdk_levels_found_;
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -274,7 +274,7 @@ Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference&
|
||||
if (out_error) *out_error = "is not an attribute";
|
||||
return {};
|
||||
}
|
||||
return xml::AaptAttribute{symbol->id, *symbol->attribute};
|
||||
return xml::AaptAttribute(*symbol->attribute, symbol->id);
|
||||
}
|
||||
|
||||
void ReferenceLinker::WriteResourceName(DiagMessage* out_msg,
|
||||
|
||||
179
tools/aapt2/link/XmlCompatVersioner.cpp
Normal file
179
tools/aapt2/link/XmlCompatVersioner.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 "link/XmlCompatVersioner.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "util/Util.h"
|
||||
|
||||
namespace aapt {
|
||||
|
||||
static xml::Attribute CopyAttr(const xml::Attribute& src, StringPool* out_string_pool) {
|
||||
xml::Attribute dst{src.namespace_uri, src.name, src.value, src.compiled_attribute};
|
||||
if (src.compiled_value != nullptr) {
|
||||
dst.compiled_value.reset(src.compiled_value->Clone(out_string_pool));
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
// Returns false if the attribute is not copied because an existing attribute takes precedence
|
||||
// (came from a rule).
|
||||
static bool CopyAttribute(const xml::Attribute& src_attr, bool generated, xml::Element* dst_el,
|
||||
StringPool* out_string_pool) {
|
||||
xml::Attribute* dst_attr = dst_el->FindAttribute(src_attr.namespace_uri, src_attr.name);
|
||||
if (dst_attr != nullptr) {
|
||||
if (generated) {
|
||||
// Generated attributes always take precedence.
|
||||
dst_attr->value = src_attr.value;
|
||||
dst_attr->compiled_attribute = src_attr.compiled_attribute;
|
||||
if (src_attr.compiled_value != nullptr) {
|
||||
dst_attr->compiled_value.reset(src_attr.compiled_value->Clone(out_string_pool));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
dst_el->attributes.push_back(CopyAttr(src_attr, out_string_pool));
|
||||
return true;
|
||||
}
|
||||
|
||||
void XmlCompatVersioner::ProcessRule(const xml::Element& src_el, const xml::Attribute& src_attr,
|
||||
const ApiVersion& src_attr_version, const IDegradeRule* rule,
|
||||
const util::Range<ApiVersion>& api_range, bool generated,
|
||||
xml::Element* dst_el,
|
||||
std::set<ApiVersion>* out_apis_referenced,
|
||||
StringPool* out_string_pool) {
|
||||
if (src_attr_version <= api_range.start) {
|
||||
// The API is compatible, so don't check the rule and just copy.
|
||||
if (!CopyAttribute(src_attr, generated, dst_el, out_string_pool)) {
|
||||
// TODO(adamlesinski): Log a warning that an attribute was overridden?
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (api_range.start >= SDK_LOLLIPOP_MR1) {
|
||||
// Since LOLLIPOP MR1, the framework can handle silently ignoring unknown public attributes,
|
||||
// so we don't need to erase/version them.
|
||||
// Copy.
|
||||
if (!CopyAttribute(src_attr, generated, dst_el, out_string_pool)) {
|
||||
// TODO(adamlesinski): Log a warning that an attribute was overridden?
|
||||
}
|
||||
} else {
|
||||
// We are going to erase this attribute from this XML resource version, but check if
|
||||
// we even need to move it anywhere. A developer may have effectively overwritten it with
|
||||
// a similarly versioned XML resource.
|
||||
if (src_attr_version < api_range.end) {
|
||||
// There is room for another versioned XML resource between this XML resource and the next
|
||||
// versioned XML resource defined by the developer.
|
||||
out_apis_referenced->insert(std::min<ApiVersion>(src_attr_version, SDK_LOLLIPOP_MR1));
|
||||
}
|
||||
}
|
||||
|
||||
if (rule != nullptr) {
|
||||
for (const DegradeResult& result : rule->Degrade(src_el, src_attr, out_string_pool)) {
|
||||
const ResourceId attr_resid = result.attr.compiled_attribute.value().id.value();
|
||||
const ApiVersion attr_version = FindAttributeSdkLevel(attr_resid);
|
||||
|
||||
auto iter = rules_->find(attr_resid);
|
||||
ProcessRule(src_el, result.attr, attr_version,
|
||||
iter != rules_->end() ? iter->second.get() : nullptr, api_range,
|
||||
true /*generated*/, dst_el, out_apis_referenced, out_string_pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XmlCompatVersioner::XmlCompatVersioner(const Rules* rules) : rules_(rules) {
|
||||
}
|
||||
|
||||
std::unique_ptr<xml::XmlResource> XmlCompatVersioner::ProcessDoc(
|
||||
ApiVersion target_api, ApiVersion max_api, xml::XmlResource* doc,
|
||||
std::set<ApiVersion>* out_apis_referenced) {
|
||||
const util::Range<ApiVersion> api_range{target_api, max_api};
|
||||
|
||||
std::unique_ptr<xml::XmlResource> cloned_doc = util::make_unique<xml::XmlResource>(doc->file);
|
||||
cloned_doc->file.config.sdkVersion = static_cast<uint16_t>(target_api);
|
||||
|
||||
cloned_doc->root = doc->root->Clone([&](const xml::Element& el, xml::Element* out_el) {
|
||||
for (const auto& attr : el.attributes) {
|
||||
if (!attr.compiled_attribute) {
|
||||
// Just copy if this isn't a compiled attribute.
|
||||
out_el->attributes.push_back(CopyAttr(attr, &cloned_doc->string_pool));
|
||||
continue;
|
||||
}
|
||||
|
||||
const ResourceId attr_resid = attr.compiled_attribute.value().id.value();
|
||||
const ApiVersion attr_version = FindAttributeSdkLevel(attr_resid);
|
||||
|
||||
auto rule = rules_->find(attr_resid);
|
||||
ProcessRule(el, attr, attr_version, rule != rules_->end() ? rule->second.get() : nullptr,
|
||||
api_range, false /*generated*/, out_el, out_apis_referenced,
|
||||
&cloned_doc->string_pool);
|
||||
}
|
||||
});
|
||||
return cloned_doc;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<xml::XmlResource>> XmlCompatVersioner::Process(
|
||||
IAaptContext* context, xml::XmlResource* doc, util::Range<ApiVersion> api_range) {
|
||||
// Adjust the API range so that it falls after this document and after minSdkVersion.
|
||||
api_range.start = std::max(api_range.start, context->GetMinSdkVersion());
|
||||
api_range.start = std::max(api_range.start, static_cast<ApiVersion>(doc->file.config.sdkVersion));
|
||||
|
||||
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs;
|
||||
std::set<ApiVersion> apis_referenced;
|
||||
versioned_docs.push_back(ProcessDoc(api_range.start, api_range.end, doc, &apis_referenced));
|
||||
|
||||
// Adjust the sdkVersion of the first XML document back to its original (this only really
|
||||
// makes a difference if the sdk version was below the minSdk to start).
|
||||
versioned_docs.back()->file.config.sdkVersion = doc->file.config.sdkVersion;
|
||||
|
||||
// Iterate from smallest to largest API version.
|
||||
for (ApiVersion api : apis_referenced) {
|
||||
std::set<ApiVersion> dummy;
|
||||
versioned_docs.push_back(ProcessDoc(api, api_range.end, doc, &dummy));
|
||||
}
|
||||
return versioned_docs;
|
||||
}
|
||||
|
||||
DegradeToManyRule::DegradeToManyRule(std::vector<ReplacementAttr> attrs)
|
||||
: attrs_(std::move(attrs)) {
|
||||
}
|
||||
|
||||
static inline std::unique_ptr<Item> CloneIfNotNull(const std::unique_ptr<Item>& src,
|
||||
StringPool* out_string_pool) {
|
||||
if (src == nullptr) {
|
||||
return {};
|
||||
}
|
||||
return std::unique_ptr<Item>(src->Clone(out_string_pool));
|
||||
}
|
||||
|
||||
std::vector<DegradeResult> DegradeToManyRule::Degrade(const xml::Element& src_el,
|
||||
const xml::Attribute& src_attr,
|
||||
StringPool* out_string_pool) const {
|
||||
std::vector<DegradeResult> result;
|
||||
result.reserve(attrs_.size());
|
||||
for (const ReplacementAttr& attr : attrs_) {
|
||||
result.push_back(
|
||||
DegradeResult{xml::Attribute{xml::kSchemaAndroid, attr.name, src_attr.value,
|
||||
xml::AaptAttribute{attr.attr, attr.id},
|
||||
CloneIfNotNull(src_attr.compiled_value, out_string_pool)},
|
||||
FindAttributeSdkLevel(attr.id)});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
100
tools/aapt2/link/XmlCompatVersioner.h
Normal file
100
tools/aapt2/link/XmlCompatVersioner.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef AAPT_LINKER_XMLCOMPATVERSIONER_H
|
||||
#define AAPT_LINKER_XMLCOMPATVERSIONER_H
|
||||
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "SdkConstants.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
namespace aapt {
|
||||
|
||||
class IDegradeRule;
|
||||
|
||||
struct DegradeResult {
|
||||
xml::Attribute attr;
|
||||
ApiVersion attr_api_version;
|
||||
};
|
||||
|
||||
class IDegradeRule {
|
||||
public:
|
||||
IDegradeRule() = default;
|
||||
virtual ~IDegradeRule() = default;
|
||||
|
||||
virtual std::vector<DegradeResult> Degrade(const xml::Element& src_el,
|
||||
const xml::Attribute& src_attr,
|
||||
StringPool* out_string_pool) const = 0;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(IDegradeRule);
|
||||
};
|
||||
|
||||
class XmlCompatVersioner {
|
||||
public:
|
||||
using Rules = std::unordered_map<ResourceId, std::unique_ptr<IDegradeRule>>;
|
||||
|
||||
XmlCompatVersioner(const Rules* rules);
|
||||
|
||||
std::vector<std::unique_ptr<xml::XmlResource>> Process(IAaptContext* context,
|
||||
xml::XmlResource* doc,
|
||||
util::Range<ApiVersion> api_range);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(XmlCompatVersioner);
|
||||
|
||||
std::unique_ptr<xml::XmlResource> ProcessDoc(ApiVersion target_api, ApiVersion max_api,
|
||||
xml::XmlResource* doc,
|
||||
std::set<ApiVersion>* out_apis_referenced);
|
||||
void ProcessRule(const xml::Element& src_el, const xml::Attribute& src_attr,
|
||||
const ApiVersion& src_attr_version, const IDegradeRule* rule,
|
||||
const util::Range<ApiVersion>& api_range, bool generated, xml::Element* dst_el,
|
||||
std::set<ApiVersion>* out_apis_referenced, StringPool* out_string_pool);
|
||||
|
||||
const Rules* rules_;
|
||||
};
|
||||
|
||||
struct ReplacementAttr {
|
||||
std::string name;
|
||||
ResourceId id;
|
||||
Attribute attr;
|
||||
};
|
||||
|
||||
class DegradeToManyRule : public IDegradeRule {
|
||||
public:
|
||||
DegradeToManyRule(std::vector<ReplacementAttr> attrs);
|
||||
virtual ~DegradeToManyRule() = default;
|
||||
|
||||
std::vector<DegradeResult> Degrade(const xml::Element& src_el, const xml::Attribute& src_attr,
|
||||
StringPool* out_string_pool) const override;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(DegradeToManyRule);
|
||||
|
||||
std::vector<ReplacementAttr> attrs_;
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
#endif // AAPT_LINKER_XMLCOMPATVERSIONER_H
|
||||
320
tools/aapt2/link/XmlCompatVersioner_test.cpp
Normal file
320
tools/aapt2/link/XmlCompatVersioner_test.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 "link/XmlCompatVersioner.h"
|
||||
|
||||
#include "Linkers.h"
|
||||
#include "test/Test.h"
|
||||
|
||||
namespace aapt {
|
||||
|
||||
constexpr auto TYPE_DIMENSION = android::ResTable_map::TYPE_DIMENSION;
|
||||
constexpr auto TYPE_STRING = android::ResTable_map::TYPE_STRING;
|
||||
|
||||
struct R {
|
||||
struct attr {
|
||||
enum : uint32_t {
|
||||
paddingLeft = 0x010100d6u, // (API 1)
|
||||
paddingRight = 0x010100d8u, // (API 1)
|
||||
progressBarPadding = 0x01010319u, // (API 11)
|
||||
paddingStart = 0x010103b3u, // (API 17)
|
||||
paddingHorizontal = 0x0101053du, // (API 26)
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
class XmlCompatVersionerTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
context_ =
|
||||
test::ContextBuilder()
|
||||
.SetCompilationPackage("com.app")
|
||||
.SetPackageId(0x7f)
|
||||
.SetPackageType(PackageType::kApp)
|
||||
.SetMinSdkVersion(SDK_GINGERBREAD)
|
||||
.AddSymbolSource(
|
||||
test::StaticSymbolSourceBuilder()
|
||||
.AddPublicSymbol("android:attr/paddingLeft", R::attr::paddingLeft,
|
||||
util::make_unique<Attribute>(false, TYPE_DIMENSION))
|
||||
.AddPublicSymbol("android:attr/paddingRight", R::attr::paddingRight,
|
||||
util::make_unique<Attribute>(false, TYPE_DIMENSION))
|
||||
.AddPublicSymbol("android:attr/progressBarPadding", R::attr::progressBarPadding,
|
||||
util::make_unique<Attribute>(false, TYPE_DIMENSION))
|
||||
.AddPublicSymbol("android:attr/paddingStart", R::attr::paddingStart,
|
||||
util::make_unique<Attribute>(false, TYPE_DIMENSION))
|
||||
.AddPublicSymbol("android:attr/paddingHorizontal", R::attr::paddingHorizontal,
|
||||
util::make_unique<Attribute>(false, TYPE_DIMENSION))
|
||||
.AddSymbol("com.app:attr/foo", ResourceId(0x7f010000),
|
||||
util::make_unique<Attribute>(false, TYPE_STRING))
|
||||
.Build())
|
||||
.Build();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<IAaptContext> context_;
|
||||
};
|
||||
|
||||
TEST_F(XmlCompatVersionerTest, NoRulesOnlyStripsAndCopies) {
|
||||
auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:paddingHorizontal="24dp"
|
||||
app:foo="16dp"
|
||||
foo="bar"/>)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
|
||||
XmlCompatVersioner::Rules rules;
|
||||
const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
|
||||
|
||||
XmlCompatVersioner versioner(&rules);
|
||||
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
|
||||
versioner.Process(context_.get(), doc.get(), api_range);
|
||||
ASSERT_EQ(2u, versioned_docs.size());
|
||||
|
||||
xml::Element* el;
|
||||
|
||||
// Source XML file's sdkVersion == 0, so the first one must also have the same sdkVersion.
|
||||
EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
|
||||
el = xml::FindRootElement(versioned_docs[0].get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
EXPECT_EQ(2u, el->attributes.size());
|
||||
EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
|
||||
EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
|
||||
EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
|
||||
|
||||
EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion);
|
||||
el = xml::FindRootElement(versioned_docs[1].get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
EXPECT_EQ(3u, el->attributes.size());
|
||||
EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
|
||||
EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
|
||||
EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
|
||||
}
|
||||
|
||||
TEST_F(XmlCompatVersionerTest, SingleRule) {
|
||||
auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:paddingHorizontal="24dp"
|
||||
app:foo="16dp"
|
||||
foo="bar"/>)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
|
||||
XmlCompatVersioner::Rules rules;
|
||||
rules[R::attr::paddingHorizontal] =
|
||||
util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>(
|
||||
{ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)},
|
||||
ReplacementAttr{"paddingRight", R::attr::paddingRight,
|
||||
Attribute(false, TYPE_DIMENSION)}}));
|
||||
|
||||
const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
|
||||
|
||||
XmlCompatVersioner versioner(&rules);
|
||||
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
|
||||
versioner.Process(context_.get(), doc.get(), api_range);
|
||||
ASSERT_EQ(2u, versioned_docs.size());
|
||||
|
||||
xml::Element* el;
|
||||
|
||||
EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
|
||||
el = xml::FindRootElement(versioned_docs[0].get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
EXPECT_EQ(4u, el->attributes.size());
|
||||
EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
|
||||
EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
|
||||
EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
|
||||
|
||||
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion);
|
||||
el = xml::FindRootElement(versioned_docs[1].get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
EXPECT_EQ(5u, el->attributes.size());
|
||||
EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
|
||||
EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
|
||||
EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
}
|
||||
|
||||
TEST_F(XmlCompatVersionerTest, ChainedRule) {
|
||||
auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingHorizontal="24dp" />)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
|
||||
XmlCompatVersioner::Rules rules;
|
||||
rules[R::attr::progressBarPadding] =
|
||||
util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>(
|
||||
{ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)},
|
||||
ReplacementAttr{"paddingRight", R::attr::paddingRight,
|
||||
Attribute(false, TYPE_DIMENSION)}}));
|
||||
rules[R::attr::paddingHorizontal] =
|
||||
util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>({ReplacementAttr{
|
||||
"progressBarPadding", R::attr::progressBarPadding, Attribute(false, TYPE_DIMENSION)}}));
|
||||
|
||||
const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
|
||||
|
||||
XmlCompatVersioner versioner(&rules);
|
||||
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
|
||||
versioner.Process(context_.get(), doc.get(), api_range);
|
||||
ASSERT_EQ(3u, versioned_docs.size());
|
||||
|
||||
xml::Element* el;
|
||||
|
||||
EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
|
||||
el = xml::FindRootElement(versioned_docs[0].get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
EXPECT_EQ(2u, el->attributes.size());
|
||||
EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
|
||||
|
||||
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
EXPECT_EQ(static_cast<uint16_t>(SDK_HONEYCOMB), versioned_docs[1]->file.config.sdkVersion);
|
||||
el = xml::FindRootElement(versioned_docs[1].get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
EXPECT_EQ(1u, el->attributes.size());
|
||||
EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
|
||||
EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"));
|
||||
EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingRight"));
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "progressBarPadding");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[2]->file.config.sdkVersion);
|
||||
el = xml::FindRootElement(versioned_docs[2].get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
EXPECT_EQ(2u, el->attributes.size());
|
||||
EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"));
|
||||
EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingRight"));
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "progressBarPadding");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
}
|
||||
|
||||
TEST_F(XmlCompatVersionerTest, DegradeRuleOverridesExistingAttribute) {
|
||||
auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"/>)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
|
||||
Item* padding_horizontal_value = xml::FindRootElement(doc.get())
|
||||
->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")
|
||||
->compiled_value.get();
|
||||
ASSERT_NE(nullptr, padding_horizontal_value);
|
||||
|
||||
XmlCompatVersioner::Rules rules;
|
||||
rules[R::attr::paddingHorizontal] =
|
||||
util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>(
|
||||
{ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)},
|
||||
ReplacementAttr{"paddingRight", R::attr::paddingRight,
|
||||
Attribute(false, TYPE_DIMENSION)}}));
|
||||
|
||||
const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
|
||||
|
||||
XmlCompatVersioner versioner(&rules);
|
||||
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
|
||||
versioner.Process(context_.get(), doc.get(), api_range);
|
||||
ASSERT_EQ(2u, versioned_docs.size());
|
||||
|
||||
xml::Element* el;
|
||||
|
||||
EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
|
||||
el = xml::FindRootElement(versioned_docs[0].get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
EXPECT_EQ(2u, el->attributes.size());
|
||||
EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
|
||||
|
||||
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion);
|
||||
el = xml::FindRootElement(versioned_docs[1].get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
EXPECT_EQ(3u, el->attributes.size());
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
|
||||
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
|
||||
ASSERT_NE(nullptr, attr);
|
||||
ASSERT_NE(nullptr, attr->compiled_value);
|
||||
ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
|
||||
ASSERT_TRUE(attr->compiled_attribute);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
@@ -72,13 +72,13 @@ class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
using xml::PackageAwareVisitor::Visit;
|
||||
|
||||
XmlVisitor(const Source& source, const CallSite& callsite, IAaptContext* context,
|
||||
SymbolTable* symbols, std::set<int>* sdk_levels_found)
|
||||
SymbolTable* symbols)
|
||||
: source_(source),
|
||||
callsite_(callsite),
|
||||
context_(context),
|
||||
symbols_(symbols),
|
||||
sdk_levels_found_(sdk_levels_found),
|
||||
reference_visitor_(callsite, context, symbols, this) {}
|
||||
reference_visitor_(callsite, context, symbols, this) {
|
||||
}
|
||||
|
||||
void Visit(xml::Element* el) override {
|
||||
// The default Attribute allows everything except enums or flags.
|
||||
@@ -118,22 +118,12 @@ class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find this compiled attribute's SDK level.
|
||||
const xml::AaptAttribute& aapt_attr = attr.compiled_attribute.value();
|
||||
if (aapt_attr.id) {
|
||||
// Record all SDK levels from which the attributes were defined.
|
||||
const size_t sdk_level = FindAttributeSdkLevel(aapt_attr.id.value());
|
||||
if (sdk_level > 1) {
|
||||
sdk_levels_found_->insert(sdk_level);
|
||||
}
|
||||
}
|
||||
attribute = &aapt_attr.attribute;
|
||||
attribute = &attr.compiled_attribute.value().attribute;
|
||||
}
|
||||
|
||||
attr.compiled_value = ResourceUtils::TryParseItemForAttribute(attr.value, attribute);
|
||||
if (attr.compiled_value) {
|
||||
// With a compiledValue, we must resolve the reference and assign it an
|
||||
// ID.
|
||||
// With a compiledValue, we must resolve the reference and assign it an ID.
|
||||
attr.compiled_value->SetSource(source);
|
||||
attr.compiled_value->Accept(&reference_visitor_);
|
||||
} else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) {
|
||||
@@ -164,7 +154,6 @@ class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
IAaptContext* context_;
|
||||
SymbolTable* symbols_;
|
||||
|
||||
std::set<int>* sdk_levels_found_;
|
||||
ReferenceVisitor reference_visitor_;
|
||||
bool error_ = false;
|
||||
};
|
||||
@@ -172,10 +161,8 @@ class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
} // namespace
|
||||
|
||||
bool XmlReferenceLinker::Consume(IAaptContext* context, xml::XmlResource* resource) {
|
||||
sdk_levels_found_.clear();
|
||||
const CallSite callsite = {resource->file.name};
|
||||
XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols(),
|
||||
&sdk_levels_found_);
|
||||
XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
|
||||
if (resource->root) {
|
||||
resource->root->Accept(&visitor);
|
||||
return !visitor.HasError();
|
||||
|
||||
@@ -160,16 +160,6 @@ TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:colorAccent="#ffffff" />)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
EXPECT_TRUE(linker.sdk_levels().count(21) == 1);
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
|
||||
|
||||
@@ -237,14 +237,12 @@ static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
|
||||
}
|
||||
|
||||
// We found a resource.
|
||||
std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>();
|
||||
s->id = id;
|
||||
std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id);
|
||||
|
||||
// Check to see if it is an attribute.
|
||||
for (size_t i = 0; i < (size_t)count; i++) {
|
||||
if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
|
||||
s->attribute = std::make_shared<Attribute>(false);
|
||||
s->attribute->type_mask = entry[i].map.value.data;
|
||||
s->attribute = std::make_shared<Attribute>(false, entry[i].map.value.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,16 +53,12 @@ class NameMangler;
|
||||
class SymbolTable {
|
||||
public:
|
||||
struct Symbol {
|
||||
Symbol() : Symbol(Maybe<ResourceId>{}) {}
|
||||
Symbol() = default;
|
||||
|
||||
explicit Symbol(const Maybe<ResourceId>& i) : Symbol(i, nullptr) {}
|
||||
|
||||
Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr)
|
||||
: Symbol(i, attr, false) {}
|
||||
|
||||
Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr,
|
||||
bool pub)
|
||||
: id(i), attribute(attr), is_public(pub) {}
|
||||
explicit Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr = {},
|
||||
bool pub = false)
|
||||
: id(i), attribute(attr), is_public(pub) {
|
||||
}
|
||||
|
||||
Symbol(const Symbol&) = default;
|
||||
Symbol(Symbol&&) = default;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Android Asset Packaging Tool 2.0 (AAPT2) release notes
|
||||
|
||||
## Version 2.16
|
||||
### `aapt2 link ...`
|
||||
- Versioning of XML files is more intelligent, using a small set of rules to degrade
|
||||
specific newer attributes to backwards compatible versions of them.
|
||||
Ex: `android:paddingHorizontal` degrades to `android:paddingLeft` and `android:paddingRight`.
|
||||
|
||||
## Version 2.15
|
||||
### `aapt2 compile ...`
|
||||
- Add `--no-crunch` option to avoid processing PNGs during the compile phase. Note that this
|
||||
|
||||
@@ -44,6 +44,12 @@
|
||||
namespace aapt {
|
||||
namespace util {
|
||||
|
||||
template <typename T>
|
||||
struct Range {
|
||||
T start;
|
||||
T end;
|
||||
};
|
||||
|
||||
std::vector<std::string> Split(const android::StringPiece& str, char sep);
|
||||
std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
|
||||
|
||||
|
||||
@@ -356,17 +356,16 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
|
||||
return util::make_unique<XmlResource>(ResourceFile{}, std::move(string_pool), std::move(root));
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> Namespace::Clone() {
|
||||
std::unique_ptr<Node> Namespace::Clone(const ElementCloneFunc& el_cloner) {
|
||||
auto ns = util::make_unique<Namespace>();
|
||||
ns->comment = comment;
|
||||
ns->line_number = line_number;
|
||||
ns->column_number = column_number;
|
||||
ns->namespace_prefix = namespace_prefix;
|
||||
ns->namespace_uri = namespace_uri;
|
||||
|
||||
ns->children.reserve(children.size());
|
||||
for (const std::unique_ptr<xml::Node>& child : children) {
|
||||
ns->AppendChild(child->Clone());
|
||||
ns->AppendChild(child->Clone(el_cloner));
|
||||
}
|
||||
return std::move(ns);
|
||||
}
|
||||
@@ -412,6 +411,15 @@ Attribute* Element::FindAttribute(const StringPiece& ns,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece& name) const {
|
||||
for (const auto& attr : attributes) {
|
||||
if (ns == attr.namespace_uri && name == attr.name) {
|
||||
return &attr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) {
|
||||
return FindChildWithAttribute(ns, name, {}, {}, {});
|
||||
}
|
||||
@@ -464,29 +472,23 @@ std::vector<Element*> Element::GetChildElements() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> Element::Clone() {
|
||||
std::unique_ptr<Node> Element::Clone(const ElementCloneFunc& el_cloner) {
|
||||
auto el = util::make_unique<Element>();
|
||||
el->comment = comment;
|
||||
el->line_number = line_number;
|
||||
el->column_number = column_number;
|
||||
el->name = name;
|
||||
el->namespace_uri = namespace_uri;
|
||||
|
||||
el->attributes.reserve(attributes.size());
|
||||
for (xml::Attribute& attr : attributes) {
|
||||
// Don't copy compiled values or attributes.
|
||||
el->attributes.push_back(
|
||||
xml::Attribute{attr.namespace_uri, attr.name, attr.value});
|
||||
}
|
||||
|
||||
el_cloner(*this, el.get());
|
||||
el->children.reserve(children.size());
|
||||
for (const std::unique_ptr<xml::Node>& child : children) {
|
||||
el->AppendChild(child->Clone());
|
||||
el->AppendChild(child->Clone(el_cloner));
|
||||
}
|
||||
return std::move(el);
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> Text::Clone() {
|
||||
std::unique_ptr<Node> Text::Clone(const ElementCloneFunc&) {
|
||||
auto t = util::make_unique<Text>();
|
||||
t->comment = comment;
|
||||
t->line_number = line_number;
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace xml {
|
||||
|
||||
class RawVisitor;
|
||||
|
||||
class Element;
|
||||
|
||||
/**
|
||||
* Base class for all XML nodes.
|
||||
*/
|
||||
@@ -51,7 +53,11 @@ class Node {
|
||||
void AppendChild(std::unique_ptr<Node> child);
|
||||
void InsertChild(size_t index, std::unique_ptr<Node> child);
|
||||
virtual void Accept(RawVisitor* visitor) = 0;
|
||||
virtual std::unique_ptr<Node> Clone() = 0;
|
||||
|
||||
using ElementCloneFunc = std::function<void(const Element&, Element*)>;
|
||||
|
||||
// Clones the Node subtree, using the given function to decide how to clone an Element.
|
||||
virtual std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -72,12 +78,16 @@ class Namespace : public BaseNode<Namespace> {
|
||||
std::string namespace_prefix;
|
||||
std::string namespace_uri;
|
||||
|
||||
std::unique_ptr<Node> Clone() override;
|
||||
std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
|
||||
};
|
||||
|
||||
struct AaptAttribute {
|
||||
Maybe<ResourceId> id;
|
||||
explicit AaptAttribute(const ::aapt::Attribute& attr, const Maybe<ResourceId>& resid = {})
|
||||
: attribute(attr), id(resid) {
|
||||
}
|
||||
|
||||
aapt::Attribute attribute;
|
||||
Maybe<ResourceId> id;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -102,6 +112,8 @@ class Element : public BaseNode<Element> {
|
||||
std::vector<Attribute> attributes;
|
||||
|
||||
Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name);
|
||||
const Attribute* FindAttribute(const android::StringPiece& ns,
|
||||
const android::StringPiece& name) const;
|
||||
xml::Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name);
|
||||
xml::Element* FindChildWithAttribute(const android::StringPiece& ns,
|
||||
const android::StringPiece& name,
|
||||
@@ -109,7 +121,7 @@ class Element : public BaseNode<Element> {
|
||||
const android::StringPiece& attr_name,
|
||||
const android::StringPiece& attr_value);
|
||||
std::vector<xml::Element*> GetChildElements();
|
||||
std::unique_ptr<Node> Clone() override;
|
||||
std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -119,7 +131,7 @@ class Text : public BaseNode<Text> {
|
||||
public:
|
||||
std::string text;
|
||||
|
||||
std::unique_ptr<Node> Clone() override;
|
||||
std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user