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