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