blob: 5f1174588438bd5c379b51a5d4961f06d41ce5b8 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
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"
Adam Lesinskia6fe3452015-12-09 15:20:52 -080018#include "ResourceUtils.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070019#include "ResourceValues.h"
20#include "ValueVisitor.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070021#include "link/TableMerger.h"
22#include "util/Util.h"
23
24#include <cassert>
25
26namespace aapt {
27
Adam Lesinskia6fe3452015-12-09 15:20:52 -080028TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable,
29 const TableMergerOptions& options) :
30 mContext(context), mMasterTable(outTable), mOptions(options) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070031 // 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
Adam Lesinski83f22552015-11-07 11:51:23 -080037/**
38 * This will merge packages with the same package name (or no package name).
39 */
Adam Lesinskia6fe3452015-12-09 15:20:52 -080040bool TableMerger::mergeImpl(const Source& src, ResourceTable* table,
41 bool overlay, bool allowNew) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070042 const uint8_t desiredPackageId = mContext->getPackageId();
43
44 bool error = false;
45 for (auto& package : table->packages) {
46 // Warn of packages with an unrelated ID.
Adam Lesinski9ba47d82015-10-13 11:37:10 -070047 if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070048 mContext->getDiagnostics()->warn(DiagMessage(src)
49 << "ignoring package " << package->name);
50 continue;
51 }
52
Adam Lesinski83f22552015-11-07 11:51:23 -080053 if (package->name.empty() || mContext->getCompilationPackage() == package->name) {
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.
Adam Lesinskia6fe3452015-12-09 15:20:52 -080059 error |= !doMerge(src, table, package.get(),
60 false /* mangle */, overlay, allowNew, {});
Adam Lesinski83f22552015-11-07 11:51:23 -080061 }
62 }
63 return !error;
64}
65
Adam Lesinskia6fe3452015-12-09 15:20:52 -080066bool TableMerger::merge(const Source& src, ResourceTable* table) {
67 return mergeImpl(src, table, false /* overlay */, true /* allow new */);
68}
69
70bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table) {
71 return mergeImpl(src, table, true /* overlay */, mOptions.autoAddOverlay);
72}
73
Adam Lesinski83f22552015-11-07 11:51:23 -080074/**
75 * This will merge and mangle resources from a static library.
76 */
77bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& packageName,
Adam Lesinskia6fe3452015-12-09 15:20:52 -080078 ResourceTable* table, io::IFileCollection* collection) {
Adam Lesinski83f22552015-11-07 11:51:23 -080079 bool error = false;
80 for (auto& package : table->packages) {
81 // Warn of packages with an unrelated ID.
82 if (packageName != package->name) {
83 mContext->getDiagnostics()->warn(DiagMessage(src)
84 << "ignoring package " << package->name);
85 continue;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070086 }
87
Adam Lesinski83f22552015-11-07 11:51:23 -080088 bool mangle = packageName != mContext->getCompilationPackage();
89 mMergedPackages.insert(package->name);
Adam Lesinskia6fe3452015-12-09 15:20:52 -080090
91 auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
92 FileReference* newFile, FileReference* oldFile) -> bool {
93 // The old file's path points inside the APK, so we can use it as is.
94 io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path));
95 if (!f) {
96 mContext->getDiagnostics()->error(DiagMessage(src) << "file '" << *oldFile->path
97 << "' not found");
98 return false;
99 }
100
Adam Lesinski355f2852016-02-13 20:26:45 -0800101 newFile->file = f;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800102 return true;
103 };
104
105 error |= !doMerge(src, table, package.get(),
106 mangle, false /* overlay */, true /* allow new */, callback);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700107 }
108 return !error;
109}
110
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800111bool TableMerger::doMerge(const Source& src,
112 ResourceTable* srcTable,
113 ResourceTablePackage* srcPackage,
114 const bool manglePackage,
115 const bool overlay,
116 const bool allowNewResources,
117 FileMergeCallback callback) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700118 bool error = false;
119
120 for (auto& srcType : srcPackage->types) {
121 ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700122 if (srcType->symbolStatus.state == SymbolState::kPublic) {
123 if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700124 && dstType->id.value() == srcType->id.value()) {
125 // Both types are public and have different IDs.
126 mContext->getDiagnostics()->error(DiagMessage(src)
127 << "can not merge type '"
128 << srcType->type
129 << "': conflicting public IDs");
130 error = true;
131 continue;
132 }
133
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700134 dstType->symbolStatus = std::move(srcType->symbolStatus);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700135 dstType->id = srcType->id;
136 }
137
138 for (auto& srcEntry : srcType->entries) {
139 ResourceEntry* dstEntry;
140 if (manglePackage) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800141 std::u16string mangledName = NameMangler::mangleEntry(srcPackage->name,
142 srcEntry->name);
143 if (allowNewResources) {
144 dstEntry = dstType->findOrCreateEntry(mangledName);
145 } else {
146 dstEntry = dstType->findEntry(mangledName);
147 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700148 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800149 if (allowNewResources) {
150 dstEntry = dstType->findOrCreateEntry(srcEntry->name);
151 } else {
152 dstEntry = dstType->findEntry(srcEntry->name);
153 }
154 }
155
156 if (!dstEntry) {
157 mContext->getDiagnostics()->error(DiagMessage(src)
158 << "resource "
159 << ResourceNameRef(srcPackage->name,
160 srcType->type,
161 srcEntry->name)
162 << " does not override an existing resource");
163 mContext->getDiagnostics()->note(DiagMessage(src)
164 << "define an <add-resource> tag or use "
165 "--auto-add-overlay");
166 error = true;
167 continue;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700168 }
169
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700170 if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
171 if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
172 if (dstEntry->symbolStatus.state == SymbolState::kPublic &&
173 dstEntry->id && srcEntry->id &&
174 dstEntry->id.value() != srcEntry->id.value()) {
175 // Both entries are public and have different IDs.
176 mContext->getDiagnostics()->error(DiagMessage(src)
177 << "can not merge entry '"
178 << srcEntry->name
179 << "': conflicting public IDs");
180 error = true;
181 continue;
182 }
183
184 if (srcEntry->id) {
185 dstEntry->id = srcEntry->id;
186 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700187 }
188
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700189 if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
190 dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
191 dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
192 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700193 }
194
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800195 ResourceNameRef resName(mMasterPackage->name, dstType->type, dstEntry->name);
196
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800197 for (auto& srcValue : srcEntry->values) {
198 ResourceConfigValue* dstValue = dstEntry->findValue(srcValue->config,
199 srcValue->product);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800200 if (dstValue) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700201 const int collisionResult = ResourceTable::resolveValueCollision(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800202 dstValue->value.get(), srcValue->value.get());
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800203 if (collisionResult == 0 && !overlay) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700204 // Error!
Adam Lesinskie78fd612015-10-22 12:48:43 -0700205 ResourceNameRef resourceName(srcPackage->name,
206 srcType->type,
207 srcEntry->name);
208
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800209 mContext->getDiagnostics()->error(DiagMessage(srcValue->value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700210 << "resource '" << resourceName
211 << "' has a conflicting value for "
212 << "configuration ("
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800213 << srcValue->config << ")");
214 mContext->getDiagnostics()->note(DiagMessage(dstValue->value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700215 << "originally defined here");
216 error = true;
217 continue;
218 } else if (collisionResult < 0) {
219 // Keep our existing value.
220 continue;
221 }
222
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700223 }
224
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800225 if (!dstValue) {
226 // Force create the entry if we didn't have it.
227 dstValue = dstEntry->findOrCreateValue(srcValue->config, srcValue->product);
228 }
229
230 if (FileReference* f = valueCast<FileReference>(srcValue->value.get())) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800231 std::unique_ptr<FileReference> newFileRef;
232 if (manglePackage) {
233 newFileRef = cloneAndMangleFile(srcPackage->name, *f);
234 } else {
235 newFileRef = std::unique_ptr<FileReference>(f->clone(
236 &mMasterTable->stringPool));
237 }
238
239 if (callback) {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800240 if (!callback(resName, srcValue->config, newFileRef.get(), f)) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800241 error = true;
242 continue;
243 }
244 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800245 dstValue->value = std::move(newFileRef);
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800246
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700247 } else {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800248 dstValue->value = std::unique_ptr<Value>(srcValue->value->clone(
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800249 &mMasterTable->stringPool));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700250 }
251 }
252 }
253 }
254 return !error;
255}
256
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800257std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(const std::u16string& package,
258 const FileReference& fileRef) {
259
260 StringPiece16 prefix, entry, suffix;
261 if (util::extractResFilePathParts(*fileRef.path, &prefix, &entry, &suffix)) {
262 std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
263 std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
264 std::unique_ptr<FileReference> newFileRef = util::make_unique<FileReference>(
265 mMasterTable->stringPool.makeRef(newPath));
266 newFileRef->setComment(fileRef.getComment());
267 newFileRef->setSource(fileRef.getSource());
268 return newFileRef;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700269 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800270 return std::unique_ptr<FileReference>(fileRef.clone(&mMasterTable->stringPool));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700271}
272
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800273bool TableMerger::mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay) {
274 ResourceTable table;
275 std::u16string path = util::utf8ToUtf16(ResourceUtils::buildResourceFileName(fileDesc,
276 nullptr));
277 std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
278 table.stringPool.makeRef(path));
279 fileRef->setSource(fileDesc.source);
Adam Lesinski355f2852016-02-13 20:26:45 -0800280 fileRef->file = file;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800281
282 ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
283 pkg->findOrCreateType(fileDesc.name.type)
284 ->findOrCreateEntry(fileDesc.name.entry)
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800285 ->findOrCreateValue(fileDesc.config, {})
286 ->value = std::move(fileRef);
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800287
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800288 return doMerge(file->getSource(), &table, pkg,
Adam Lesinski355f2852016-02-13 20:26:45 -0800289 false /* mangle */, overlay /* overlay */, true /* allow new */, {});
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800290}
291
292bool TableMerger::mergeFile(const ResourceFile& fileDesc, io::IFile* file) {
293 return mergeFileImpl(fileDesc, file, false /* overlay */);
294}
295
296bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file) {
297 return mergeFileImpl(fileDesc, file, true /* overlay */);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700298}
299
300} // namespace aapt