/* * Copyright (C) 2015 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/TableMerger.h" #include "android-base/logging.h" #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" #include "trace/TraceBuffer.h" #include "ValueVisitor.h" #include "util/Util.h" using ::android::StringPiece; namespace aapt { TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table, const TableMergerOptions& options) : context_(context), master_table_(out_table), options_(options) { // Create the desired package that all tables will be merged into. master_package_ = master_table_->CreatePackage(context_->GetCompilationPackage(), context_->GetPackageId()); CHECK(master_package_ != nullptr) << "package name or ID already taken"; } bool TableMerger::Merge(const Source& src, ResourceTable* table, bool overlay) { TRACE_CALL(); // We allow adding new resources if this is not an overlay, or if the options allow overlays // to add new resources. return MergeImpl(src, table, overlay, options_.auto_add_overlay || !overlay /*allow_new*/); } // This will merge packages with the same package name (or no package name). bool TableMerger::MergeImpl(const Source& src, ResourceTable* table, bool overlay, bool allow_new) { bool error = false; for (auto& package : table->packages) { // Only merge an empty package or the package we're building. // Other packages may exist, which likely contain attribute definitions. // This is because at compile time it is unknown if the attributes are // simply uses of the attribute or definitions. if (package->name.empty() || context_->GetCompilationPackage() == package->name) { // Merge here. Once the entries are merged and mangled, any references to them are still // valid. This is because un-mangled references are mangled, then looked up at resolution // time. Also, when linking, we convert references with no package name to use the compilation // package name. error |= !DoMerge(src, package.get(), false /*mangle*/, overlay, allow_new); } } return !error; } // This will merge and mangle resources from a static library. It is assumed that all FileReferences // have correctly set their io::IFile*. bool TableMerger::MergeAndMangle(const Source& src, const StringPiece& package_name, ResourceTable* table) { bool error = false; for (auto& package : table->packages) { // Warn of packages with an unrelated ID. if (package_name != package->name) { context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package " << package->name); continue; } bool mangle = package_name != context_->GetCompilationPackage(); merged_packages_.insert(package->name); error |= !DoMerge(src, package.get(), mangle, false /*overlay*/, true /*allow_new*/); } return !error; } static bool MergeType(IAaptContext* context, const Source& src, ResourceTableType* dst_type, ResourceTableType* src_type) { if (src_type->visibility_level > dst_type->visibility_level) { // The incoming type's visibility is stronger, so we should override the visibility. if (src_type->visibility_level == Visibility::Level::kPublic) { // Only copy the ID if the source is public, or else the ID is meaningless. dst_type->id = src_type->id; } dst_type->visibility_level = src_type->visibility_level; } else if (dst_type->visibility_level == Visibility::Level::kPublic && src_type->visibility_level == Visibility::Level::kPublic && dst_type->id && src_type->id && dst_type->id.value() != src_type->id.value()) { // Both types are public and have different IDs. context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge type '" << src_type->type << "': conflicting public IDs"); return false; } return true; } static bool MergeEntry(IAaptContext* context, const Source& src, ResourceEntry* dst_entry, ResourceEntry* src_entry, bool strict_visibility) { if (strict_visibility && dst_entry->visibility.level != Visibility::Level::kUndefined && src_entry->visibility.level != dst_entry->visibility.level) { context->GetDiagnostics()->Error( DiagMessage(src) << "cannot merge resource '" << dst_entry->name << "' with conflicting visibilities: " << "public and private"); return false; } // Copy over the strongest visibility. if (src_entry->visibility.level > dst_entry->visibility.level) { // Only copy the ID if the source is public, or else the ID is meaningless. if (src_entry->visibility.level == Visibility::Level::kPublic) { dst_entry->id = src_entry->id; } dst_entry->visibility = std::move(src_entry->visibility); } else if (src_entry->visibility.level == Visibility::Level::kPublic && dst_entry->visibility.level == Visibility::Level::kPublic && dst_entry->id && src_entry->id && src_entry->id != dst_entry->id) { // Both entries are public and have different IDs. context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge entry '" << src_entry->name << "': conflicting public IDs"); return false; } // Copy over the rest of the properties, if needed. if (src_entry->allow_new) { dst_entry->allow_new = std::move(src_entry->allow_new); } if (src_entry->overlayable_item) { if (dst_entry->overlayable_item) { CHECK(src_entry->overlayable_item.value().overlayable != nullptr); Overlayable* src_overlayable = src_entry->overlayable_item.value().overlayable.get(); CHECK(dst_entry->overlayable_item.value().overlayable != nullptr); Overlayable* dst_overlayable = dst_entry->overlayable_item.value().overlayable.get(); if (src_overlayable->name != dst_overlayable->name || src_overlayable->actor != dst_overlayable->actor || src_entry->overlayable_item.value().policies != dst_entry->overlayable_item.value().policies) { // Do not allow a resource with an overlayable declaration to have that overlayable // declaration redefined. context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable_item.value().source) << "duplicate overlayable declaration for resource '" << src_entry->name << "'"); context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable_item.value().source) << "previous declaration here"); return false; } } dst_entry->overlayable_item = std::move(src_entry->overlayable_item); } return true; } // Modified CollisionResolver which will merge Styleables and Styles. Used with overlays. // // Styleables are not actual resources, but they are treated as such during the compilation phase. // // Styleables and Styles don't simply overlay each other, their definitions merge and accumulate. // If both values are Styleables/Styles, we just merge them into the existing value. static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing, Value* incoming, StringPool* pool) { if (Styleable* existing_styleable = ValueCast(existing)) { if (Styleable* incoming_styleable = ValueCast(incoming)) { // Styleables get merged. existing_styleable->MergeWith(incoming_styleable); return ResourceTable::CollisionResult::kKeepOriginal; } } else if (Style* existing_style = ValueCast