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_ =
     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