TableMerger.cpp revision e78fd617ec60139a973a01925fa7adad31febb39
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 "ResourceValues.h" 19#include "ValueVisitor.h" 20 21#include "link/TableMerger.h" 22#include "util/Comparators.h" 23#include "util/Util.h" 24 25#include <cassert> 26 27namespace aapt { 28 29TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable) : 30 mContext(context), mMasterTable(outTable) { 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 37bool TableMerger::merge(const Source& src, ResourceTable* table) { 38 const uint8_t desiredPackageId = mContext->getPackageId(); 39 40 bool error = false; 41 for (auto& package : table->packages) { 42 // Warn of packages with an unrelated ID. 43 if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) { 44 mContext->getDiagnostics()->warn(DiagMessage(src) 45 << "ignoring package " << package->name); 46 continue; 47 } 48 49 bool manglePackage = false; 50 if (!package->name.empty() && mContext->getCompilationPackage() != package->name) { 51 manglePackage = true; 52 mMergedPackages.insert(package->name); 53 } 54 55 // Merge here. Once the entries are merged and mangled, any references to 56 // them are still valid. This is because un-mangled references are 57 // mangled, then looked up at resolution time. 58 // Also, when linking, we convert references with no package name to use 59 // the compilation package name. 60 if (!doMerge(src, table, package.get(), manglePackage)) { 61 error = true; 62 } 63 } 64 return !error; 65} 66 67bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable, 68 ResourceTablePackage* srcPackage, const bool manglePackage) { 69 bool error = false; 70 71 for (auto& srcType : srcPackage->types) { 72 ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type); 73 if (srcType->symbolStatus.state == SymbolState::kPublic) { 74 if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id 75 && dstType->id.value() == srcType->id.value()) { 76 // Both types are public and have different IDs. 77 mContext->getDiagnostics()->error(DiagMessage(src) 78 << "can not merge type '" 79 << srcType->type 80 << "': conflicting public IDs"); 81 error = true; 82 continue; 83 } 84 85 dstType->symbolStatus = std::move(srcType->symbolStatus); 86 dstType->id = srcType->id; 87 } 88 89 for (auto& srcEntry : srcType->entries) { 90 ResourceEntry* dstEntry; 91 if (manglePackage) { 92 dstEntry = dstType->findOrCreateEntry(NameMangler::mangleEntry( 93 srcPackage->name, srcEntry->name)); 94 } else { 95 dstEntry = dstType->findOrCreateEntry(srcEntry->name); 96 } 97 98 if (srcEntry->symbolStatus.state != SymbolState::kUndefined) { 99 if (srcEntry->symbolStatus.state == SymbolState::kPublic) { 100 if (dstEntry->symbolStatus.state == SymbolState::kPublic && 101 dstEntry->id && srcEntry->id && 102 dstEntry->id.value() != srcEntry->id.value()) { 103 // Both entries are public and have different IDs. 104 mContext->getDiagnostics()->error(DiagMessage(src) 105 << "can not merge entry '" 106 << srcEntry->name 107 << "': conflicting public IDs"); 108 error = true; 109 continue; 110 } 111 112 if (srcEntry->id) { 113 dstEntry->id = srcEntry->id; 114 } 115 } 116 117 if (dstEntry->symbolStatus.state != SymbolState::kPublic && 118 dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) { 119 dstEntry->symbolStatus = std::move(srcEntry->symbolStatus); 120 } 121 } 122 123 for (ResourceConfigValue& srcValue : srcEntry->values) { 124 auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(), 125 srcValue.config, cmp::lessThan); 126 127 if (iter != dstEntry->values.end() && iter->config == srcValue.config) { 128 const int collisionResult = ResourceTable::resolveValueCollision( 129 iter->value.get(), srcValue.value.get()); 130 if (collisionResult == 0) { 131 // Error! 132 ResourceNameRef resourceName(srcPackage->name, 133 srcType->type, 134 srcEntry->name); 135 136 mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource()) 137 << "resource '" << resourceName 138 << "' has a conflicting value for " 139 << "configuration (" 140 << srcValue.config << ")"); 141 mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource()) 142 << "originally defined here"); 143 error = true; 144 continue; 145 } else if (collisionResult < 0) { 146 // Keep our existing value. 147 continue; 148 } 149 150 } else { 151 // Insert a place holder value. We will fill it in below. 152 iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config }); 153 } 154 155 if (manglePackage) { 156 iter->value = cloneAndMangle(srcTable, srcPackage->name, srcValue.value.get()); 157 } else { 158 iter->value = clone(srcValue.value.get()); 159 } 160 } 161 } 162 } 163 return !error; 164} 165 166std::unique_ptr<Value> TableMerger::cloneAndMangle(ResourceTable* table, 167 const std::u16string& package, 168 Value* value) { 169 if (FileReference* f = valueCast<FileReference>(value)) { 170 // Mangle the path. 171 StringPiece16 prefix, entry, suffix; 172 if (util::extractResFilePathParts(*f->path, &prefix, &entry, &suffix)) { 173 std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString()); 174 std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString(); 175 mFilesToMerge.push(FileToMerge{ table, *f->path, newPath }); 176 std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>( 177 mMasterTable->stringPool.makeRef(newPath)); 178 fileRef->setComment(f->getComment()); 179 fileRef->setSource(f->getSource()); 180 return std::move(fileRef); 181 } 182 } 183 return clone(value); 184} 185 186std::unique_ptr<Value> TableMerger::clone(Value* value) { 187 return std::unique_ptr<Value>(value->clone(&mMasterTable->stringPool)); 188} 189 190} // namespace aapt 191