blob: 7471e15db41a76ba616e5ec1c16425d7353bc835 [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 Lesinski64587af2016-02-18 18:33:06 -080037bool 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
Adam Lesinski83f22552015-11-07 11:51:23 -080047/**
48 * This will merge packages with the same package name (or no package name).
49 */
Adam Lesinskia6fe3452015-12-09 15:20:52 -080050bool TableMerger::mergeImpl(const Source& src, ResourceTable* table,
Adam Lesinski64587af2016-02-18 18:33:06 -080051 io::IFileCollection* collection,
Adam Lesinskia6fe3452015-12-09 15:20:52 -080052 bool overlay, bool allowNew) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070053 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.
Adam Lesinski9ba47d82015-10-13 11:37:10 -070058 if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070059 mContext->getDiagnostics()->warn(DiagMessage(src)
60 << "ignoring package " << package->name);
61 continue;
62 }
63
Adam Lesinski83f22552015-11-07 11:51:23 -080064 if (package->name.empty() || mContext->getCompilationPackage() == package->name) {
Adam Lesinski64587af2016-02-18 18:33:06 -080065 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
Adam Lesinski83f22552015-11-07 11:51:23 -080083 // 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.
Adam Lesinskia6fe3452015-12-09 15:20:52 -080088 error |= !doMerge(src, table, package.get(),
Adam Lesinski64587af2016-02-18 18:33:06 -080089 false /* mangle */, overlay, allowNew, callback);
Adam Lesinski83f22552015-11-07 11:51:23 -080090 }
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,
Adam Lesinskia6fe3452015-12-09 15:20:52 -080099 ResourceTable* table, io::IFileCollection* collection) {
Adam Lesinski83f22552015-11-07 11:51:23 -0800100 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;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700107 }
108
Adam Lesinski83f22552015-11-07 11:51:23 -0800109 bool mangle = packageName != mContext->getCompilationPackage();
110 mMergedPackages.insert(package->name);
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800111
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
Adam Lesinski355f2852016-02-13 20:26:45 -0800122 newFile->file = f;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800123 return true;
124 };
125
126 error |= !doMerge(src, table, package.get(),
127 mangle, false /* overlay */, true /* allow new */, callback);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700128 }
129 return !error;
130}
131
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800132bool 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) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700139 bool error = false;
140
141 for (auto& srcType : srcPackage->types) {
142 ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700143 if (srcType->symbolStatus.state == SymbolState::kPublic) {
144 if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700145 && 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
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700155 dstType->symbolStatus = std::move(srcType->symbolStatus);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700156 dstType->id = srcType->id;
157 }
158
159 for (auto& srcEntry : srcType->entries) {
160 ResourceEntry* dstEntry;
161 if (manglePackage) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800162 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 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700169 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800170 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;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700189 }
190
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700191 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 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700208 }
209
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700210 if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
211 dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
212 dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
213 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700214 }
215
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800216 ResourceNameRef resName(mMasterPackage->name, dstType->type, dstEntry->name);
217
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800218 for (auto& srcValue : srcEntry->values) {
219 ResourceConfigValue* dstValue = dstEntry->findValue(srcValue->config,
220 srcValue->product);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800221 if (dstValue) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700222 const int collisionResult = ResourceTable::resolveValueCollision(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800223 dstValue->value.get(), srcValue->value.get());
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800224 if (collisionResult == 0 && !overlay) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700225 // Error!
Adam Lesinskie78fd612015-10-22 12:48:43 -0700226 ResourceNameRef resourceName(srcPackage->name,
227 srcType->type,
228 srcEntry->name);
229
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800230 mContext->getDiagnostics()->error(DiagMessage(srcValue->value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700231 << "resource '" << resourceName
232 << "' has a conflicting value for "
233 << "configuration ("
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800234 << srcValue->config << ")");
235 mContext->getDiagnostics()->note(DiagMessage(dstValue->value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700236 << "originally defined here");
237 error = true;
238 continue;
239 } else if (collisionResult < 0) {
240 // Keep our existing value.
241 continue;
242 }
243
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700244 }
245
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800246 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())) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800252 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) {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800261 if (!callback(resName, srcValue->config, newFileRef.get(), f)) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800262 error = true;
263 continue;
264 }
265 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800266 dstValue->value = std::move(newFileRef);
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800267
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700268 } else {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800269 dstValue->value = std::unique_ptr<Value>(srcValue->value->clone(
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800270 &mMasterTable->stringPool));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700271 }
272 }
273 }
274 }
275 return !error;
276}
277
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800278std::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;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700290 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800291 return std::unique_ptr<FileReference>(fileRef.clone(&mMasterTable->stringPool));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700292}
293
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800294bool 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);
Adam Lesinski355f2852016-02-13 20:26:45 -0800301 fileRef->file = file;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800302
303 ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
304 pkg->findOrCreateType(fileDesc.name.type)
305 ->findOrCreateEntry(fileDesc.name.entry)
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800306 ->findOrCreateValue(fileDesc.config, {})
307 ->value = std::move(fileRef);
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800308
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800309 return doMerge(file->getSource(), &table, pkg,
Adam Lesinski355f2852016-02-13 20:26:45 -0800310 false /* mangle */, overlay /* overlay */, true /* allow new */, {});
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800311}
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 */);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700319}
320
321} // namespace aapt