blob: e01a00401133571f3b3fb82cfcdb6ef64470fd32 [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"
21
22#include "link/TableMerger.h"
Adam Lesinskie78fd612015-10-22 12:48:43 -070023#include "util/Comparators.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070024#include "util/Util.h"
25
26#include <cassert>
27
28namespace aapt {
29
Adam Lesinskia6fe3452015-12-09 15:20:52 -080030TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable,
31 const TableMergerOptions& options) :
32 mContext(context), mMasterTable(outTable), mOptions(options) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070033 // Create the desired package that all tables will be merged into.
34 mMasterPackage = mMasterTable->createPackage(
35 mContext->getCompilationPackage(), mContext->getPackageId());
36 assert(mMasterPackage && "package name or ID already taken");
37}
38
Adam Lesinski83f22552015-11-07 11:51:23 -080039/**
40 * This will merge packages with the same package name (or no package name).
41 */
Adam Lesinskia6fe3452015-12-09 15:20:52 -080042bool TableMerger::mergeImpl(const Source& src, ResourceTable* table,
43 bool overlay, bool allowNew) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070044 const uint8_t desiredPackageId = mContext->getPackageId();
45
46 bool error = false;
47 for (auto& package : table->packages) {
48 // Warn of packages with an unrelated ID.
Adam Lesinski9ba47d82015-10-13 11:37:10 -070049 if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070050 mContext->getDiagnostics()->warn(DiagMessage(src)
51 << "ignoring package " << package->name);
52 continue;
53 }
54
Adam Lesinski83f22552015-11-07 11:51:23 -080055 if (package->name.empty() || mContext->getCompilationPackage() == package->name) {
56 // Merge here. Once the entries are merged and mangled, any references to
57 // them are still valid. This is because un-mangled references are
58 // mangled, then looked up at resolution time.
59 // Also, when linking, we convert references with no package name to use
60 // the compilation package name.
Adam Lesinskia6fe3452015-12-09 15:20:52 -080061 error |= !doMerge(src, table, package.get(),
62 false /* mangle */, overlay, allowNew, {});
Adam Lesinski83f22552015-11-07 11:51:23 -080063 }
64 }
65 return !error;
66}
67
Adam Lesinskia6fe3452015-12-09 15:20:52 -080068bool TableMerger::merge(const Source& src, ResourceTable* table) {
69 return mergeImpl(src, table, false /* overlay */, true /* allow new */);
70}
71
72bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table) {
73 return mergeImpl(src, table, true /* overlay */, mOptions.autoAddOverlay);
74}
75
Adam Lesinski83f22552015-11-07 11:51:23 -080076/**
77 * This will merge and mangle resources from a static library.
78 */
79bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& packageName,
Adam Lesinskia6fe3452015-12-09 15:20:52 -080080 ResourceTable* table, io::IFileCollection* collection) {
Adam Lesinski83f22552015-11-07 11:51:23 -080081 bool error = false;
82 for (auto& package : table->packages) {
83 // Warn of packages with an unrelated ID.
84 if (packageName != package->name) {
85 mContext->getDiagnostics()->warn(DiagMessage(src)
86 << "ignoring package " << package->name);
87 continue;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070088 }
89
Adam Lesinski83f22552015-11-07 11:51:23 -080090 bool mangle = packageName != mContext->getCompilationPackage();
91 mMergedPackages.insert(package->name);
Adam Lesinskia6fe3452015-12-09 15:20:52 -080092
93 auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
94 FileReference* newFile, FileReference* oldFile) -> bool {
95 // The old file's path points inside the APK, so we can use it as is.
96 io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path));
97 if (!f) {
98 mContext->getDiagnostics()->error(DiagMessage(src) << "file '" << *oldFile->path
99 << "' not found");
100 return false;
101 }
102
Adam Lesinski6a008172016-02-02 17:02:58 -0800103 mFilesToMerge[ResourceKeyRef(name, config)] = FileToMerge{
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800104 f, oldFile->getSource(), util::utf16ToUtf8(*newFile->path) };
105 return true;
106 };
107
108 error |= !doMerge(src, table, package.get(),
109 mangle, false /* overlay */, true /* allow new */, callback);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700110 }
111 return !error;
112}
113
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800114bool TableMerger::doMerge(const Source& src,
115 ResourceTable* srcTable,
116 ResourceTablePackage* srcPackage,
117 const bool manglePackage,
118 const bool overlay,
119 const bool allowNewResources,
120 FileMergeCallback callback) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700121 bool error = false;
122
123 for (auto& srcType : srcPackage->types) {
124 ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700125 if (srcType->symbolStatus.state == SymbolState::kPublic) {
126 if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700127 && dstType->id.value() == srcType->id.value()) {
128 // Both types are public and have different IDs.
129 mContext->getDiagnostics()->error(DiagMessage(src)
130 << "can not merge type '"
131 << srcType->type
132 << "': conflicting public IDs");
133 error = true;
134 continue;
135 }
136
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700137 dstType->symbolStatus = std::move(srcType->symbolStatus);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700138 dstType->id = srcType->id;
139 }
140
141 for (auto& srcEntry : srcType->entries) {
142 ResourceEntry* dstEntry;
143 if (manglePackage) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800144 std::u16string mangledName = NameMangler::mangleEntry(srcPackage->name,
145 srcEntry->name);
146 if (allowNewResources) {
147 dstEntry = dstType->findOrCreateEntry(mangledName);
148 } else {
149 dstEntry = dstType->findEntry(mangledName);
150 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700151 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800152 if (allowNewResources) {
153 dstEntry = dstType->findOrCreateEntry(srcEntry->name);
154 } else {
155 dstEntry = dstType->findEntry(srcEntry->name);
156 }
157 }
158
159 if (!dstEntry) {
160 mContext->getDiagnostics()->error(DiagMessage(src)
161 << "resource "
162 << ResourceNameRef(srcPackage->name,
163 srcType->type,
164 srcEntry->name)
165 << " does not override an existing resource");
166 mContext->getDiagnostics()->note(DiagMessage(src)
167 << "define an <add-resource> tag or use "
168 "--auto-add-overlay");
169 error = true;
170 continue;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700171 }
172
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700173 if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
174 if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
175 if (dstEntry->symbolStatus.state == SymbolState::kPublic &&
176 dstEntry->id && srcEntry->id &&
177 dstEntry->id.value() != srcEntry->id.value()) {
178 // Both entries are public and have different IDs.
179 mContext->getDiagnostics()->error(DiagMessage(src)
180 << "can not merge entry '"
181 << srcEntry->name
182 << "': conflicting public IDs");
183 error = true;
184 continue;
185 }
186
187 if (srcEntry->id) {
188 dstEntry->id = srcEntry->id;
189 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700190 }
191
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700192 if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
193 dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
194 dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
195 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700196 }
197
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800198 ResourceNameRef resName(mMasterPackage->name, dstType->type, dstEntry->name);
199
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700200 for (ResourceConfigValue& srcValue : srcEntry->values) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700201 auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
Adam Lesinskib274e352015-11-06 15:14:35 -0800202 srcValue.config, cmp::lessThanConfig);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700203
Adam Lesinski6a008172016-02-02 17:02:58 -0800204 const bool stripConfig = mOptions.filter ?
205 !mOptions.filter->match(srcValue.config) : false;
206
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700207 if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
208 const int collisionResult = ResourceTable::resolveValueCollision(
209 iter->value.get(), srcValue.value.get());
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800210 if (collisionResult == 0 && !overlay) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700211 // Error!
Adam Lesinskie78fd612015-10-22 12:48:43 -0700212 ResourceNameRef resourceName(srcPackage->name,
213 srcType->type,
214 srcEntry->name);
215
216 mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700217 << "resource '" << resourceName
218 << "' has a conflicting value for "
219 << "configuration ("
220 << srcValue.config << ")");
Adam Lesinskie78fd612015-10-22 12:48:43 -0700221 mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700222 << "originally defined here");
223 error = true;
224 continue;
225 } else if (collisionResult < 0) {
226 // Keep our existing value.
227 continue;
228 }
229
Adam Lesinski6a008172016-02-02 17:02:58 -0800230 } else if (!stripConfig){
Adam Lesinskie78fd612015-10-22 12:48:43 -0700231 // Insert a place holder value. We will fill it in below.
232 iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700233 }
234
Adam Lesinski6a008172016-02-02 17:02:58 -0800235 if (stripConfig) {
236 continue;
237 }
238
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800239 if (FileReference* f = valueCast<FileReference>(srcValue.value.get())) {
240 std::unique_ptr<FileReference> newFileRef;
241 if (manglePackage) {
242 newFileRef = cloneAndMangleFile(srcPackage->name, *f);
243 } else {
244 newFileRef = std::unique_ptr<FileReference>(f->clone(
245 &mMasterTable->stringPool));
246 }
247
248 if (callback) {
249 if (!callback(resName, iter->config, newFileRef.get(), f)) {
250 error = true;
251 continue;
252 }
253 }
254 iter->value = std::move(newFileRef);
255
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700256 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800257 iter->value = std::unique_ptr<Value>(srcValue.value->clone(
258 &mMasterTable->stringPool));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700259 }
260 }
261 }
262 }
263 return !error;
264}
265
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800266std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(const std::u16string& package,
267 const FileReference& fileRef) {
268
269 StringPiece16 prefix, entry, suffix;
270 if (util::extractResFilePathParts(*fileRef.path, &prefix, &entry, &suffix)) {
271 std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
272 std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
273 std::unique_ptr<FileReference> newFileRef = util::make_unique<FileReference>(
274 mMasterTable->stringPool.makeRef(newPath));
275 newFileRef->setComment(fileRef.getComment());
276 newFileRef->setSource(fileRef.getSource());
277 return newFileRef;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700278 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800279 return std::unique_ptr<FileReference>(fileRef.clone(&mMasterTable->stringPool));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700280}
281
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800282bool TableMerger::mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay) {
283 ResourceTable table;
284 std::u16string path = util::utf8ToUtf16(ResourceUtils::buildResourceFileName(fileDesc,
285 nullptr));
286 std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
287 table.stringPool.makeRef(path));
288 fileRef->setSource(fileDesc.source);
289
290 ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
291 pkg->findOrCreateType(fileDesc.name.type)
292 ->findOrCreateEntry(fileDesc.name.entry)
293 ->values.push_back(ResourceConfigValue{ fileDesc.config, std::move(fileRef) });
294
295 auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
296 FileReference* newFile, FileReference* oldFile) -> bool {
Adam Lesinski6a008172016-02-02 17:02:58 -0800297 mFilesToMerge[ResourceKeyRef(name, config)] = FileToMerge{
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800298 file, oldFile->getSource(), util::utf16ToUtf8(*newFile->path) };
299 return true;
300 };
301
302 return doMerge(file->getSource(), &table, pkg,
303 false /* mangle */, overlay /* overlay */, true /* allow new */, callback);
304}
305
306bool TableMerger::mergeFile(const ResourceFile& fileDesc, io::IFile* file) {
307 return mergeFileImpl(fileDesc, file, false /* overlay */);
308}
309
310bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file) {
311 return mergeFileImpl(fileDesc, file, true /* overlay */);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700312}
313
314} // namespace aapt