TableMerger.cpp revision 64587af8179affd38ee26543b748f2d63b7f67bb
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 26namespace aapt { 27 28TableMerger::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 37bool TableMerger::merge(const Source& src, ResourceTable* table, 38 io::IFileCollection* collection) { 39 return mergeImpl(src, table, collection, false /* overlay */, true /* allow new */); 40} 41 42bool 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 */ 50bool 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 */ 98bool 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 132bool 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 278std::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 294bool 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 313bool TableMerger::mergeFile(const ResourceFile& fileDesc, io::IFile* file) { 314 return mergeFileImpl(fileDesc, file, false /* overlay */); 315} 316 317bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file) { 318 return mergeFileImpl(fileDesc, file, true /* overlay */); 319} 320 321} // namespace aapt 322