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 "ResourceTable.h"
     18 #include "ResourceUtils.h"
     19 #include "ResourceValues.h"
     20 #include "ValueVisitor.h"
     21 #include "link/TableMerger.h"
     22 #include "util/Util.h"
     23 
     24 #include <cassert>
     25 
     26 namespace aapt {
     27 
     28 TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable,
     29                          const TableMergerOptions& options) :
     30         mContext(context), mMasterTable(outTable), mOptions(options) {
     31     // Create the desired package that all tables will be merged into.
     32     mMasterPackage = mMasterTable->createPackage(
     33             mContext->getCompilationPackage(), mContext->getPackageId());
     34     assert(mMasterPackage && "package name or ID already taken");
     35 }
     36 
     37 bool TableMerger::merge(const Source& src, ResourceTable* table,
     38                         io::IFileCollection* collection) {
     39     return mergeImpl(src, table, collection, false /* overlay */, true /* allow new */);
     40 }
     41 
     42 bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table,
     43                                io::IFileCollection* collection) {
     44     return mergeImpl(src, table, collection, true /* overlay */, mOptions.autoAddOverlay);
     45 }
     46 
     47 /**
     48  * This will merge packages with the same package name (or no package name).
     49  */
     50 bool TableMerger::mergeImpl(const Source& src, ResourceTable* table,
     51                             io::IFileCollection* collection,
     52                             bool overlay, bool allowNew) {
     53     const uint8_t desiredPackageId = mContext->getPackageId();
     54 
     55     bool error = false;
     56     for (auto& package : table->packages) {
     57         // Warn of packages with an unrelated ID.
     58         if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
     59             mContext->getDiagnostics()->warn(DiagMessage(src)
     60                                              << "ignoring package " << package->name);
     61             continue;
     62         }
     63 
     64         if (package->name.empty() || mContext->getCompilationPackage() == package->name) {
     65             FileMergeCallback callback;
     66             if (collection) {
     67                 callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
     68                                FileReference* newFile, FileReference* oldFile) -> bool {
     69                     // The old file's path points inside the APK, so we can use it as is.
     70                     io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path));
     71                     if (!f) {
     72                         mContext->getDiagnostics()->error(DiagMessage(src) << "file '"
     73                                                           << *oldFile->path
     74                                                           << "' not found");
     75                         return false;
     76                     }
     77 
     78                     newFile->file = f;
     79                     return true;
     80                 };
     81             }
     82 
     83             // Merge here. Once the entries are merged and mangled, any references to
     84             // them are still valid. This is because un-mangled references are
     85             // mangled, then looked up at resolution time.
     86             // Also, when linking, we convert references with no package name to use
     87             // the compilation package name.
     88             error |= !doMerge(src, table, package.get(),
     89                               false /* mangle */, overlay, allowNew, callback);
     90         }
     91     }
     92     return !error;
     93 }
     94 
     95 /**
     96  * This will merge and mangle resources from a static library.
     97  */
     98 bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& packageName,
     99                                  ResourceTable* table, io::IFileCollection* collection) {
    100     bool error = false;
    101     for (auto& package : table->packages) {
    102         // Warn of packages with an unrelated ID.
    103         if (packageName != package->name) {
    104             mContext->getDiagnostics()->warn(DiagMessage(src)
    105                                              << "ignoring package " << package->name);
    106             continue;
    107         }
    108 
    109         bool mangle = packageName != mContext->getCompilationPackage();
    110         mMergedPackages.insert(package->name);
    111 
    112         auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
    113                             FileReference* newFile, FileReference* oldFile) -> bool {
    114             // The old file's path points inside the APK, so we can use it as is.
    115             io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path));
    116             if (!f) {
    117                 mContext->getDiagnostics()->error(DiagMessage(src) << "file '" << *oldFile->path
    118                                                   << "' not found");
    119                 return false;
    120             }
    121 
    122             newFile->file = f;
    123             return true;
    124         };
    125 
    126         error |= !doMerge(src, table, package.get(),
    127                           mangle, false /* overlay */, true /* allow new */, callback);
    128     }
    129     return !error;
    130 }
    131 
    132 bool TableMerger::doMerge(const Source& src,
    133                           ResourceTable* srcTable,
    134                           ResourceTablePackage* srcPackage,
    135                           const bool manglePackage,
    136                           const bool overlay,
    137                           const bool allowNewResources,
    138                           FileMergeCallback callback) {
    139     bool error = false;
    140 
    141     for (auto& srcType : srcPackage->types) {
    142         ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
    143         if (srcType->symbolStatus.state == SymbolState::kPublic) {
    144             if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
    145                     && dstType->id.value() == srcType->id.value()) {
    146                 // Both types are public and have different IDs.
    147                 mContext->getDiagnostics()->error(DiagMessage(src)
    148                                                   << "can not merge type '"
    149                                                   << srcType->type
    150                                                   << "': conflicting public IDs");
    151                 error = true;
    152                 continue;
    153             }
    154 
    155             dstType->symbolStatus = std::move(srcType->symbolStatus);
    156             dstType->id = srcType->id;
    157         }
    158 
    159         for (auto& srcEntry : srcType->entries) {
    160             ResourceEntry* dstEntry;
    161             if (manglePackage) {
    162                 std::u16string mangledName = NameMangler::mangleEntry(srcPackage->name,
    163                                                                       srcEntry->name);
    164                 if (allowNewResources) {
    165                     dstEntry = dstType->findOrCreateEntry(mangledName);
    166                 } else {
    167                     dstEntry = dstType->findEntry(mangledName);
    168                 }
    169             } else {
    170                 if (allowNewResources) {
    171                     dstEntry = dstType->findOrCreateEntry(srcEntry->name);
    172                 } else {
    173                     dstEntry = dstType->findEntry(srcEntry->name);
    174                 }
    175             }
    176 
    177             if (!dstEntry) {
    178                 mContext->getDiagnostics()->error(DiagMessage(src)
    179                                                   << "resource "
    180                                                   << ResourceNameRef(srcPackage->name,
    181                                                                      srcType->type,
    182                                                                      srcEntry->name)
    183                                                   << " does not override an existing resource");
    184                 mContext->getDiagnostics()->note(DiagMessage(src)
    185                                                  << "define an <add-resource> tag or use "
    186                                                     "--auto-add-overlay");
    187                 error = true;
    188                 continue;
    189             }
    190 
    191             if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
    192                 if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
    193                     if (dstEntry->symbolStatus.state == SymbolState::kPublic &&
    194                             dstEntry->id && srcEntry->id &&
    195                             dstEntry->id.value() != srcEntry->id.value()) {
    196                         // Both entries are public and have different IDs.
    197                         mContext->getDiagnostics()->error(DiagMessage(src)
    198                                                           << "can not merge entry '"
    199                                                           << srcEntry->name
    200                                                           << "': conflicting public IDs");
    201                         error = true;
    202                         continue;
    203                     }
    204 
    205                     if (srcEntry->id) {
    206                         dstEntry->id = srcEntry->id;
    207                     }
    208                 }
    209 
    210                 if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
    211                         dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
    212                     dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
    213                 }
    214             }
    215 
    216             ResourceNameRef resName(mMasterPackage->name, dstType->type, dstEntry->name);
    217 
    218             for (auto& srcValue : srcEntry->values) {
    219                 ResourceConfigValue* dstValue = dstEntry->findValue(srcValue->config,
    220                                                                     srcValue->product);
    221                 if (dstValue) {
    222                     const int collisionResult = ResourceTable::resolveValueCollision(
    223                             dstValue->value.get(), srcValue->value.get());
    224                     if (collisionResult == 0 && !overlay) {
    225                         // Error!
    226                         ResourceNameRef resourceName(srcPackage->name,
    227                                                      srcType->type,
    228                                                      srcEntry->name);
    229 
    230                         mContext->getDiagnostics()->error(DiagMessage(srcValue->value->getSource())
    231                                                           << "resource '" << resourceName
    232                                                           << "' has a conflicting value for "
    233                                                           << "configuration ("
    234                                                           << srcValue->config << ")");
    235                         mContext->getDiagnostics()->note(DiagMessage(dstValue->value->getSource())
    236                                                          << "originally defined here");
    237                         error = true;
    238                         continue;
    239                     } else if (collisionResult < 0) {
    240                         // Keep our existing value.
    241                         continue;
    242                     }
    243 
    244                 }
    245 
    246                 if (!dstValue) {
    247                     // Force create the entry if we didn't have it.
    248                     dstValue = dstEntry->findOrCreateValue(srcValue->config, srcValue->product);
    249                 }
    250 
    251                 if (FileReference* f = valueCast<FileReference>(srcValue->value.get())) {
    252                     std::unique_ptr<FileReference> newFileRef;
    253                     if (manglePackage) {
    254                         newFileRef = cloneAndMangleFile(srcPackage->name, *f);
    255                     } else {
    256                         newFileRef = std::unique_ptr<FileReference>(f->clone(
    257                                 &mMasterTable->stringPool));
    258                     }
    259 
    260                     if (callback) {
    261                         if (!callback(resName, srcValue->config, newFileRef.get(), f)) {
    262                             error = true;
    263                             continue;
    264                         }
    265                     }
    266                     dstValue->value = std::move(newFileRef);
    267 
    268                 } else {
    269                     dstValue->value = std::unique_ptr<Value>(srcValue->value->clone(
    270                             &mMasterTable->stringPool));
    271                 }
    272             }
    273         }
    274     }
    275     return !error;
    276 }
    277 
    278 std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(const std::u16string& package,
    279                                                                const FileReference& fileRef) {
    280 
    281     StringPiece16 prefix, entry, suffix;
    282     if (util::extractResFilePathParts(*fileRef.path, &prefix, &entry, &suffix)) {
    283         std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
    284         std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
    285         std::unique_ptr<FileReference> newFileRef = util::make_unique<FileReference>(
    286                 mMasterTable->stringPool.makeRef(newPath));
    287         newFileRef->setComment(fileRef.getComment());
    288         newFileRef->setSource(fileRef.getSource());
    289         return newFileRef;
    290     }
    291     return std::unique_ptr<FileReference>(fileRef.clone(&mMasterTable->stringPool));
    292 }
    293 
    294 bool TableMerger::mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay) {
    295     ResourceTable table;
    296     std::u16string path = util::utf8ToUtf16(ResourceUtils::buildResourceFileName(fileDesc,
    297                                                                                  nullptr));
    298     std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
    299             table.stringPool.makeRef(path));
    300     fileRef->setSource(fileDesc.source);
    301     fileRef->file = file;
    302 
    303     ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
    304     pkg->findOrCreateType(fileDesc.name.type)
    305             ->findOrCreateEntry(fileDesc.name.entry)
    306             ->findOrCreateValue(fileDesc.config, {})
    307             ->value = std::move(fileRef);
    308 
    309     return doMerge(file->getSource(), &table, pkg,
    310                    false /* mangle */, overlay /* overlay */, true /* allow new */, {});
    311 }
    312 
    313 bool TableMerger::mergeFile(const ResourceFile& fileDesc, io::IFile* file) {
    314     return mergeFileImpl(fileDesc, file, false /* overlay */);
    315 }
    316 
    317 bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file) {
    318     return mergeFileImpl(fileDesc, file, true /* overlay */);
    319 }
    320 
    321 } // namespace aapt
    322