Home | History | Annotate | Download | only in link
      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