1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "link/TableMerger.h" 18 19 #include "android-base/logging.h" 20 21 #include "ResourceTable.h" 22 #include "ResourceUtils.h" 23 #include "ResourceValues.h" 24 #include "ValueVisitor.h" 25 #include "util/Util.h" 26 27 using ::android::StringPiece; 28 29 namespace aapt { 30 31 TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table, 32 const TableMergerOptions& options) 33 : context_(context), master_table_(out_table), options_(options) { 34 // Create the desired package that all tables will be merged into. 35 master_package_ = 36 master_table_->CreatePackage(context_->GetCompilationPackage(), context_->GetPackageId()); 37 CHECK(master_package_ != nullptr) << "package name or ID already taken"; 38 } 39 40 bool TableMerger::Merge(const Source& src, ResourceTable* table, bool overlay) { 41 // We allow adding new resources if this is not an overlay, or if the options allow overlays 42 // to add new resources. 43 return MergeImpl(src, table, overlay, options_.auto_add_overlay || !overlay /*allow_new*/); 44 } 45 46 // This will merge packages with the same package name (or no package name). 47 bool TableMerger::MergeImpl(const Source& src, ResourceTable* table, bool overlay, bool allow_new) { 48 bool error = false; 49 for (auto& package : table->packages) { 50 // Only merge an empty package or the package we're building. 51 // Other packages may exist, which likely contain attribute definitions. 52 // This is because at compile time it is unknown if the attributes are 53 // simply uses of the attribute or definitions. 54 if (package->name.empty() || context_->GetCompilationPackage() == package->name) { 55 // Merge here. Once the entries are merged and mangled, any references to them are still 56 // valid. This is because un-mangled references are mangled, then looked up at resolution 57 // time. Also, when linking, we convert references with no package name to use the compilation 58 // package name. 59 error |= !DoMerge(src, table, package.get(), false /*mangle*/, overlay, allow_new); 60 } 61 } 62 return !error; 63 } 64 65 // This will merge and mangle resources from a static library. It is assumed that all FileReferences 66 // have correctly set their io::IFile*. 67 bool TableMerger::MergeAndMangle(const Source& src, const StringPiece& package_name, 68 ResourceTable* table) { 69 bool error = false; 70 for (auto& package : table->packages) { 71 // Warn of packages with an unrelated ID. 72 if (package_name != package->name) { 73 context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package " << package->name); 74 continue; 75 } 76 77 bool mangle = package_name != context_->GetCompilationPackage(); 78 merged_packages_.insert(package->name); 79 error |= !DoMerge(src, table, package.get(), mangle, false /*overlay*/, true /*allow_new*/); 80 } 81 return !error; 82 } 83 84 static bool MergeType(IAaptContext* context, const Source& src, ResourceTableType* dst_type, 85 ResourceTableType* src_type) { 86 if (src_type->visibility_level > dst_type->visibility_level) { 87 // The incoming type's visibility is stronger, so we should override the visibility. 88 if (src_type->visibility_level == Visibility::Level::kPublic) { 89 // Only copy the ID if the source is public, or else the ID is meaningless. 90 dst_type->id = src_type->id; 91 } 92 dst_type->visibility_level = src_type->visibility_level; 93 } else if (dst_type->visibility_level == Visibility::Level::kPublic && 94 src_type->visibility_level == Visibility::Level::kPublic && dst_type->id && 95 src_type->id && dst_type->id.value() != src_type->id.value()) { 96 // Both types are public and have different IDs. 97 context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge type '" << src_type->type 98 << "': conflicting public IDs"); 99 return false; 100 } 101 return true; 102 } 103 104 static bool MergeEntry(IAaptContext* context, const Source& src, bool overlay, 105 ResourceEntry* dst_entry, ResourceEntry* src_entry) { 106 // Copy over the strongest visibility. 107 if (src_entry->visibility.level > dst_entry->visibility.level) { 108 // Only copy the ID if the source is public, or else the ID is meaningless. 109 if (src_entry->visibility.level == Visibility::Level::kPublic) { 110 dst_entry->id = src_entry->id; 111 } 112 dst_entry->visibility = std::move(src_entry->visibility); 113 } else if (src_entry->visibility.level == Visibility::Level::kPublic && 114 dst_entry->visibility.level == Visibility::Level::kPublic && dst_entry->id && 115 src_entry->id && src_entry->id != dst_entry->id) { 116 // Both entries are public and have different IDs. 117 context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge entry '" << src_entry->name 118 << "': conflicting public IDs"); 119 return false; 120 } 121 122 // Copy over the rest of the properties, if needed. 123 if (src_entry->allow_new) { 124 dst_entry->allow_new = std::move(src_entry->allow_new); 125 } 126 127 if (src_entry->overlayable) { 128 if (dst_entry->overlayable && !overlay) { 129 context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source) 130 << "duplicate overlayable declaration for resource '" 131 << src_entry->name << "'"); 132 context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source) 133 << "previous declaration here"); 134 return false; 135 } 136 dst_entry->overlayable = std::move(src_entry->overlayable); 137 } 138 return true; 139 } 140 141 // Modified CollisionResolver which will merge Styleables and Styles. Used with overlays. 142 // 143 // Styleables are not actual resources, but they are treated as such during the compilation phase. 144 // 145 // Styleables and Styles don't simply overlay each other, their definitions merge and accumulate. 146 // If both values are Styleables/Styles, we just merge them into the existing value. 147 static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing, Value* incoming, 148 StringPool* pool) { 149 if (Styleable* existing_styleable = ValueCast<Styleable>(existing)) { 150 if (Styleable* incoming_styleable = ValueCast<Styleable>(incoming)) { 151 // Styleables get merged. 152 existing_styleable->MergeWith(incoming_styleable); 153 return ResourceTable::CollisionResult::kKeepOriginal; 154 } 155 } else if (Style* existing_style = ValueCast<Style>(existing)) { 156 if (Style* incoming_style = ValueCast<Style>(incoming)) { 157 // Styles get merged. 158 existing_style->MergeWith(incoming_style, pool); 159 return ResourceTable::CollisionResult::kKeepOriginal; 160 } 161 } 162 // Delegate to the default handler. 163 return ResourceTable::ResolveValueCollision(existing, incoming); 164 } 165 166 static ResourceTable::CollisionResult MergeConfigValue(IAaptContext* context, 167 const ResourceNameRef& res_name, 168 bool overlay, 169 ResourceConfigValue* dst_config_value, 170 ResourceConfigValue* src_config_value, 171 StringPool* pool) { 172 using CollisionResult = ResourceTable::CollisionResult; 173 174 Value* dst_value = dst_config_value->value.get(); 175 Value* src_value = src_config_value->value.get(); 176 177 CollisionResult collision_result; 178 if (overlay) { 179 collision_result = ResolveMergeCollision(dst_value, src_value, pool); 180 } else { 181 collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value); 182 } 183 184 if (collision_result == CollisionResult::kConflict) { 185 if (overlay) { 186 return CollisionResult::kTakeNew; 187 } 188 189 // Error! 190 context->GetDiagnostics()->Error(DiagMessage(src_value->GetSource()) 191 << "resource '" << res_name << "' has a conflicting value for " 192 << "configuration (" << src_config_value->config << ")"); 193 context->GetDiagnostics()->Note(DiagMessage(dst_value->GetSource()) 194 << "originally defined here"); 195 return CollisionResult::kConflict; 196 } 197 return collision_result; 198 } 199 200 bool TableMerger::DoMerge(const Source& src, ResourceTable* src_table, 201 ResourceTablePackage* src_package, bool mangle_package, bool overlay, 202 bool allow_new_resources) { 203 bool error = false; 204 205 for (auto& src_type : src_package->types) { 206 ResourceTableType* dst_type = master_package_->FindOrCreateType(src_type->type); 207 if (!MergeType(context_, src, dst_type, src_type.get())) { 208 error = true; 209 continue; 210 } 211 212 for (auto& src_entry : src_type->entries) { 213 std::string entry_name = src_entry->name; 214 if (mangle_package) { 215 entry_name = NameMangler::MangleEntry(src_package->name, src_entry->name); 216 } 217 218 ResourceEntry* dst_entry; 219 if (allow_new_resources || src_entry->allow_new) { 220 dst_entry = dst_type->FindOrCreateEntry(entry_name); 221 } else { 222 dst_entry = dst_type->FindEntry(entry_name); 223 } 224 225 const ResourceNameRef res_name(src_package->name, src_type->type, src_entry->name); 226 227 if (!dst_entry) { 228 context_->GetDiagnostics()->Error(DiagMessage(src) 229 << "resource " << res_name 230 << " does not override an existing resource"); 231 context_->GetDiagnostics()->Note(DiagMessage(src) << "define an <add-resource> tag or use " 232 << "--auto-add-overlay"); 233 error = true; 234 continue; 235 } 236 237 if (!MergeEntry(context_, src, overlay, dst_entry, src_entry.get())) { 238 error = true; 239 continue; 240 } 241 242 for (auto& src_config_value : src_entry->values) { 243 using CollisionResult = ResourceTable::CollisionResult; 244 245 ResourceConfigValue* dst_config_value = dst_entry->FindValue( 246 src_config_value->config, src_config_value->product); 247 if (dst_config_value) { 248 CollisionResult collision_result = 249 MergeConfigValue(context_, res_name, overlay, dst_config_value, 250 src_config_value.get(), &master_table_->string_pool); 251 if (collision_result == CollisionResult::kConflict) { 252 error = true; 253 continue; 254 } else if (collision_result == CollisionResult::kKeepOriginal) { 255 continue; 256 } 257 } else { 258 dst_config_value = 259 dst_entry->FindOrCreateValue(src_config_value->config, src_config_value->product); 260 } 261 262 // Continue if we're taking the new resource. 263 264 if (FileReference* f = ValueCast<FileReference>(src_config_value->value.get())) { 265 std::unique_ptr<FileReference> new_file_ref; 266 if (mangle_package) { 267 new_file_ref = CloneAndMangleFile(src_package->name, *f); 268 } else { 269 new_file_ref = std::unique_ptr<FileReference>(f->Clone(&master_table_->string_pool)); 270 } 271 dst_config_value->value = std::move(new_file_ref); 272 273 } else { 274 dst_config_value->value = std::unique_ptr<Value>( 275 src_config_value->value->Clone(&master_table_->string_pool)); 276 } 277 } 278 } 279 } 280 return !error; 281 } 282 283 std::unique_ptr<FileReference> TableMerger::CloneAndMangleFile( 284 const std::string& package, const FileReference& file_ref) { 285 StringPiece prefix, entry, suffix; 286 if (util::ExtractResFilePathParts(*file_ref.path, &prefix, &entry, &suffix)) { 287 std::string mangled_entry = NameMangler::MangleEntry(package, entry.to_string()); 288 std::string newPath = prefix.to_string() + mangled_entry + suffix.to_string(); 289 std::unique_ptr<FileReference> new_file_ref = 290 util::make_unique<FileReference>(master_table_->string_pool.MakeRef(newPath)); 291 new_file_ref->SetComment(file_ref.GetComment()); 292 new_file_ref->SetSource(file_ref.GetSource()); 293 new_file_ref->type = file_ref.type; 294 new_file_ref->file = file_ref.file; 295 return new_file_ref; 296 } 297 return std::unique_ptr<FileReference>(file_ref.Clone(&master_table_->string_pool)); 298 } 299 300 bool TableMerger::MergeFile(const ResourceFile& file_desc, bool overlay, io::IFile* file) { 301 ResourceTable table; 302 std::string path = ResourceUtils::BuildResourceFileName(file_desc); 303 std::unique_ptr<FileReference> file_ref = 304 util::make_unique<FileReference>(table.string_pool.MakeRef(path)); 305 file_ref->SetSource(file_desc.source); 306 file_ref->type = file_desc.type; 307 file_ref->file = file; 308 309 ResourceTablePackage* pkg = table.CreatePackage(file_desc.name.package, 0x0); 310 pkg->FindOrCreateType(file_desc.name.type) 311 ->FindOrCreateEntry(file_desc.name.entry) 312 ->FindOrCreateValue(file_desc.config, {}) 313 ->value = std::move(file_ref); 314 315 return DoMerge(file->GetSource(), &table, pkg, false /*mangle*/, overlay /*overlay*/, 316 true /*allow_new*/); 317 } 318 319 } // namespace aapt 320