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_ = master_table_->CreatePackage( 36 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, 41 io::IFileCollection* collection) { 42 return MergeImpl(src, table, collection, false /* overlay */, true /* allow new */); 43 } 44 45 bool TableMerger::MergeOverlay(const Source& src, ResourceTable* table, 46 io::IFileCollection* collection) { 47 return MergeImpl(src, table, collection, true /* overlay */, options_.auto_add_overlay); 48 } 49 50 /** 51 * This will merge packages with the same package name (or no package name). 52 */ 53 bool TableMerger::MergeImpl(const Source& src, ResourceTable* table, 54 io::IFileCollection* collection, bool overlay, 55 bool allow_new) { 56 bool error = false; 57 for (auto& package : table->packages) { 58 // Only merge an empty package or the package we're building. 59 // Other packages may exist, which likely contain attribute definitions. 60 // This is because at compile time it is unknown if the attributes are 61 // simply uses of the attribute or definitions. 62 if (package->name.empty() || context_->GetCompilationPackage() == package->name) { 63 FileMergeCallback callback; 64 if (collection) { 65 callback = [&](const ResourceNameRef& name, 66 const ConfigDescription& config, FileReference* new_file, 67 FileReference* old_file) -> bool { 68 // The old file's path points inside the APK, so we can use it as is. 69 io::IFile* f = collection->FindFile(*old_file->path); 70 if (!f) { 71 context_->GetDiagnostics()->Error(DiagMessage(src) 72 << "file '" << *old_file->path << "' not found"); 73 return false; 74 } 75 76 new_file->file = f; 77 return true; 78 }; 79 } 80 81 // Merge here. Once the entries are merged and mangled, any references to 82 // them are still valid. This is because un-mangled references are 83 // mangled, then looked up at resolution time. 84 // Also, when linking, we convert references with no package name to use 85 // the compilation package name. 86 error |= !DoMerge(src, table, package.get(), false /* mangle */, overlay, 87 allow_new, callback); 88 } 89 } 90 return !error; 91 } 92 93 /** 94 * This will merge and mangle resources from a static library. 95 */ 96 bool TableMerger::MergeAndMangle(const Source& src, 97 const StringPiece& package_name, 98 ResourceTable* table, 99 io::IFileCollection* collection) { 100 bool error = false; 101 for (auto& package : table->packages) { 102 // Warn of packages with an unrelated ID. 103 if (package_name != package->name) { 104 context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package " 105 << package->name); 106 continue; 107 } 108 109 bool mangle = package_name != context_->GetCompilationPackage(); 110 merged_packages_.insert(package->name); 111 112 auto callback = [&]( 113 const ResourceNameRef& name, const ConfigDescription& config, 114 FileReference* new_file, FileReference* old_file) -> bool { 115 // The old file's path points inside the APK, so we can use it as is. 116 io::IFile* f = collection->FindFile(*old_file->path); 117 if (!f) { 118 context_->GetDiagnostics()->Error( 119 DiagMessage(src) << "file '" << *old_file->path << "' not found"); 120 return false; 121 } 122 123 new_file->file = f; 124 return true; 125 }; 126 127 error |= !DoMerge(src, table, package.get(), mangle, false /* overlay */, 128 true /* allow new */, callback); 129 } 130 return !error; 131 } 132 133 static bool MergeType(IAaptContext* context, const Source& src, 134 ResourceTableType* dst_type, 135 ResourceTableType* src_type) { 136 if (dst_type->symbol_status.state < src_type->symbol_status.state) { 137 // The incoming type's visibility is stronger, so we should override 138 // the visibility. 139 if (src_type->symbol_status.state == SymbolState::kPublic) { 140 // Only copy the ID if the source is public, or else the ID is 141 // meaningless. 142 dst_type->id = src_type->id; 143 } 144 dst_type->symbol_status = std::move(src_type->symbol_status); 145 } else if (dst_type->symbol_status.state == SymbolState::kPublic && 146 src_type->symbol_status.state == SymbolState::kPublic && 147 dst_type->id && src_type->id && 148 dst_type->id.value() != src_type->id.value()) { 149 // Both types are public and have different IDs. 150 context->GetDiagnostics()->Error(DiagMessage(src) 151 << "cannot merge type '" << src_type->type 152 << "': conflicting public IDs"); 153 return false; 154 } 155 return true; 156 } 157 158 static bool MergeEntry(IAaptContext* context, const Source& src, 159 ResourceEntry* dst_entry, ResourceEntry* src_entry) { 160 if (dst_entry->symbol_status.state < src_entry->symbol_status.state) { 161 // The incoming type's visibility is stronger, so we should override 162 // the visibility. 163 if (src_entry->symbol_status.state == SymbolState::kPublic) { 164 // Only copy the ID if the source is public, or else the ID is 165 // meaningless. 166 dst_entry->id = src_entry->id; 167 } 168 dst_entry->symbol_status = std::move(src_entry->symbol_status); 169 } else if (src_entry->symbol_status.state == SymbolState::kPublic && 170 dst_entry->symbol_status.state == SymbolState::kPublic && 171 dst_entry->id && src_entry->id && 172 dst_entry->id.value() != src_entry->id.value()) { 173 // Both entries are public and have different IDs. 174 context->GetDiagnostics()->Error( 175 DiagMessage(src) << "cannot merge entry '" << src_entry->name 176 << "': conflicting public IDs"); 177 return false; 178 } 179 return true; 180 } 181 182 // Modified CollisionResolver which will merge Styleables and Styles. Used with overlays. 183 // 184 // Styleables are not actual resources, but they are treated as such during the 185 // compilation phase. 186 // 187 // Styleables and Styles don't simply overlay each other, their definitions merge 188 // and accumulate. If both values are Styleables/Styles, we just merge them into the 189 // existing value. 190 static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing, Value* incoming, 191 StringPool* pool) { 192 if (Styleable* existing_styleable = ValueCast<Styleable>(existing)) { 193 if (Styleable* incoming_styleable = ValueCast<Styleable>(incoming)) { 194 // Styleables get merged. 195 existing_styleable->MergeWith(incoming_styleable); 196 return ResourceTable::CollisionResult::kKeepOriginal; 197 } 198 } else if (Style* existing_style = ValueCast<Style>(existing)) { 199 if (Style* incoming_style = ValueCast<Style>(incoming)) { 200 // Styles get merged. 201 existing_style->MergeWith(incoming_style, pool); 202 return ResourceTable::CollisionResult::kKeepOriginal; 203 } 204 } 205 // Delegate to the default handler. 206 return ResourceTable::ResolveValueCollision(existing, incoming); 207 } 208 209 static ResourceTable::CollisionResult MergeConfigValue(IAaptContext* context, 210 const ResourceNameRef& res_name, 211 const bool overlay, 212 ResourceConfigValue* dst_config_value, 213 ResourceConfigValue* src_config_value, 214 StringPool* pool) { 215 using CollisionResult = ResourceTable::CollisionResult; 216 217 Value* dst_value = dst_config_value->value.get(); 218 Value* src_value = src_config_value->value.get(); 219 220 CollisionResult collision_result; 221 if (overlay) { 222 collision_result = ResolveMergeCollision(dst_value, src_value, pool); 223 } else { 224 collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value); 225 } 226 227 if (collision_result == CollisionResult::kConflict) { 228 if (overlay) { 229 return CollisionResult::kTakeNew; 230 } 231 232 // Error! 233 context->GetDiagnostics()->Error(DiagMessage(src_value->GetSource()) 234 << "resource '" << res_name << "' has a conflicting value for " 235 << "configuration (" << src_config_value->config << ")"); 236 context->GetDiagnostics()->Note(DiagMessage(dst_value->GetSource()) 237 << "originally defined here"); 238 return CollisionResult::kConflict; 239 } 240 return collision_result; 241 } 242 243 bool TableMerger::DoMerge(const Source& src, ResourceTable* src_table, 244 ResourceTablePackage* src_package, 245 const bool mangle_package, const bool overlay, 246 const bool allow_new_resources, 247 const FileMergeCallback& callback) { 248 bool error = false; 249 250 for (auto& src_type : src_package->types) { 251 ResourceTableType* dst_type = master_package_->FindOrCreateType(src_type->type); 252 if (!MergeType(context_, src, dst_type, src_type.get())) { 253 error = true; 254 continue; 255 } 256 257 for (auto& src_entry : src_type->entries) { 258 std::string entry_name = src_entry->name; 259 if (mangle_package) { 260 entry_name = NameMangler::MangleEntry(src_package->name, src_entry->name); 261 } 262 263 ResourceEntry* dst_entry; 264 if (allow_new_resources || src_entry->symbol_status.allow_new) { 265 dst_entry = dst_type->FindOrCreateEntry(entry_name); 266 } else { 267 dst_entry = dst_type->FindEntry(entry_name); 268 } 269 270 const ResourceNameRef res_name(src_package->name, src_type->type, src_entry->name); 271 272 if (!dst_entry) { 273 context_->GetDiagnostics()->Error(DiagMessage(src) 274 << "resource " << res_name 275 << " does not override an existing resource"); 276 context_->GetDiagnostics()->Note(DiagMessage(src) << "define an <add-resource> tag or use " 277 << "--auto-add-overlay"); 278 error = true; 279 continue; 280 } 281 282 if (!MergeEntry(context_, src, dst_entry, src_entry.get())) { 283 error = true; 284 continue; 285 } 286 287 for (auto& src_config_value : src_entry->values) { 288 using CollisionResult = ResourceTable::CollisionResult; 289 290 ResourceConfigValue* dst_config_value = dst_entry->FindValue( 291 src_config_value->config, src_config_value->product); 292 if (dst_config_value) { 293 CollisionResult collision_result = 294 MergeConfigValue(context_, res_name, overlay, dst_config_value, 295 src_config_value.get(), &master_table_->string_pool); 296 if (collision_result == CollisionResult::kConflict) { 297 error = true; 298 continue; 299 } else if (collision_result == CollisionResult::kKeepOriginal) { 300 continue; 301 } 302 } else { 303 dst_config_value = 304 dst_entry->FindOrCreateValue(src_config_value->config, src_config_value->product); 305 } 306 307 // Continue if we're taking the new resource. 308 309 if (FileReference* f = ValueCast<FileReference>(src_config_value->value.get())) { 310 std::unique_ptr<FileReference> new_file_ref; 311 if (mangle_package) { 312 new_file_ref = CloneAndMangleFile(src_package->name, *f); 313 } else { 314 new_file_ref = std::unique_ptr<FileReference>(f->Clone(&master_table_->string_pool)); 315 } 316 317 if (callback) { 318 if (!callback(res_name, src_config_value->config, new_file_ref.get(), f)) { 319 error = true; 320 continue; 321 } 322 } 323 dst_config_value->value = std::move(new_file_ref); 324 325 } else { 326 dst_config_value->value = std::unique_ptr<Value>( 327 src_config_value->value->Clone(&master_table_->string_pool)); 328 } 329 } 330 } 331 } 332 return !error; 333 } 334 335 std::unique_ptr<FileReference> TableMerger::CloneAndMangleFile( 336 const std::string& package, const FileReference& file_ref) { 337 StringPiece prefix, entry, suffix; 338 if (util::ExtractResFilePathParts(*file_ref.path, &prefix, &entry, &suffix)) { 339 std::string mangled_entry = NameMangler::MangleEntry(package, entry.to_string()); 340 std::string newPath = prefix.to_string() + mangled_entry + suffix.to_string(); 341 std::unique_ptr<FileReference> new_file_ref = 342 util::make_unique<FileReference>(master_table_->string_pool.MakeRef(newPath)); 343 new_file_ref->SetComment(file_ref.GetComment()); 344 new_file_ref->SetSource(file_ref.GetSource()); 345 return new_file_ref; 346 } 347 return std::unique_ptr<FileReference>(file_ref.Clone(&master_table_->string_pool)); 348 } 349 350 bool TableMerger::MergeFileImpl(const ResourceFile& file_desc, io::IFile* file, bool overlay) { 351 ResourceTable table; 352 std::string path = ResourceUtils::BuildResourceFileName(file_desc); 353 std::unique_ptr<FileReference> file_ref = 354 util::make_unique<FileReference>(table.string_pool.MakeRef(path)); 355 file_ref->SetSource(file_desc.source); 356 file_ref->file = file; 357 358 ResourceTablePackage* pkg = table.CreatePackage(file_desc.name.package, 0x0); 359 pkg->FindOrCreateType(file_desc.name.type) 360 ->FindOrCreateEntry(file_desc.name.entry) 361 ->FindOrCreateValue(file_desc.config, {}) 362 ->value = std::move(file_ref); 363 364 return DoMerge(file->GetSource(), &table, pkg, false /* mangle */, 365 overlay /* overlay */, true /* allow_new */, {}); 366 } 367 368 bool TableMerger::MergeFile(const ResourceFile& file_desc, io::IFile* file) { 369 return MergeFileImpl(file_desc, file, false /* overlay */); 370 } 371 372 bool TableMerger::MergeFileOverlay(const ResourceFile& file_desc, 373 io::IFile* file) { 374 return MergeFileImpl(file_desc, file, true /* overlay */); 375 } 376 377 } // namespace aapt 378